libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

decode_oneshot.cc (8483B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 // This C++ example decodes a JPEG XL image in one shot (all input bytes
      7 // available at once). The example outputs the pixels and color information to a
      8 // floating point image and an ICC profile on disk.
      9 
     10 #include <jxl/codestream_header.h>
     11 #include <jxl/decode.h>
     12 #include <jxl/decode_cxx.h>
     13 #include <jxl/resizable_parallel_runner.h>
     14 #include <jxl/resizable_parallel_runner_cxx.h>
     15 #include <jxl/types.h>
     16 #include <limits.h>
     17 #include <stdint.h>
     18 #include <stdio.h>
     19 #include <string.h>
     20 
     21 #include <vector>
     22 
     23 /** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
     24  * stored as floating point, as interleaved RGBA (4 floating point values per
     25  * pixel), line per line from top to bottom.  Pixel values have nominal range
     26  * 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile
     27  * describes the color format of the pixel data.
     28  */
     29 bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
     30                          std::vector<float>* pixels, size_t* xsize,
     31                          size_t* ysize, std::vector<uint8_t>* icc_profile) {
     32   // Multi-threaded parallel runner.
     33   auto runner = JxlResizableParallelRunnerMake(nullptr);
     34 
     35   auto dec = JxlDecoderMake(nullptr);
     36   if (JXL_DEC_SUCCESS !=
     37       JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
     38                                                JXL_DEC_COLOR_ENCODING |
     39                                                JXL_DEC_FULL_IMAGE)) {
     40     fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
     41     return false;
     42   }
     43 
     44   if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
     45                                                      JxlResizableParallelRunner,
     46                                                      runner.get())) {
     47     fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
     48     return false;
     49   }
     50 
     51   JxlBasicInfo info;
     52   JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
     53 
     54   JxlDecoderSetInput(dec.get(), jxl, size);
     55   JxlDecoderCloseInput(dec.get());
     56 
     57   for (;;) {
     58     JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
     59 
     60     if (status == JXL_DEC_ERROR) {
     61       fprintf(stderr, "Decoder error\n");
     62       return false;
     63     } else if (status == JXL_DEC_NEED_MORE_INPUT) {
     64       fprintf(stderr, "Error, already provided all input\n");
     65       return false;
     66     } else if (status == JXL_DEC_BASIC_INFO) {
     67       if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
     68         fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
     69         return false;
     70       }
     71       *xsize = info.xsize;
     72       *ysize = info.ysize;
     73       JxlResizableParallelRunnerSetThreads(
     74           runner.get(),
     75           JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
     76     } else if (status == JXL_DEC_COLOR_ENCODING) {
     77       // Get the ICC color profile of the pixel data
     78       size_t icc_size;
     79       if (JXL_DEC_SUCCESS !=
     80           JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
     81                                       &icc_size)) {
     82         fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
     83         return false;
     84       }
     85       icc_profile->resize(icc_size);
     86       if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
     87                                  dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
     88                                  icc_profile->data(), icc_profile->size())) {
     89         fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
     90         return false;
     91       }
     92     } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
     93       size_t buffer_size;
     94       if (JXL_DEC_SUCCESS !=
     95           JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
     96         fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
     97         return false;
     98       }
     99       if (buffer_size != *xsize * *ysize * 16) {
    100         fprintf(stderr, "Invalid out buffer size %d %d\n",
    101                 static_cast<int>(buffer_size),
    102                 static_cast<int>(*xsize * *ysize * 16));
    103         return false;
    104       }
    105       pixels->resize(*xsize * *ysize * 4);
    106       void* pixels_buffer = static_cast<void*>(pixels->data());
    107       size_t pixels_buffer_size = pixels->size() * sizeof(float);
    108       if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
    109                                                          pixels_buffer,
    110                                                          pixels_buffer_size)) {
    111         fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
    112         return false;
    113       }
    114     } else if (status == JXL_DEC_FULL_IMAGE) {
    115       // Nothing to do. Do not yet return. If the image is an animation, more
    116       // full frames may be decoded. This example only keeps the last one.
    117     } else if (status == JXL_DEC_SUCCESS) {
    118       // All decoding successfully finished.
    119       // It's not required to call JxlDecoderReleaseInput(dec.get()) here since
    120       // the decoder will be destroyed.
    121       return true;
    122     } else {
    123       fprintf(stderr, "Unknown decoder status\n");
    124       return false;
    125     }
    126   }
    127 }
    128 
    129 /** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick
    130  * support viewing this format.
    131  * The input pixels are given as 32-bit floating point with 4-channel RGBA.
    132  * The alpha channel will not be written since .pfm does not support it.
    133  */
    134 bool WritePFM(const char* filename, const float* pixels, size_t xsize,
    135               size_t ysize) {
    136   FILE* file = fopen(filename, "wb");
    137   if (!file) {
    138     fprintf(stderr, "Could not open %s for writing", filename);
    139     return false;
    140   }
    141   uint32_t endian_test = 1;
    142   uint8_t little_endian[4];
    143   memcpy(little_endian, &endian_test, 4);
    144 
    145   fprintf(file, "PF\n%d %d\n%s\n", static_cast<int>(xsize),
    146           static_cast<int>(ysize), little_endian[0] ? "-1.0" : "1.0");
    147   for (int y = ysize - 1; y >= 0; y--) {
    148     for (size_t x = 0; x < xsize; x++) {
    149       for (size_t c = 0; c < 3; c++) {
    150         const float* f = &pixels[(y * xsize + x) * 4 + c];
    151         fwrite(f, 4, 1, file);
    152       }
    153     }
    154   }
    155   if (fclose(file) != 0) {
    156     return false;
    157   }
    158   return true;
    159 }
    160 
    161 bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
    162   FILE* file = fopen(filename, "rb");
    163   if (!file) {
    164     return false;
    165   }
    166 
    167   if (fseek(file, 0, SEEK_END) != 0) {
    168     fclose(file);
    169     return false;
    170   }
    171 
    172   long size = ftell(file);
    173   // Avoid invalid file or directory.
    174   if (size >= LONG_MAX || size < 0) {
    175     fclose(file);
    176     return false;
    177   }
    178 
    179   if (fseek(file, 0, SEEK_SET) != 0) {
    180     fclose(file);
    181     return false;
    182   }
    183 
    184   out->resize(size);
    185   size_t readsize = fread(out->data(), 1, size, file);
    186   if (fclose(file) != 0) {
    187     return false;
    188   }
    189 
    190   return readsize == static_cast<size_t>(size);
    191 }
    192 
    193 bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
    194   FILE* file = fopen(filename, "wb");
    195   if (!file) {
    196     fprintf(stderr, "Could not open %s for writing", filename);
    197     return false;
    198   }
    199   fwrite(data, 1, size, file);
    200   if (fclose(file) != 0) {
    201     return false;
    202   }
    203   return true;
    204 }
    205 
    206 int main(int argc, char* argv[]) {
    207   if (argc != 4) {
    208     fprintf(stderr,
    209             "Usage: %s <jxl> <pfm> <icc>\n"
    210             "Where:\n"
    211             "  jxl = input JPEG XL image filename\n"
    212             "  pfm = output Portable FloatMap image filename\n"
    213             "  icc = output ICC color profile filename\n"
    214             "Output files will be overwritten.\n",
    215             argv[0]);
    216     return 1;
    217   }
    218 
    219   const char* jxl_filename = argv[1];
    220   const char* pfm_filename = argv[2];
    221   const char* icc_filename = argv[3];
    222 
    223   std::vector<uint8_t> jxl;
    224   if (!LoadFile(jxl_filename, &jxl)) {
    225     fprintf(stderr, "couldn't load %s\n", jxl_filename);
    226     return 1;
    227   }
    228 
    229   std::vector<float> pixels;
    230   std::vector<uint8_t> icc_profile;
    231   size_t xsize = 0;
    232   size_t ysize = 0;
    233   if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize,
    234                            &icc_profile)) {
    235     fprintf(stderr, "Error while decoding the jxl file\n");
    236     return 1;
    237   }
    238   if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) {
    239     fprintf(stderr, "Error while writing the PFM image file\n");
    240     return 1;
    241   }
    242   if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) {
    243     fprintf(stderr, "Error while writing the ICC profile file\n");
    244     return 1;
    245   }
    246   printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename);
    247   return 0;
    248 }