tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

decode_progressive.cc (7833B)


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