libjxl

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

decode_progressive.cc (7738B)


      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 progressively (input bytes are
      7 // passed in chunks). The example outputs the intermediate steps to PAM files.
      8 
      9 #include <inttypes.h>
     10 #include <jxl/decode.h>
     11 #include <jxl/decode_cxx.h>
     12 #include <jxl/resizable_parallel_runner.h>
     13 #include <jxl/resizable_parallel_runner_cxx.h>
     14 #include <limits.h>
     15 #include <stdint.h>
     16 #include <stdio.h>
     17 #include <string.h>
     18 
     19 #include <vector>
     20 
     21 bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
     22   FILE* fp = fopen(filename, "wb");
     23   if (!fp) {
     24     fprintf(stderr, "Could not open %s for writing", filename);
     25     return false;
     26   }
     27   fprintf(fp,
     28           "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
     29           "RGB_ALPHA\nENDHDR\n",
     30           static_cast<int>(w), static_cast<int>(h));
     31   size_t num_bytes = w * h * 4;
     32   if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) {
     33     fclose(fp);
     34     return false;
     35   };
     36   if (fclose(fp) != 0) {
     37     return false;
     38   }
     39   return true;
     40 }
     41 
     42 /** Decodes JPEG XL image to 8-bit integer RGBA pixels and an ICC Profile, in a
     43  * progressive way, saving the intermediate steps.
     44  */
     45 bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size,
     46                              const char* filename, size_t chunksize) {
     47   std::vector<uint8_t> pixels;
     48   std::vector<uint8_t> icc_profile;
     49   size_t xsize = 0;
     50   size_t ysize = 0;
     51 
     52   // Multi-threaded parallel runner.
     53   auto runner = JxlResizableParallelRunnerMake(nullptr);
     54 
     55   auto dec = JxlDecoderMake(nullptr);
     56   if (JXL_DEC_SUCCESS !=
     57       JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
     58                                                JXL_DEC_COLOR_ENCODING |
     59                                                JXL_DEC_FULL_IMAGE)) {
     60     fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
     61     return false;
     62   }
     63 
     64   if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
     65                                                      JxlResizableParallelRunner,
     66                                                      runner.get())) {
     67     fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
     68     return false;
     69   }
     70 
     71   JxlBasicInfo info;
     72   JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
     73 
     74   size_t seen = 0;
     75   JxlDecoderSetInput(dec.get(), jxl, chunksize);
     76   size_t remaining = chunksize;
     77 
     78   for (;;) {
     79     JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
     80 
     81     if (status == JXL_DEC_ERROR) {
     82       fprintf(stderr, "Decoder error\n");
     83       return false;
     84     } else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS ||
     85                status == JXL_DEC_FULL_IMAGE) {
     86       seen += remaining - JxlDecoderReleaseInput(dec.get());
     87       printf("Flushing after %" PRIu64 " bytes\n", static_cast<uint64_t>(seen));
     88       if (status == JXL_DEC_NEED_MORE_INPUT &&
     89           JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec.get())) {
     90         printf("flush error (no preview yet)\n");
     91       } else {
     92         char fname[1024];
     93         if (snprintf(fname, 1024, "%s-%" PRIu64 ".pam", filename,
     94                      static_cast<uint64_t>(seen)) >= 1024) {
     95           fprintf(stderr, "Filename too long\n");
     96           return false;
     97         };
     98         if (!WritePAM(fname, pixels.data(), xsize, ysize)) {
     99           fprintf(stderr, "Error writing progressive output\n");
    100         }
    101       }
    102       remaining = size - seen;
    103       if (remaining > chunksize) remaining = chunksize;
    104       if (remaining == 0) {
    105         if (status == JXL_DEC_NEED_MORE_INPUT) {
    106           fprintf(stderr, "Error, already provided all input\n");
    107           return false;
    108         } else {
    109           return true;
    110         }
    111       }
    112       JxlDecoderSetInput(dec.get(), jxl + seen, remaining);
    113     } else if (status == JXL_DEC_BASIC_INFO) {
    114       if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
    115         fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
    116         return false;
    117       }
    118       xsize = info.xsize;
    119       ysize = info.ysize;
    120       JxlResizableParallelRunnerSetThreads(
    121           runner.get(),
    122           JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
    123     } else if (status == JXL_DEC_COLOR_ENCODING) {
    124       // Get the ICC color profile of the pixel data
    125       size_t icc_size;
    126       if (JXL_DEC_SUCCESS !=
    127           JxlDecoderGetICCProfileSize(
    128               dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL, &icc_size)) {
    129         fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
    130         return false;
    131       }
    132       icc_profile.resize(icc_size);
    133       if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
    134                                  dec.get(), JXL_COLOR_PROFILE_TARGET_ORIGINAL,
    135                                  icc_profile.data(), icc_profile.size())) {
    136         fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
    137         return false;
    138       }
    139     } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
    140       size_t buffer_size;
    141       if (JXL_DEC_SUCCESS !=
    142           JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
    143         fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
    144         return false;
    145       }
    146       if (buffer_size != xsize * ysize * 4) {
    147         fprintf(stderr, "Invalid out buffer size %" PRIu64 " != %" PRIu64 "\n",
    148                 static_cast<uint64_t>(buffer_size),
    149                 static_cast<uint64_t>(xsize * ysize * 4));
    150         return false;
    151       }
    152       pixels.resize(xsize * ysize * 4);
    153       if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
    154                                                          pixels.data(),
    155                                                          pixels.size())) {
    156         fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
    157         return false;
    158       }
    159     } else {
    160       fprintf(stderr, "Unknown decoder status\n");
    161       return false;
    162     }
    163   }
    164 }
    165 
    166 bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
    167   FILE* file = fopen(filename, "rb");
    168   if (!file) {
    169     return false;
    170   }
    171 
    172   if (fseek(file, 0, SEEK_END) != 0) {
    173     fclose(file);
    174     return false;
    175   }
    176 
    177   long size = ftell(file);
    178   // Avoid invalid file or directory.
    179   if (size >= LONG_MAX || size < 0) {
    180     fclose(file);
    181     return false;
    182   }
    183 
    184   if (fseek(file, 0, SEEK_SET) != 0) {
    185     fclose(file);
    186     return false;
    187   }
    188 
    189   out->resize(size);
    190   size_t readsize = fread(out->data(), 1, size, file);
    191   if (fclose(file) != 0) {
    192     return false;
    193   }
    194 
    195   return readsize == static_cast<size_t>(size);
    196 }
    197 
    198 int main(int argc, char* argv[]) {
    199   if (argc < 3) {
    200     fprintf(
    201         stderr,
    202         "Usage: %s <jxl> <basename> [chunksize]\n"
    203         "Where:\n"
    204         "  jxl = input JPEG XL image filename\n"
    205         "  basename = prefix of output filenames\n"
    206         "  chunksize = loads chunksize bytes at a time and writes\n"
    207         "              intermediate results to basename-[bytes loaded].pam\n"
    208         "Output files will be overwritten.\n",
    209         argv[0]);
    210     return 1;
    211   }
    212 
    213   const char* jxl_filename = argv[1];
    214   const char* png_filename = argv[2];
    215 
    216   std::vector<uint8_t> jxl;
    217   if (!LoadFile(jxl_filename, &jxl)) {
    218     fprintf(stderr, "couldn't load %s\n", jxl_filename);
    219     return 1;
    220   }
    221   size_t chunksize = jxl.size();
    222   if (argc > 3) {
    223     long cs = atol(argv[3]);
    224     if (cs < 100) {
    225       fprintf(stderr, "Chunk size is too low, try at least 100 bytes\n");
    226       return 1;
    227     }
    228     chunksize = cs;
    229   }
    230 
    231   if (!DecodeJpegXlProgressive(jxl.data(), jxl.size(), png_filename,
    232                                chunksize)) {
    233     fprintf(stderr, "Error while decoding the jxl file\n");
    234     return 1;
    235   }
    236   return 0;
    237 }