tor-browser

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

jxl.cc (23600B)


      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 #include "lib/extras/dec/jxl.h"
      7 
      8 #include <jxl/cms.h>
      9 #include <jxl/codestream_header.h>
     10 #include <jxl/decode.h>
     11 #include <jxl/decode_cxx.h>
     12 #include <jxl/types.h>
     13 
     14 #include <cinttypes>  // PRIu32
     15 #include <cstddef>
     16 #include <cstdint>
     17 #include <cstdio>
     18 #include <limits>
     19 #include <vector>
     20 
     21 #include "lib/extras/common.h"
     22 #include "lib/extras/dec/color_description.h"
     23 #include "lib/jxl/base/common.h"
     24 #include "lib/jxl/base/exif.h"
     25 #include "lib/jxl/base/printf_macros.h"
     26 #include "lib/jxl/base/status.h"
     27 
     28 namespace jxl {
     29 namespace extras {
     30 namespace {
     31 
     32 #define QUIT(M)               \
     33  fprintf(stderr, "%s\n", M); \
     34  return false;
     35 
     36 struct BoxProcessor {
     37  explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); }
     38 
     39  bool InitializeOutput(std::vector<uint8_t>* out) {
     40    if (out == nullptr) {
     41      fprintf(stderr, "internal: out == nullptr\n");
     42      return false;
     43    }
     44    box_data_ = out;
     45    return AddMoreOutput();
     46  }
     47 
     48  bool AddMoreOutput() {
     49    if (box_data_ == nullptr) {
     50      fprintf(stderr, "internal: box_data_ == nullptr\n");
     51      return false;
     52    }
     53    Flush();
     54    static const size_t kBoxOutputChunkSize = 1 << 16;
     55    box_data_->resize(box_data_->size() + kBoxOutputChunkSize);
     56    next_out_ = box_data_->data() + total_size_;
     57    avail_out_ = box_data_->size() - total_size_;
     58    if (JXL_DEC_SUCCESS !=
     59        JxlDecoderSetBoxBuffer(dec_, next_out_, avail_out_)) {
     60      fprintf(stderr, "JxlDecoderSetBoxBuffer failed\n");
     61      return false;
     62    }
     63    return true;
     64  }
     65 
     66  void FinalizeOutput() {
     67    if (box_data_ == nullptr) return;
     68    Flush();
     69    box_data_->resize(total_size_);
     70    Reset();
     71  }
     72 
     73 private:
     74  JxlDecoder* dec_;
     75  std::vector<uint8_t>* box_data_;
     76  uint8_t* next_out_;
     77  size_t avail_out_;
     78  size_t total_size_;
     79 
     80  void Reset() {
     81    box_data_ = nullptr;
     82    next_out_ = nullptr;
     83    avail_out_ = 0;
     84    total_size_ = 0;
     85  }
     86  void Flush() {
     87    if (box_data_ == nullptr) return;
     88    size_t remaining = JxlDecoderReleaseBoxBuffer(dec_);
     89    size_t bytes_written = avail_out_ - remaining;
     90    next_out_ += bytes_written;
     91    avail_out_ -= bytes_written;
     92    total_size_ += bytes_written;
     93  }
     94 };
     95 
     96 void SetBitDepthFromDataType(JxlDataType data_type, uint32_t* bits_per_sample,
     97                             uint32_t* exponent_bits_per_sample) {
     98  switch (data_type) {
     99    case JXL_TYPE_UINT8:
    100      *bits_per_sample = 8;
    101      *exponent_bits_per_sample = 0;
    102      break;
    103    case JXL_TYPE_UINT16:
    104      *bits_per_sample = 16;
    105      *exponent_bits_per_sample = 0;
    106      break;
    107    case JXL_TYPE_FLOAT16:
    108      *bits_per_sample = 16;
    109      *exponent_bits_per_sample = 5;
    110      break;
    111    case JXL_TYPE_FLOAT:
    112      *bits_per_sample = 32;
    113      *exponent_bits_per_sample = 8;
    114      break;
    115  }
    116 }
    117 
    118 template <typename T>
    119 void UpdateBitDepth(JxlBitDepth bit_depth, JxlDataType data_type, T* info) {
    120  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
    121    SetBitDepthFromDataType(data_type, &info->bits_per_sample,
    122                            &info->exponent_bits_per_sample);
    123  } else if (bit_depth.type == JXL_BIT_DEPTH_CUSTOM) {
    124    info->bits_per_sample = bit_depth.bits_per_sample;
    125    info->exponent_bits_per_sample = bit_depth.exponent_bits_per_sample;
    126  }
    127 }
    128 
    129 }  // namespace
    130 
    131 bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
    132                    const JXLDecompressParams& dparams, size_t* decoded_bytes,
    133                    PackedPixelFile* ppf, std::vector<uint8_t>* jpeg_bytes) {
    134  JxlSignature sig = JxlSignatureCheck(bytes, bytes_size);
    135  // silently return false if this is not a JXL file
    136  if (sig == JXL_SIG_INVALID) return false;
    137 
    138  auto decoder = JxlDecoderMake(dparams.memory_manager);
    139  JxlDecoder* dec = decoder.get();
    140  ppf->frames.clear();
    141 
    142  if (dparams.runner_opaque != nullptr &&
    143      JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec, dparams.runner,
    144                                                     dparams.runner_opaque)) {
    145    fprintf(stderr, "JxlEncoderSetParallelRunner failed\n");
    146    return false;
    147  }
    148 
    149  JxlPixelFormat format = {};  // Initialize to calm down clang-tidy.
    150  std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats;
    151 
    152  JxlColorEncoding color_encoding;
    153  size_t num_color_channels = 0;
    154  if (!dparams.color_space.empty()) {
    155    if (!jxl::ParseDescription(dparams.color_space, &color_encoding)) {
    156      fprintf(stderr, "Failed to parse color space %s.\n",
    157              dparams.color_space.c_str());
    158      return false;
    159    }
    160    num_color_channels =
    161        color_encoding.color_space == JXL_COLOR_SPACE_GRAY ? 1 : 3;
    162  }
    163 
    164  bool can_reconstruct_jpeg = false;
    165  std::vector<uint8_t> jpeg_data_chunk;
    166  if (jpeg_bytes != nullptr) {
    167    // This bound is very likely to be enough to hold the entire
    168    // reconstructed JPEG, to avoid having to do expensive retries.
    169    jpeg_data_chunk.resize(bytes_size * 3 / 2 + 1024);
    170    jpeg_bytes->resize(0);
    171  }
    172 
    173  int events = (JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
    174 
    175  bool max_passes_defined =
    176      (dparams.max_passes < std::numeric_limits<uint32_t>::max());
    177  if (max_passes_defined || dparams.max_downsampling > 1) {
    178    events |= JXL_DEC_FRAME_PROGRESSION;
    179    if (max_passes_defined) {
    180      JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kPasses);
    181    } else {
    182      JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kLastPasses);
    183    }
    184  }
    185  if (jpeg_bytes != nullptr) {
    186    events |= JXL_DEC_JPEG_RECONSTRUCTION;
    187  } else {
    188    events |= (JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_PREVIEW_IMAGE |
    189               JXL_DEC_BOX);
    190    if (accepted_formats.empty()) {
    191      // decoding just the metadata, not the pixel data
    192      events ^= (JXL_DEC_FULL_IMAGE | JXL_DEC_PREVIEW_IMAGE);
    193    }
    194  }
    195  if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec, events)) {
    196    fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
    197    return false;
    198  }
    199  if (jpeg_bytes == nullptr) {
    200    if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors(
    201                               dec, TO_JXL_BOOL(dparams.render_spotcolors))) {
    202      fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n");
    203      return false;
    204    }
    205    if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation(
    206                               dec, TO_JXL_BOOL(dparams.keep_orientation))) {
    207      fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n");
    208      return false;
    209    }
    210    if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha(
    211                               dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) {
    212      fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n");
    213      return false;
    214    }
    215    if (dparams.display_nits > 0 &&
    216        JXL_DEC_SUCCESS !=
    217            JxlDecoderSetDesiredIntensityTarget(dec, dparams.display_nits)) {
    218      fprintf(stderr, "Decoder failed to set desired intensity target\n");
    219      return false;
    220    }
    221    if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec, JXL_TRUE)) {
    222      fprintf(stderr, "JxlDecoderSetDecompressBoxes failed\n");
    223      return false;
    224    }
    225  }
    226  if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec, bytes, bytes_size)) {
    227    fprintf(stderr, "Decoder failed to set input\n");
    228    return false;
    229  }
    230  uint32_t progression_index = 0;
    231  bool codestream_done = jpeg_bytes == nullptr && accepted_formats.empty();
    232  BoxProcessor boxes(dec);
    233  for (;;) {
    234    JxlDecoderStatus status = JxlDecoderProcessInput(dec);
    235    if (status == JXL_DEC_ERROR) {
    236      fprintf(stderr, "Failed to decode image\n");
    237      return false;
    238    } else if (status == JXL_DEC_NEED_MORE_INPUT) {
    239      if (codestream_done) {
    240        break;
    241      }
    242      if (dparams.allow_partial_input) {
    243        if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) {
    244          fprintf(stderr,
    245                  "Input file is truncated and there is no preview "
    246                  "available yet.\n");
    247          return false;
    248        }
    249        break;
    250      }
    251      size_t released_size = JxlDecoderReleaseInput(dec);
    252      fprintf(stderr,
    253              "Input file is truncated (total bytes: %" PRIuS
    254              ", processed bytes: %" PRIuS
    255              ") and --allow_partial_files is not present.\n",
    256              bytes_size, bytes_size - released_size);
    257      return false;
    258    } else if (status == JXL_DEC_BOX) {
    259      boxes.FinalizeOutput();
    260      JxlBoxType box_type;
    261      if (JXL_DEC_SUCCESS != JxlDecoderGetBoxType(dec, box_type, JXL_TRUE)) {
    262        fprintf(stderr, "JxlDecoderGetBoxType failed\n");
    263        return false;
    264      }
    265      std::vector<uint8_t>* box_data = nullptr;
    266      if (memcmp(box_type, "Exif", 4) == 0) {
    267        box_data = &ppf->metadata.exif;
    268      } else if (memcmp(box_type, "iptc", 4) == 0) {
    269        box_data = &ppf->metadata.iptc;
    270      } else if (memcmp(box_type, "jumb", 4) == 0) {
    271        box_data = &ppf->metadata.jumbf;
    272      } else if (memcmp(box_type, "jhgm", 4) == 0) {
    273        box_data = &ppf->metadata.jhgm;
    274      } else if (memcmp(box_type, "xml ", 4) == 0) {
    275        box_data = &ppf->metadata.xmp;
    276      }
    277      if (box_data) {
    278        if (!boxes.InitializeOutput(box_data)) {
    279          return false;
    280        }
    281      }
    282    } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
    283      if (!boxes.AddMoreOutput()) {
    284        return false;
    285      }
    286    } else if (status == JXL_DEC_JPEG_RECONSTRUCTION) {
    287      can_reconstruct_jpeg = true;
    288      // Decoding to JPEG.
    289      if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec,
    290                                                     jpeg_data_chunk.data(),
    291                                                     jpeg_data_chunk.size())) {
    292        fprintf(stderr, "Decoder failed to set JPEG Buffer\n");
    293        return false;
    294      }
    295    } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) {
    296      if (jpeg_bytes == nullptr) {
    297        fprintf(stderr, "internal: jpeg_bytes == nullptr\n");
    298        return false;
    299      }
    300      // Decoded a chunk to JPEG.
    301      size_t used_jpeg_output =
    302          jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec);
    303      jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(),
    304                         jpeg_data_chunk.data() + used_jpeg_output);
    305      if (used_jpeg_output == 0) {
    306        // Chunk is too small.
    307        jpeg_data_chunk.resize(jpeg_data_chunk.size() * 2);
    308      }
    309      if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec,
    310                                                     jpeg_data_chunk.data(),
    311                                                     jpeg_data_chunk.size())) {
    312        fprintf(stderr, "Decoder failed to set JPEG Buffer\n");
    313        return false;
    314      }
    315    } else if (status == JXL_DEC_BASIC_INFO) {
    316      if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &ppf->info)) {
    317        fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
    318        return false;
    319      }
    320      if (accepted_formats.empty()) continue;
    321      if (num_color_channels != 0) {
    322        // Mark the change in number of color channels due to the requested
    323        // color space.
    324        ppf->info.num_color_channels = num_color_channels;
    325      }
    326      if (dparams.output_bitdepth.type == JXL_BIT_DEPTH_CUSTOM) {
    327        // Select format based on custom bits per sample.
    328        ppf->info.bits_per_sample = dparams.output_bitdepth.bits_per_sample;
    329      }
    330      // Select format according to accepted formats.
    331      if (!jxl::extras::SelectFormat(accepted_formats, ppf->info, &format)) {
    332        fprintf(stderr, "SelectFormat failed\n");
    333        return false;
    334      }
    335      bool have_alpha = (format.num_channels == 2 || format.num_channels == 4);
    336      if (!have_alpha) {
    337        // Mark in the basic info that alpha channel was dropped.
    338        ppf->info.alpha_bits = 0;
    339      } else {
    340        if (dparams.unpremultiply_alpha) {
    341          // Mark in the basic info that alpha was unpremultiplied.
    342          ppf->info.alpha_premultiplied = JXL_FALSE;
    343        }
    344      }
    345      bool alpha_found = false;
    346      for (uint32_t i = 0; i < ppf->info.num_extra_channels; ++i) {
    347        JxlExtraChannelInfo eci;
    348        if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &eci)) {
    349          fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n");
    350          return false;
    351        }
    352        if (eci.type == JXL_CHANNEL_ALPHA && have_alpha && !alpha_found) {
    353          // Skip the first alpha channels because it is already present in the
    354          // interleaved image.
    355          alpha_found = true;
    356          continue;
    357        }
    358        std::string name(eci.name_length + 1, 0);
    359        if (JXL_DEC_SUCCESS !=
    360            JxlDecoderGetExtraChannelName(
    361                dec, i, const_cast<char*>(name.data()), name.size())) {
    362          fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n");
    363          return false;
    364        }
    365        name.resize(eci.name_length);
    366        ppf->extra_channels_info.push_back({eci, i, name});
    367      }
    368    } else if (status == JXL_DEC_COLOR_ENCODING) {
    369      if (!dparams.color_space.empty()) {
    370        if (ppf->info.uses_original_profile) {
    371          fprintf(stderr,
    372                  "Warning: --color_space ignored because the image is "
    373                  "not XYB encoded.\n");
    374        } else {
    375          JxlDecoderSetCms(dec, *JxlGetDefaultCms());
    376          if (JXL_DEC_SUCCESS !=
    377              JxlDecoderSetPreferredColorProfile(dec, &color_encoding)) {
    378            fprintf(stderr, "Failed to set color space.\n");
    379            return false;
    380          }
    381        }
    382      }
    383      size_t icc_size = 0;
    384      JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA;
    385      if (JXL_DEC_SUCCESS !=
    386          JxlDecoderGetICCProfileSize(dec, target, &icc_size)) {
    387        fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
    388      }
    389      if (icc_size != 0) {
    390        ppf->icc.resize(icc_size);
    391        if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
    392                                   dec, target, ppf->icc.data(), icc_size)) {
    393          fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
    394          return false;
    395        }
    396      }
    397      if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile(
    398                                 dec, target, &ppf->color_encoding)) {
    399        ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN;
    400      }
    401      ppf->primary_color_representation =
    402          PackedPixelFile::kColorEncodingIsPrimary;
    403      icc_size = 0;
    404      target = JXL_COLOR_PROFILE_TARGET_ORIGINAL;
    405      if (JXL_DEC_SUCCESS !=
    406          JxlDecoderGetICCProfileSize(dec, target, &icc_size)) {
    407        fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
    408      }
    409      if (icc_size != 0) {
    410        ppf->orig_icc.resize(icc_size);
    411        if (JXL_DEC_SUCCESS !=
    412            JxlDecoderGetColorAsICCProfile(dec, target, ppf->orig_icc.data(),
    413                                           icc_size)) {
    414          fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
    415          return false;
    416        }
    417        ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
    418      }
    419    } else if (status == JXL_DEC_FRAME) {
    420      auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize,
    421                                                       ppf->info.ysize, format);
    422      JXL_ASSIGN_OR_QUIT(jxl::extras::PackedFrame frame,
    423                         jxl::extras::PackedFrame::Create(
    424                             ppf->info.xsize, ppf->info.ysize, format),
    425                         "Failed to create image frame.");
    426      if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) {
    427        fprintf(stderr, "JxlDecoderGetFrameHeader failed\n");
    428        return false;
    429      }
    430      frame.name.resize(frame.frame_info.name_length + 1, 0);
    431      if (JXL_DEC_SUCCESS !=
    432          JxlDecoderGetFrameName(dec, const_cast<char*>(frame.name.data()),
    433                                 frame.name.size())) {
    434        fprintf(stderr, "JxlDecoderGetFrameName failed\n");
    435        return false;
    436      }
    437      frame.name.resize(frame.frame_info.name_length);
    438      ppf->frames.emplace_back(std::move(frame));
    439      progression_index = 0;
    440    } else if (status == JXL_DEC_FRAME_PROGRESSION) {
    441      size_t downsampling = JxlDecoderGetIntendedDownsamplingRatio(dec);
    442      if ((max_passes_defined && progression_index >= dparams.max_passes) ||
    443          (!max_passes_defined && downsampling <= dparams.max_downsampling)) {
    444        if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) {
    445          fprintf(stderr, "JxlDecoderFlushImage failed\n");
    446          return false;
    447        }
    448        if (ppf->frames.back().frame_info.is_last) {
    449          break;
    450        }
    451        if (JXL_DEC_SUCCESS != JxlDecoderSkipCurrentFrame(dec)) {
    452          fprintf(stderr, "JxlDecoderSkipCurrentFrame failed\n");
    453          return false;
    454        }
    455      }
    456      ++progression_index;
    457    } else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) {
    458      size_t buffer_size;
    459      if (JXL_DEC_SUCCESS !=
    460          JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)) {
    461        fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n");
    462        return false;
    463      }
    464      JXL_ASSIGN_OR_QUIT(
    465          jxl::extras::PackedImage preview_image,
    466          jxl::extras::PackedImage::Create(ppf->info.preview.xsize,
    467                                           ppf->info.preview.ysize, format),
    468          "Failed to create preview image.");
    469      ppf->preview_frame =
    470          jxl::make_unique<jxl::extras::PackedFrame>(std::move(preview_image));
    471      if (buffer_size != ppf->preview_frame->color.pixels_size) {
    472        fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n",
    473                buffer_size, ppf->preview_frame->color.pixels_size);
    474        return false;
    475      }
    476      if (JXL_DEC_SUCCESS !=
    477          JxlDecoderSetPreviewOutBuffer(
    478              dec, &format, ppf->preview_frame->color.pixels(), buffer_size)) {
    479        fprintf(stderr, "JxlDecoderSetPreviewOutBuffer failed\n");
    480        return false;
    481      }
    482    } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
    483      if (jpeg_bytes != nullptr) {
    484        break;
    485      }
    486      size_t buffer_size;
    487      if (JXL_DEC_SUCCESS !=
    488          JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)) {
    489        fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
    490        return false;
    491      }
    492      jxl::extras::PackedFrame& frame = ppf->frames.back();
    493      if (buffer_size != frame.color.pixels_size) {
    494        fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n",
    495                buffer_size, frame.color.pixels_size);
    496        return false;
    497      }
    498 
    499      if (dparams.use_image_callback) {
    500        auto callback = [](void* opaque, size_t x, size_t y, size_t num_pixels,
    501                           const void* pixels) {
    502          auto* ppf = reinterpret_cast<jxl::extras::PackedPixelFile*>(opaque);
    503          jxl::extras::PackedImage& color = ppf->frames.back().color;
    504          uint8_t* pixels_buffer = reinterpret_cast<uint8_t*>(color.pixels());
    505          size_t sample_size = color.pixel_stride();
    506          memcpy(pixels_buffer + (color.stride * y + sample_size * x), pixels,
    507                 num_pixels * sample_size);
    508        };
    509        if (JXL_DEC_SUCCESS !=
    510            JxlDecoderSetImageOutCallback(dec, &format, callback, ppf)) {
    511          fprintf(stderr, "JxlDecoderSetImageOutCallback failed\n");
    512          return false;
    513        }
    514      } else {
    515        if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec, &format,
    516                                                           frame.color.pixels(),
    517                                                           buffer_size)) {
    518          fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
    519          return false;
    520        }
    521      }
    522      if (JXL_DEC_SUCCESS !=
    523          JxlDecoderSetImageOutBitDepth(dec, &dparams.output_bitdepth)) {
    524        fprintf(stderr, "JxlDecoderSetImageOutBitDepth failed\n");
    525        return false;
    526      }
    527      UpdateBitDepth(dparams.output_bitdepth, format.data_type, &ppf->info);
    528      bool have_alpha = (format.num_channels == 2 || format.num_channels == 4);
    529      if (have_alpha) {
    530        // Interleaved alpha channels has the same bit depth as color channels.
    531        ppf->info.alpha_bits = ppf->info.bits_per_sample;
    532        ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample;
    533      }
    534      JxlPixelFormat ec_format = format;
    535      ec_format.num_channels = 1;
    536      for (auto& eci : ppf->extra_channels_info) {
    537        JXL_ASSIGN_OR_QUIT(jxl::extras::PackedImage image,
    538                           jxl::extras::PackedImage::Create(
    539                               ppf->info.xsize, ppf->info.ysize, ec_format),
    540                           "Failed to create extra channel image.");
    541        frame.extra_channels.emplace_back(std::move(image));
    542        auto& ec = frame.extra_channels.back();
    543        size_t buffer_size;
    544        if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize(
    545                                   dec, &ec_format, &buffer_size, eci.index)) {
    546          fprintf(stderr, "JxlDecoderExtraChannelBufferSize failed\n");
    547          return false;
    548        }
    549        if (buffer_size != ec.pixels_size) {
    550          fprintf(stderr,
    551                  "Invalid extra channel buffer size"
    552                  " %" PRIuS " %" PRIuS "\n",
    553                  buffer_size, ec.pixels_size);
    554          return false;
    555        }
    556        if (JXL_DEC_SUCCESS !=
    557            JxlDecoderSetExtraChannelBuffer(dec, &ec_format, ec.pixels(),
    558                                            buffer_size, eci.index)) {
    559          fprintf(stderr, "JxlDecoderSetExtraChannelBuffer failed\n");
    560          return false;
    561        }
    562        UpdateBitDepth(dparams.output_bitdepth, ec_format.data_type,
    563                       &eci.ec_info);
    564      }
    565    } else if (status == JXL_DEC_SUCCESS) {
    566      // Decoding finished successfully.
    567      break;
    568    } else if (status == JXL_DEC_PREVIEW_IMAGE) {
    569      // Nothing to do.
    570    } else if (status == JXL_DEC_FULL_IMAGE) {
    571      if (jpeg_bytes != nullptr || ppf->frames.back().frame_info.is_last) {
    572        codestream_done = true;
    573      }
    574    } else {
    575      fprintf(stderr, "Error: unexpected status: %d\n",
    576              static_cast<int>(status));
    577      return false;
    578    }
    579  }
    580  boxes.FinalizeOutput();
    581  if (!ppf->metadata.exif.empty()) {
    582    // Verify that Exif box has a valid TIFF header at the specified offset.
    583    // Discard bytes preceding the header.
    584    if (ppf->metadata.exif.size() >= 4) {
    585      uint32_t offset = LoadBE32(ppf->metadata.exif.data());
    586      if (offset <= ppf->metadata.exif.size() - 8) {
    587        std::vector<uint8_t> exif(ppf->metadata.exif.begin() + 4 + offset,
    588                                  ppf->metadata.exif.end());
    589        bool bigendian;
    590        if (IsExif(exif, &bigendian)) {
    591          ppf->metadata.exif = std::move(exif);
    592        } else {
    593          fprintf(stderr, "Warning: invalid TIFF header in Exif\n");
    594        }
    595      } else {
    596        fprintf(stderr, "Warning: invalid Exif offset: %" PRIu32 "\n", offset);
    597      }
    598    } else {
    599      fprintf(stderr, "Warning: invalid Exif length: %" PRIuS "\n",
    600              ppf->metadata.exif.size());
    601    }
    602  }
    603  if (jpeg_bytes != nullptr) {
    604    if (!can_reconstruct_jpeg) return false;
    605    size_t used_jpeg_output =
    606        jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec);
    607    jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(),
    608                       jpeg_data_chunk.data() + used_jpeg_output);
    609  }
    610  if (decoded_bytes) {
    611    *decoded_bytes = bytes_size - JxlDecoderReleaseInput(dec);
    612  }
    613  return true;
    614 }
    615 
    616 }  // namespace extras
    617 }  // namespace jxl