tor-browser

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

decode.cc (105213B)


      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 <jxl/decode.h>
      7 #include <jxl/types.h>
      8 #include <jxl/version.h>
      9 
     10 #include <algorithm>
     11 #include <array>
     12 #include <functional>
     13 #include <memory>
     14 #include <utility>
     15 #include <vector>
     16 
     17 #include "lib/jxl/base/byte_order.h"
     18 #include "lib/jxl/base/common.h"
     19 #include "lib/jxl/base/compiler_specific.h"
     20 #include "lib/jxl/base/span.h"
     21 #include "lib/jxl/base/status.h"
     22 #include "lib/jxl/padded_bytes.h"
     23 
     24 // JPEGXL_ENABLE_BOXES, JPEGXL_ENABLE_TRANSCODE_JPEG
     25 #include "lib/jxl/common.h"
     26 
     27 #if JPEGXL_ENABLE_BOXES || JPEGXL_ENABLE_TRANSCODE_JPEG
     28 #include "lib/jxl/box_content_decoder.h"
     29 #endif
     30 #include "lib/jxl/dec_frame.h"
     31 #if JPEGXL_ENABLE_TRANSCODE_JPEG
     32 #include "lib/jxl/decode_to_jpeg.h"
     33 #endif
     34 #include "lib/jxl/fields.h"
     35 #include "lib/jxl/frame_dimensions.h"
     36 #include "lib/jxl/frame_header.h"
     37 #include "lib/jxl/headers.h"
     38 #include "lib/jxl/icc_codec.h"
     39 #include "lib/jxl/image_bundle.h"
     40 #include "lib/jxl/memory_manager_internal.h"
     41 
     42 namespace {
     43 
     44 // Checks if a + b > size, taking possible integer overflow into account.
     45 bool OutOfBounds(size_t a, size_t b, size_t size) {
     46  size_t pos = a + b;
     47  if (pos > size) return true;
     48  if (pos < a) return true;  // overflow happened
     49  return false;
     50 }
     51 
     52 JXL_INLINE size_t InitialBasicInfoSizeHint() {
     53  // Amount of bytes before the start of the codestream in the container format,
     54  // assuming that the codestream is the first box after the signature and
     55  // filetype boxes. 12 bytes signature box + 20 bytes filetype box + 16 bytes
     56  // codestream box length + name + optional XLBox length.
     57  const size_t container_header_size = 48;
     58 
     59  // Worst-case amount of bytes for basic info of the JPEG XL codestream header,
     60  // that is all information up to and including extra_channel_bits. Up to
     61  // around 2 bytes signature + 8 bytes SizeHeader + 31 bytes ColorEncoding + 4
     62  // bytes rest of ImageMetadata + 5 bytes part of ImageMetadata2.
     63  // TODO(lode): recompute and update this value when alpha_bits is moved to
     64  // extra channels info.
     65  const size_t max_codestream_basic_info_size = 50;
     66 
     67  return container_header_size + max_codestream_basic_info_size;
     68 }
     69 
     70 // Debug-printing failure macro similar to JXL_FAILURE, but for the status code
     71 // JXL_DEC_ERROR
     72 #if (JXL_CRASH_ON_ERROR)
     73 #define JXL_API_ERROR(format, ...)                                           \
     74  (::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, ##__VA_ARGS__), \
     75   ::jxl::Abort(), JXL_DEC_ERROR)
     76 #else  // JXL_CRASH_ON_ERROR
     77 #define JXL_API_ERROR(format, ...)                                             \
     78  (((JXL_IS_DEBUG_BUILD) &&                                                    \
     79    ::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, ##__VA_ARGS__)), \
     80   JXL_DEC_ERROR)
     81 #endif  // JXL_CRASH_ON_ERROR
     82 
     83 // Error caused by bad input (invalid file) rather than incorrect API usage.
     84 // For now there is no way to distinguish these two types of errors yet.
     85 #define JXL_INPUT_ERROR(format, ...) JXL_API_ERROR(format, ##__VA_ARGS__)
     86 
     87 JxlDecoderStatus ConvertStatus(JxlDecoderStatus status) { return status; }
     88 
     89 JxlDecoderStatus ConvertStatus(jxl::Status status) {
     90  return status ? JXL_DEC_SUCCESS : JXL_DEC_ERROR;
     91 }
     92 
     93 #define JXL_API_RETURN_IF_ERROR(expr)               \
     94  {                                                 \
     95    JxlDecoderStatus status_ = ConvertStatus(expr); \
     96    if (status_ != JXL_DEC_SUCCESS) return status_; \
     97  }
     98 
     99 JxlSignature ReadSignature(const uint8_t* buf, size_t len, size_t* pos) {
    100  if (*pos >= len) return JXL_SIG_NOT_ENOUGH_BYTES;
    101 
    102  buf += *pos;
    103  len -= *pos;
    104 
    105  // JPEG XL codestream: 0xff 0x0a
    106  if (len >= 1 && buf[0] == 0xff) {
    107    if (len < 2) {
    108      return JXL_SIG_NOT_ENOUGH_BYTES;
    109    } else if (buf[1] == jxl::kCodestreamMarker) {
    110      *pos += 2;
    111      return JXL_SIG_CODESTREAM;
    112    } else {
    113      return JXL_SIG_INVALID;
    114    }
    115  }
    116 
    117  // JPEG XL container
    118  if (len >= 1 && buf[0] == 0) {
    119    if (len < 12) {
    120      return JXL_SIG_NOT_ENOUGH_BYTES;
    121    } else if (buf[1] == 0 && buf[2] == 0 && buf[3] == 0xC && buf[4] == 'J' &&
    122               buf[5] == 'X' && buf[6] == 'L' && buf[7] == ' ' &&
    123               buf[8] == 0xD && buf[9] == 0xA && buf[10] == 0x87 &&
    124               buf[11] == 0xA) {
    125      *pos += 12;
    126      return JXL_SIG_CONTAINER;
    127    } else {
    128      return JXL_SIG_INVALID;
    129    }
    130  }
    131 
    132  return JXL_SIG_INVALID;
    133 }
    134 
    135 }  // namespace
    136 
    137 uint32_t JxlDecoderVersion(void) {
    138  return JPEGXL_MAJOR_VERSION * 1000000 + JPEGXL_MINOR_VERSION * 1000 +
    139         JPEGXL_PATCH_VERSION;
    140 }
    141 
    142 JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len) {
    143  size_t pos = 0;
    144  return ReadSignature(buf, len, &pos);
    145 }
    146 
    147 namespace {
    148 
    149 size_t BitsPerChannel(JxlDataType data_type) {
    150  switch (data_type) {
    151    case JXL_TYPE_UINT8:
    152      return 8;
    153    case JXL_TYPE_UINT16:
    154      return 16;
    155    case JXL_TYPE_FLOAT:
    156      return 32;
    157    case JXL_TYPE_FLOAT16:
    158      return 16;
    159    default:
    160      return 0;  // signals unhandled JxlDataType
    161  }
    162 }
    163 
    164 template <typename T>
    165 uint32_t GetBitDepth(JxlBitDepth bit_depth, const T& metadata,
    166                     JxlPixelFormat format) {
    167  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
    168    return BitsPerChannel(format.data_type);
    169  } else if (bit_depth.type == JXL_BIT_DEPTH_FROM_CODESTREAM) {
    170    return metadata.bit_depth.bits_per_sample;
    171  } else if (bit_depth.type == JXL_BIT_DEPTH_CUSTOM) {
    172    return bit_depth.bits_per_sample;
    173  }
    174  return 0;
    175 }
    176 
    177 enum class DecoderStage : uint32_t {
    178  kInited,              // Decoder created, no JxlDecoderProcessInput called yet
    179  kStarted,             // Running JxlDecoderProcessInput calls
    180  kCodestreamFinished,  // Codestream done, but other boxes could still occur.
    181                        // This stage can also occur before having seen the
    182                        // entire codestream if the user didn't subscribe to any
    183                        // codestream events at all, e.g. only to box events,
    184                        // or, the user only subscribed to basic info, and only
    185                        // the header of the codestream was parsed.
    186  kError,               // Error occurred, decoder object no longer usable
    187 };
    188 
    189 enum class FrameStage : uint32_t {
    190  kHeader,  // Must parse frame header.
    191  kTOC,     // Must parse TOC
    192  kFull,    // Must parse full pixels
    193 };
    194 
    195 enum class BoxStage : uint32_t {
    196  kHeader,      // Parsing box header of the next box, or start of non-container
    197                // stream
    198  kFtyp,        // The ftyp box
    199  kSkip,        // Box whose contents are skipped
    200  kCodestream,  // Handling codestream box contents, or non-container stream
    201  kPartialCodestream,  // Handling the extra header of partial codestream box
    202  kJpegRecon,          // Handling jpeg reconstruction box
    203 };
    204 
    205 enum class JpegReconStage : uint32_t {
    206  kNone,             // Not outputting
    207  kSettingMetadata,  // Ready to output, must set metadata to the jpeg_data
    208  kOutputting,       // Currently outputting the JPEG bytes
    209 };
    210 
    211 // For each internal frame, which storage locations it references, and which
    212 // storage locations it is stored in, using the bit mask as defined in
    213 // FrameDecoder::References and FrameDecoder::SaveAs.
    214 typedef struct FrameRef {
    215  int reference;
    216  int saved_as;
    217 } FrameRef;
    218 
    219 /*
    220 Given list of frame references to storage slots, and storage slots in which this
    221 frame is saved, computes which frames are required to decode the frame at the
    222 given index and any frames after it. The frames on which this depends are
    223 returned as a vector of their indices, in no particular order. The given index
    224 must be smaller than saved_as.size(), and references.size() must equal
    225 saved_as.size(). Any frames beyond saved_as and references are considered
    226 unknown future frames and must be treated as if something depends on them.
    227 */
    228 std::vector<size_t> GetFrameDependencies(size_t index,
    229                                         const std::vector<FrameRef>& refs) {
    230  JXL_DASSERT(index < refs.size());
    231 
    232  std::vector<size_t> result;
    233 
    234  constexpr size_t kNumStorage = 8;
    235 
    236  // value which indicates nothing is stored in this storage slot
    237  const size_t invalid = refs.size();
    238  // for each of the 8 storage slots, a vector that translates frame index to
    239  // frame stored in this storage slot at this point, that is, the last
    240  // frame that was stored in this slot before or at this index.
    241  std::array<std::vector<size_t>, kNumStorage> storage;
    242  for (size_t s = 0; s < kNumStorage; ++s) {
    243    storage[s].resize(refs.size());
    244    int mask = 1 << s;
    245    size_t id = invalid;
    246    for (size_t i = 0; i < refs.size(); ++i) {
    247      if (refs[i].saved_as & mask) {
    248        id = i;
    249      }
    250      storage[s][i] = id;
    251    }
    252  }
    253 
    254  std::vector<char> seen(index + 1, 0);
    255  std::vector<size_t> stack;
    256  stack.push_back(index);
    257  seen[index] = 1;
    258 
    259  // For frames after index, assume they can depend on any of the 8 storage
    260  // slots, so push the frame for each stored reference to the stack and result.
    261  // All frames after index are treated as having unknown references and with
    262  // the possibility that there are more frames after the last known.
    263  // TODO(lode): take values of saved_as and references after index, and a
    264  // input flag indicating if they are all frames of the image, to further
    265  // optimize this.
    266  for (size_t s = 0; s < kNumStorage; ++s) {
    267    size_t frame_ref = storage[s][index];
    268    if (frame_ref == invalid) continue;
    269    if (seen[frame_ref]) continue;
    270    stack.push_back(frame_ref);
    271    seen[frame_ref] = 1;
    272    result.push_back(frame_ref);
    273  }
    274 
    275  while (!stack.empty()) {
    276    size_t frame_index = stack.back();
    277    stack.pop_back();
    278    if (frame_index == 0) continue;  // first frame cannot have references
    279    for (size_t s = 0; s < kNumStorage; ++s) {
    280      int mask = 1 << s;
    281      if (!(refs[frame_index].reference & mask)) continue;
    282      size_t frame_ref = storage[s][frame_index - 1];
    283      if (frame_ref == invalid) continue;
    284      if (seen[frame_ref]) continue;
    285      stack.push_back(frame_ref);
    286      seen[frame_ref] = 1;
    287      result.push_back(frame_ref);
    288    }
    289  }
    290 
    291  return result;
    292 }
    293 
    294 // Parameters for user-requested extra channel output.
    295 struct ExtraChannelOutput {
    296  JxlPixelFormat format;
    297  void* buffer;
    298  size_t buffer_size;
    299 };
    300 
    301 }  // namespace
    302 
    303 namespace jxl {
    304 
    305 typedef struct JxlDecoderFrameIndexBoxEntryStruct {
    306  // OFFi: offset of start byte of this frame compared to start
    307  // byte of previous frame from this index in the JPEG XL codestream. For the
    308  // first frame, this is the offset from the first byte of the JPEG XL
    309  // codestream.
    310  uint64_t OFFi;
    311  // Ti: duration in ticks between the start of this frame and
    312  // the start of the next frame in the index. If this is the last frame in the
    313  // index, this is the duration in ticks between the start of this frame and
    314  // the end of the stream. A tick lasts TNUM / TDEN seconds.
    315  uint32_t Ti;
    316  // Fi: amount of frames the next frame in the index occurs
    317  // after this frame. If this is the last frame in the index, this is the
    318  // amount of frames after this frame in the remainder of the stream. Only
    319  // frames that are presented by the decoder are counted for this purpose, this
    320  // excludes frames that are not intended for display but for compositing with
    321  // other frames, such as frames that aren't the last frame with a duration of
    322  // 0 ticks.
    323  uint32_t Fi;
    324 } JxlDecoderFrameIndexBoxEntry;
    325 
    326 typedef struct JxlDecoderFrameIndexBoxStruct {
    327  int64_t NF() const { return entries.size(); }
    328  int32_t TNUM = 1;
    329  int32_t TDEN = 1000;
    330 
    331  std::vector<JxlDecoderFrameIndexBoxEntry> entries;
    332 
    333  // That way we can ensure that every index box will have the first frame.
    334  // If the API user decides to mark it as an indexed frame, we call
    335  // the AddFrame again, this time with requested.
    336  void AddFrame(uint64_t OFFi, uint32_t Ti, uint32_t Fi) {
    337    JxlDecoderFrameIndexBoxEntry e;
    338    e.OFFi = OFFi;
    339    e.Ti = Ti;
    340    e.Fi = Fi;
    341    entries.push_back(e);
    342  }
    343 } JxlDecoderFrameIndexBox;
    344 
    345 }  // namespace jxl
    346 
    347 // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
    348 struct JxlDecoderStruct {
    349  JxlDecoderStruct() = default;
    350 
    351  JxlMemoryManager memory_manager;
    352  std::unique_ptr<jxl::ThreadPool> thread_pool;
    353 
    354  DecoderStage stage;
    355 
    356  // Status of progression, internal.
    357  bool got_signature;
    358  // Indicates we know that we've seen the last codestream box: either this
    359  // was a jxlc box, or a jxlp box that has its index indicated as last by
    360  // having its most significant bit set, or no boxes are used at all. This
    361  // does not indicate the full codestream has already been seen, only the
    362  // last box of it has been initiated.
    363  bool last_codestream_seen;
    364  bool got_codestream_signature;
    365  bool got_basic_info;
    366  bool got_transform_data;  // To skip everything before ICC.
    367  bool got_all_headers;     // Codestream metadata headers.
    368  bool post_headers;        // Already decoding pixels.
    369  std::unique_ptr<jxl::ICCReader> icc_reader;
    370  jxl::JxlDecoderFrameIndexBox frame_index_box;
    371  // This means either we actually got the preview image, or determined we
    372  // cannot get it or there is none.
    373  bool got_preview_image;
    374  bool preview_frame;
    375 
    376  // Position of next_in in the original file including box format if present
    377  // (as opposed to position in the codestream)
    378  size_t file_pos;
    379 
    380  size_t box_contents_begin;
    381  size_t box_contents_end;
    382  size_t box_contents_size;
    383  size_t box_size;
    384  size_t header_size;
    385  // Either a final box that runs until EOF, or the case of no container format
    386  // at all.
    387  bool box_contents_unbounded;
    388 
    389  JxlBoxType box_type;
    390  JxlBoxType box_decoded_type;  // Underlying type for brob boxes
    391  // Set to true right after a JXL_DEC_BOX event only.
    392  bool box_event;
    393  bool decompress_boxes;
    394 
    395  bool box_out_buffer_set;
    396  // Whether the out buffer is set for the current box, if the user did not yet
    397  // release the buffer while the next box is encountered, this will be set to
    398  // false. If this is false, no JXL_DEC_NEED_MORE_INPUT is emitted
    399  // (irrespective of the value of box_out_buffer_set), because not setting
    400  // output indicates the user does not wish the data of this box.
    401  bool box_out_buffer_set_current_box;
    402  uint8_t* box_out_buffer;
    403  size_t box_out_buffer_size;
    404  // which byte of the full box content the start of the out buffer points to
    405  size_t box_out_buffer_begin;
    406  // which byte of box_out_buffer to write to next
    407  size_t box_out_buffer_pos;
    408 
    409  // Settings
    410  bool keep_orientation;
    411  bool unpremul_alpha;
    412  bool render_spotcolors;
    413  bool coalescing;
    414  float desired_intensity_target;
    415 
    416  // Bitfield, for which informative events (JXL_DEC_BASIC_INFO, etc...) the
    417  // decoder returns a status. By default, do not return for any of the events,
    418  // only return when the decoder cannot continue because it needs more input or
    419  // output data.
    420  int events_wanted;
    421  int orig_events_wanted;
    422 
    423  // Fields for reading the basic info from the header.
    424  size_t basic_info_size_hint;
    425  bool have_container;
    426  size_t box_count;
    427 
    428  // The level of progressive detail in frame decoding.
    429  JxlProgressiveDetail prog_detail = kDC;
    430  // The progressive detail of the current frame.
    431  JxlProgressiveDetail frame_prog_detail;
    432  // The intended downsampling ratio for the current progression step.
    433  size_t downsampling_target;
    434 
    435  // Set to true if either an image out buffer or an image out callback was set.
    436  bool image_out_buffer_set;
    437 
    438  // Owned by the caller, buffer for preview or full resolution image.
    439  void* image_out_buffer;
    440  JxlImageOutInitCallback image_out_init_callback;
    441  JxlImageOutRunCallback image_out_run_callback;
    442  JxlImageOutDestroyCallback image_out_destroy_callback;
    443  void* image_out_init_opaque;
    444  struct SimpleImageOutCallback {
    445    JxlImageOutCallback callback;
    446    void* opaque;
    447  };
    448  SimpleImageOutCallback simple_image_out_callback;
    449 
    450  size_t image_out_size;
    451 
    452  JxlPixelFormat image_out_format;
    453  JxlBitDepth image_out_bit_depth;
    454 
    455  // For extra channels. Empty if no extra channels are requested, and they are
    456  // reset each frame
    457  std::vector<ExtraChannelOutput> extra_channel_output;
    458 
    459  jxl::CodecMetadata metadata;
    460  // Same as metadata.m, except for the color_encoding, which is set to the
    461  // output encoding.
    462  jxl::ImageMetadata image_metadata;
    463  std::unique_ptr<jxl::ImageBundle> ib;
    464 
    465  std::unique_ptr<jxl::PassesDecoderState> passes_state;
    466  std::unique_ptr<jxl::FrameDecoder> frame_dec;
    467  size_t next_section;
    468  std::vector<char> section_processed;
    469 
    470  // headers and TOC for the current frame. When got_toc is true, this is
    471  // always the frame header of the last frame of the current still series,
    472  // that is, the displayed frame.
    473  std::unique_ptr<jxl::FrameHeader> frame_header;
    474 
    475  size_t remaining_frame_size;
    476  FrameStage frame_stage;
    477  bool dc_frame_progression_done;
    478  // The currently processed frame is the last of the current composite still,
    479  // and so must be returned as pixels
    480  bool is_last_of_still;
    481  // The currently processed frame is the last of the codestream
    482  bool is_last_total;
    483  // How many frames to skip.
    484  size_t skip_frames;
    485  // Skipping the current frame. May be false if skip_frames was just set to
    486  // a positive value while already processing a current frame, then
    487  // skipping_frame will be enabled only for the next frame.
    488  bool skipping_frame;
    489 
    490  // Amount of internal frames and external frames started. External frames are
    491  // user-visible frames, internal frames includes all external frames and
    492  // also invisible frames such as patches, blending-only and dc_level frames.
    493  size_t internal_frames;
    494  size_t external_frames;
    495 
    496  std::vector<FrameRef> frame_refs;
    497 
    498  // Translates external frame index to internal frame index. The external
    499  // index is the index of user-visible frames. The internal index can be larger
    500  // since non-visible frames (such as frames with patches, ...) are included.
    501  std::vector<size_t> frame_external_to_internal;
    502 
    503  // Whether the frame with internal index is required to decode the frame
    504  // being skipped to or any frames after that. If no skipping is active,
    505  // this vector is ignored. If the current internal frame index is beyond this
    506  // vector, it must be treated as a required frame.
    507  std::vector<char> frame_required;
    508 
    509  // Codestream input data is copied here temporarily when the decoder needs
    510  // more input bytes to process the next part of the stream. We copy the input
    511  // data in order to be able to release it all through the API it when
    512  // returning JXL_DEC_NEED_MORE_INPUT.
    513  std::vector<uint8_t> codestream_copy;
    514  // Number of bytes at the end of codestream_copy that were not yet consumed
    515  // by calling AdvanceInput().
    516  size_t codestream_unconsumed;
    517  // Position in the codestream_copy vector that the decoder already finished
    518  // processing. It can be greater than the current size of codestream_copy in
    519  // case where the decoder skips some parts of the frame that were not yet
    520  // provided.
    521  size_t codestream_pos;
    522  // Number of bits after codestream_pos that were already processed.
    523  size_t codestream_bits_ahead;
    524 
    525  BoxStage box_stage;
    526 
    527 #if JPEGXL_ENABLE_BOXES
    528  jxl::JxlBoxContentDecoder box_content_decoder;
    529 #endif
    530 #if JPEGXL_ENABLE_TRANSCODE_JPEG
    531  jxl::JxlToJpegDecoder jpeg_decoder;
    532  // Decodes Exif or XMP metadata for JPEG reconstruction
    533  jxl::JxlBoxContentDecoder metadata_decoder;
    534  std::vector<uint8_t> exif_metadata;
    535  std::vector<uint8_t> xmp_metadata;
    536  // must store JPEG reconstruction metadata from the current box
    537  // 0 = not stored, 1 = currently storing, 2 = finished
    538  int store_exif;
    539  int store_xmp;
    540  size_t recon_out_buffer_pos;
    541  size_t recon_exif_size;  // Expected exif size as read from the jbrd box
    542  size_t recon_xmp_size;   // Expected exif size as read from the jbrd box
    543  JpegReconStage recon_output_jpeg;
    544 
    545  bool JbrdNeedMoreBoxes() const {
    546    // jbrd box wants exif but exif box not yet seen
    547    if (store_exif < 2 && recon_exif_size > 0) return true;
    548    // jbrd box wants xmp but xmp box not yet seen
    549    if (store_xmp < 2 && recon_xmp_size > 0) return true;
    550    return false;
    551  }
    552 #endif
    553 
    554  const uint8_t* next_in;
    555  size_t avail_in;
    556  bool input_closed;
    557 
    558  void AdvanceInput(size_t size) {
    559    JXL_DASSERT(avail_in >= size);
    560    next_in += size;
    561    avail_in -= size;
    562    file_pos += size;
    563  }
    564 
    565  size_t AvailableCodestream() const {
    566    size_t avail_codestream = avail_in;
    567    if (!box_contents_unbounded) {
    568      avail_codestream =
    569          std::min<size_t>(avail_codestream, box_contents_end - file_pos);
    570    }
    571    return avail_codestream;
    572  }
    573 
    574  void AdvanceCodestream(size_t size) {
    575    size_t avail_codestream = AvailableCodestream();
    576    if (codestream_copy.empty()) {
    577      if (size <= avail_codestream) {
    578        AdvanceInput(size);
    579      } else {
    580        codestream_pos = size - avail_codestream;
    581        AdvanceInput(avail_codestream);
    582      }
    583    } else {
    584      codestream_pos += size;
    585      if (codestream_pos + codestream_unconsumed >= codestream_copy.size()) {
    586        size_t advance = std::min(
    587            codestream_unconsumed,
    588            codestream_unconsumed + codestream_pos - codestream_copy.size());
    589        AdvanceInput(advance);
    590        codestream_pos -= std::min(codestream_pos, codestream_copy.size());
    591        codestream_unconsumed = 0;
    592        codestream_copy.clear();
    593      }
    594    }
    595  }
    596 
    597  JxlDecoderStatus RequestMoreInput() {
    598    if (codestream_copy.empty()) {
    599      size_t avail_codestream = AvailableCodestream();
    600      codestream_copy.insert(codestream_copy.end(), next_in,
    601                             next_in + avail_codestream);
    602      AdvanceInput(avail_codestream);
    603    } else {
    604      AdvanceInput(codestream_unconsumed);
    605      codestream_unconsumed = 0;
    606    }
    607    return JXL_DEC_NEED_MORE_INPUT;
    608  }
    609 
    610  JxlDecoderStatus GetCodestreamInput(jxl::Span<const uint8_t>* span) {
    611    if (codestream_copy.empty() && codestream_pos > 0) {
    612      size_t avail_codestream = AvailableCodestream();
    613      size_t skip = std::min<size_t>(codestream_pos, avail_codestream);
    614      AdvanceInput(skip);
    615      codestream_pos -= skip;
    616      if (codestream_pos > 0) {
    617        return RequestMoreInput();
    618      }
    619    }
    620    if (codestream_pos > codestream_copy.size()) {
    621      return JXL_API_ERROR("Internal: codestream_pos > codestream_copy.size()");
    622    }
    623    if (codestream_unconsumed > codestream_copy.size()) {
    624      return JXL_API_ERROR(
    625          "Internal: codestream_unconsumed > codestream_copy.size()");
    626    }
    627    size_t avail_codestream = AvailableCodestream();
    628    if (codestream_copy.empty()) {
    629      if (avail_codestream == 0) {
    630        return RequestMoreInput();
    631      }
    632      *span = jxl::Bytes(next_in, avail_codestream);
    633      return JXL_DEC_SUCCESS;
    634    } else {
    635      codestream_copy.insert(codestream_copy.end(),
    636                             next_in + codestream_unconsumed,
    637                             next_in + avail_codestream);
    638      codestream_unconsumed = avail_codestream;
    639      *span = jxl::Bytes(codestream_copy.data() + codestream_pos,
    640                         codestream_copy.size() - codestream_pos);
    641      return JXL_DEC_SUCCESS;
    642    }
    643  }
    644 
    645  // Whether the decoder can use more codestream input for a purpose it needs.
    646  // This returns false if the user didn't subscribe to any events that
    647  // require the codestream (e.g. only subscribed to metadata boxes), or all
    648  // parts of the codestream that are subscribed to (e.g. only basic info) have
    649  // already occurred.
    650  bool CanUseMoreCodestreamInput() const {
    651    // The decoder can set this to finished early if all relevant events were
    652    // processed, so this check works.
    653    return stage != DecoderStage::kCodestreamFinished;
    654  }
    655 };
    656 
    657 namespace {
    658 
    659 bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
    660  if (xsize == 0 || ysize == 0) return true;
    661  size_t padded_xsize = jxl::DivCeil(xsize, 32) * 32;
    662  if (padded_xsize < xsize) return false;  // overflow
    663  size_t num_pixels = padded_xsize * ysize;
    664  if (num_pixels / padded_xsize != ysize) return false;  // overflow
    665  return true;
    666 }
    667 
    668 }  // namespace
    669 
    670 // Resets the state that must be reset for both Rewind and Reset
    671 void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
    672  dec->stage = DecoderStage::kInited;
    673  dec->got_signature = false;
    674  dec->last_codestream_seen = false;
    675  dec->got_codestream_signature = false;
    676  dec->got_basic_info = false;
    677  dec->got_transform_data = false;
    678  dec->got_all_headers = false;
    679  dec->post_headers = false;
    680  if (dec->icc_reader) dec->icc_reader->Reset();
    681  dec->got_preview_image = false;
    682  dec->preview_frame = false;
    683  dec->file_pos = 0;
    684  dec->box_contents_begin = 0;
    685  dec->box_contents_end = 0;
    686  dec->box_contents_size = 0;
    687  dec->box_size = 0;
    688  dec->header_size = 0;
    689  dec->box_contents_unbounded = false;
    690  memset(dec->box_type, 0, sizeof(dec->box_type));
    691  memset(dec->box_decoded_type, 0, sizeof(dec->box_decoded_type));
    692  dec->box_event = false;
    693  dec->box_stage = BoxStage::kHeader;
    694  dec->box_out_buffer_set = false;
    695  dec->box_out_buffer_set_current_box = false;
    696  dec->box_out_buffer = nullptr;
    697  dec->box_out_buffer_size = 0;
    698  dec->box_out_buffer_begin = 0;
    699  dec->box_out_buffer_pos = 0;
    700 
    701 #if JPEGXL_ENABLE_TRANSCODE_JPEG
    702  dec->exif_metadata.clear();
    703  dec->xmp_metadata.clear();
    704  dec->store_exif = 0;
    705  dec->store_xmp = 0;
    706  dec->recon_out_buffer_pos = 0;
    707  dec->recon_exif_size = 0;
    708  dec->recon_xmp_size = 0;
    709  dec->recon_output_jpeg = JpegReconStage::kNone;
    710 #endif
    711 
    712  dec->events_wanted = dec->orig_events_wanted;
    713  dec->basic_info_size_hint = InitialBasicInfoSizeHint();
    714  dec->have_container = false;
    715  dec->box_count = 0;
    716  dec->downsampling_target = 8;
    717  dec->image_out_buffer_set = false;
    718  dec->image_out_buffer = nullptr;
    719  dec->image_out_init_callback = nullptr;
    720  dec->image_out_run_callback = nullptr;
    721  dec->image_out_destroy_callback = nullptr;
    722  dec->image_out_init_opaque = nullptr;
    723  dec->image_out_size = 0;
    724  dec->image_out_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT;
    725  dec->extra_channel_output.clear();
    726  dec->next_in = nullptr;
    727  dec->avail_in = 0;
    728  dec->input_closed = false;
    729 
    730  dec->passes_state.reset();
    731  dec->frame_dec.reset();
    732  dec->next_section = 0;
    733  dec->section_processed.clear();
    734 
    735  dec->ib.reset();
    736  dec->metadata = jxl::CodecMetadata();
    737  dec->image_metadata = dec->metadata.m;
    738  dec->frame_header = jxl::make_unique<jxl::FrameHeader>(&dec->metadata);
    739 
    740  dec->codestream_copy.clear();
    741  dec->codestream_unconsumed = 0;
    742  dec->codestream_pos = 0;
    743  dec->codestream_bits_ahead = 0;
    744 
    745  dec->frame_stage = FrameStage::kHeader;
    746  dec->remaining_frame_size = 0;
    747  dec->is_last_of_still = false;
    748  dec->is_last_total = false;
    749  dec->skip_frames = 0;
    750  dec->skipping_frame = false;
    751  dec->internal_frames = 0;
    752  dec->external_frames = 0;
    753 }
    754 
    755 void JxlDecoderReset(JxlDecoder* dec) {
    756  JxlDecoderRewindDecodingState(dec);
    757 
    758  dec->thread_pool.reset();
    759  dec->keep_orientation = false;
    760  dec->unpremul_alpha = false;
    761  dec->render_spotcolors = true;
    762  dec->coalescing = true;
    763  dec->desired_intensity_target = 0;
    764  dec->orig_events_wanted = 0;
    765  dec->events_wanted = 0;
    766  dec->frame_refs.clear();
    767  dec->frame_external_to_internal.clear();
    768  dec->frame_required.clear();
    769  dec->decompress_boxes = false;
    770 }
    771 
    772 JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
    773  JxlMemoryManager local_memory_manager;
    774  if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager))
    775    return nullptr;
    776 
    777  void* alloc =
    778      jxl::MemoryManagerAlloc(&local_memory_manager, sizeof(JxlDecoder));
    779  if (!alloc) return nullptr;
    780  // Placement new constructor on allocated memory
    781  JxlDecoder* dec = new (alloc) JxlDecoder();
    782  dec->memory_manager = local_memory_manager;
    783 
    784  JxlDecoderReset(dec);
    785 
    786  return dec;
    787 }
    788 
    789 void JxlDecoderDestroy(JxlDecoder* dec) {
    790  if (dec) {
    791    JxlMemoryManager local_memory_manager = dec->memory_manager;
    792    // Call destructor directly since custom free function is used.
    793    dec->~JxlDecoder();
    794    jxl::MemoryManagerFree(&local_memory_manager, dec);
    795  }
    796 }
    797 
    798 void JxlDecoderRewind(JxlDecoder* dec) { JxlDecoderRewindDecodingState(dec); }
    799 
    800 void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount) {
    801  // Increment amount, rather than set it: making the amount smaller is
    802  // impossible because the decoder may already have skipped frames required to
    803  // decode earlier frames, and making the amount larger compared to an existing
    804  // amount is impossible because if JxlDecoderSkipFrames is called in the
    805  // middle of already skipping frames, the user cannot know how many frames
    806  // have already been skipped internally so far so an absolute value cannot
    807  // be defined.
    808  dec->skip_frames += amount;
    809 
    810  dec->frame_required.clear();
    811  size_t next_frame = dec->external_frames + dec->skip_frames;
    812 
    813  // A frame that has been seen before a rewind
    814  if (next_frame < dec->frame_external_to_internal.size()) {
    815    size_t internal_index = dec->frame_external_to_internal[next_frame];
    816    if (internal_index < dec->frame_refs.size()) {
    817      std::vector<size_t> deps =
    818          GetFrameDependencies(internal_index, dec->frame_refs);
    819 
    820      dec->frame_required.resize(internal_index + 1, 0);
    821      for (size_t idx : deps) {
    822        if (idx < dec->frame_required.size()) {
    823          dec->frame_required[idx] = 1;
    824        } else {
    825          JXL_DEBUG_ABORT("Unreachable");
    826        }
    827      }
    828    }
    829  }
    830 }
    831 
    832 JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec) {
    833  if (dec->frame_stage != FrameStage::kFull) {
    834    return JXL_API_ERROR("JxlDecoderSkipCurrentFrame called at the wrong time");
    835  }
    836  JXL_DASSERT(dec->frame_dec);
    837  dec->frame_stage = FrameStage::kHeader;
    838  dec->AdvanceCodestream(dec->remaining_frame_size);
    839  if (dec->is_last_of_still) {
    840    dec->image_out_buffer_set = false;
    841  }
    842  return JXL_DEC_SUCCESS;
    843 }
    844 
    845 JXL_EXPORT JxlDecoderStatus
    846 JxlDecoderSetParallelRunner(JxlDecoder* dec, JxlParallelRunner parallel_runner,
    847                            void* parallel_runner_opaque) {
    848  if (dec->stage != DecoderStage::kInited) {
    849    return JXL_API_ERROR(
    850        "JxlDecoderSetParallelRunner must be called before starting");
    851  }
    852  dec->thread_pool = jxl::make_unique<jxl::ThreadPool>(parallel_runner,
    853                                                       parallel_runner_opaque);
    854  return JXL_DEC_SUCCESS;
    855 }
    856 
    857 size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec) {
    858  if (dec->got_basic_info) return 0;
    859  return dec->basic_info_size_hint;
    860 }
    861 
    862 JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, int events_wanted) {
    863  if (dec->stage != DecoderStage::kInited) {
    864    return JXL_DEC_ERROR;  // Cannot subscribe to events after having started.
    865  }
    866  if (events_wanted & 63) {
    867    return JXL_DEC_ERROR;  // Can only subscribe to informative events.
    868  }
    869  dec->events_wanted = events_wanted;
    870  dec->orig_events_wanted = events_wanted;
    871  return JXL_DEC_SUCCESS;
    872 }
    873 
    874 JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
    875                                              JXL_BOOL skip_reorientation) {
    876  if (dec->stage != DecoderStage::kInited) {
    877    return JXL_API_ERROR("Must set keep_orientation option before starting");
    878  }
    879  dec->keep_orientation = FROM_JXL_BOOL(skip_reorientation);
    880  return JXL_DEC_SUCCESS;
    881 }
    882 
    883 JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec,
    884                                                 JXL_BOOL unpremul_alpha) {
    885  if (dec->stage != DecoderStage::kInited) {
    886    return JXL_API_ERROR("Must set unpremul_alpha option before starting");
    887  }
    888  dec->unpremul_alpha = FROM_JXL_BOOL(unpremul_alpha);
    889  return JXL_DEC_SUCCESS;
    890 }
    891 
    892 JxlDecoderStatus JxlDecoderSetRenderSpotcolors(JxlDecoder* dec,
    893                                               JXL_BOOL render_spotcolors) {
    894  if (dec->stage != DecoderStage::kInited) {
    895    return JXL_API_ERROR("Must set render_spotcolors option before starting");
    896  }
    897  dec->render_spotcolors = FROM_JXL_BOOL(render_spotcolors);
    898  return JXL_DEC_SUCCESS;
    899 }
    900 
    901 JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, JXL_BOOL coalescing) {
    902  if (dec->stage != DecoderStage::kInited) {
    903    return JXL_API_ERROR("Must set coalescing option before starting");
    904  }
    905  dec->coalescing = FROM_JXL_BOOL(coalescing);
    906  return JXL_DEC_SUCCESS;
    907 }
    908 
    909 namespace {
    910 // helper function to get the dimensions of the current image buffer
    911 void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize) {
    912  if (dec->frame_header->nonserialized_is_preview) {
    913    xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation);
    914    ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation);
    915    return;
    916  }
    917  xsize = dec->metadata.oriented_xsize(dec->keep_orientation);
    918  ysize = dec->metadata.oriented_ysize(dec->keep_orientation);
    919  if (!dec->coalescing) {
    920    const auto frame_dim = dec->frame_header->ToFrameDimensions();
    921    xsize = frame_dim.xsize_upsampled;
    922    ysize = frame_dim.ysize_upsampled;
    923    if (!dec->keep_orientation &&
    924        static_cast<int>(dec->metadata.m.GetOrientation()) > 4) {
    925      std::swap(xsize, ysize);
    926    }
    927  }
    928 }
    929 }  // namespace
    930 
    931 namespace jxl {
    932 namespace {
    933 
    934 // Returns JXL_DEC_SUCCESS if the full bundle was successfully read, status
    935 // indicating either error or need more input otherwise.
    936 template <class T>
    937 JxlDecoderStatus ReadBundle(JxlDecoder* dec, Span<const uint8_t> data,
    938                            BitReader* reader, T* JXL_RESTRICT t) {
    939  // Use a copy of the bit reader because CanRead advances bits.
    940  BitReader reader2(data);
    941  reader2.SkipBits(reader->TotalBitsConsumed());
    942  bool can_read = Bundle::CanRead(&reader2, t);
    943  JXL_API_RETURN_IF_ERROR(reader2.Close());
    944 
    945  if (!can_read) {
    946    return dec->RequestMoreInput();
    947  }
    948  if (!Bundle::Read(reader, t)) {
    949    return JXL_DEC_ERROR;
    950  }
    951  return JXL_DEC_SUCCESS;
    952 }
    953 
    954 std::unique_ptr<BitReader, std::function<void(BitReader*)>> GetBitReader(
    955    Span<const uint8_t> span) {
    956  BitReader* reader = new BitReader(span);
    957  return std::unique_ptr<BitReader, std::function<void(BitReader*)>>(
    958      reader, [](BitReader* reader) {
    959        // We can't allow Close to abort the program if the reader is out of
    960        // bounds, or all return paths in the code, even those that already
    961        // return failure, would have to manually call AllReadsWithinBounds().
    962        // Invalid JXL codestream should not cause program to quit.
    963        (void)reader->AllReadsWithinBounds();
    964        (void)reader->Close();
    965        delete reader;
    966      });
    967 }
    968 
    969 JxlDecoderStatus JxlDecoderReadBasicInfo(JxlDecoder* dec) {
    970  if (!dec->got_codestream_signature) {
    971    // Check and skip the codestream signature
    972    Span<const uint8_t> span;
    973    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
    974    if (span.size() < 2) {
    975      return dec->RequestMoreInput();
    976    }
    977    if (span.data()[0] != 0xff || span.data()[1] != jxl::kCodestreamMarker) {
    978      return JXL_INPUT_ERROR("invalid signature");
    979    }
    980    dec->got_codestream_signature = true;
    981    dec->AdvanceCodestream(2);
    982  }
    983 
    984  Span<const uint8_t> span;
    985  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
    986  auto reader = GetBitReader(span);
    987  JXL_API_RETURN_IF_ERROR(
    988      ReadBundle(dec, span, reader.get(), &dec->metadata.size));
    989  JXL_API_RETURN_IF_ERROR(
    990      ReadBundle(dec, span, reader.get(), &dec->metadata.m));
    991  size_t total_bits = reader->TotalBitsConsumed();
    992  dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
    993  dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
    994  dec->got_basic_info = true;
    995  dec->basic_info_size_hint = 0;
    996  dec->image_metadata = dec->metadata.m;
    997  JXL_DEBUG_V(2, "Decoded BasicInfo: %s", dec->metadata.DebugString().c_str());
    998 
    999  if (!CheckSizeLimit(dec, dec->metadata.size.xsize(),
   1000                      dec->metadata.size.ysize())) {
   1001    return JXL_INPUT_ERROR("image is too large");
   1002  }
   1003 
   1004  return JXL_DEC_SUCCESS;
   1005 }
   1006 
   1007 // Reads all codestream headers (but not frame headers)
   1008 JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) {
   1009  if (!dec->got_transform_data) {
   1010    Span<const uint8_t> span;
   1011    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
   1012    auto reader = GetBitReader(span);
   1013    reader->SkipBits(dec->codestream_bits_ahead);
   1014    dec->metadata.transform_data.nonserialized_xyb_encoded =
   1015        dec->metadata.m.xyb_encoded;
   1016    JXL_API_RETURN_IF_ERROR(
   1017        ReadBundle(dec, span, reader.get(), &dec->metadata.transform_data));
   1018    size_t total_bits = reader->TotalBitsConsumed();
   1019    dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
   1020    dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
   1021    dec->got_transform_data = true;
   1022  }
   1023 
   1024  Span<const uint8_t> span;
   1025  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
   1026  auto reader = GetBitReader(span);
   1027  reader->SkipBits(dec->codestream_bits_ahead);
   1028 
   1029  if (dec->metadata.m.color_encoding.WantICC()) {
   1030    jxl::Status status = dec->icc_reader->Init(reader.get());
   1031    // Always check AllReadsWithinBounds, not all the C++ decoder implementation
   1032    // handles reader out of bounds correctly  yet (e.g. context map). Not
   1033    // checking AllReadsWithinBounds can cause reader->Close() to trigger an
   1034    // assert, but we don't want library to quit program for invalid codestream.
   1035    if (!reader->AllReadsWithinBounds() ||
   1036        status.code() == StatusCode::kNotEnoughBytes) {
   1037      return dec->RequestMoreInput();
   1038    }
   1039    if (!status) {
   1040      // Other non-successful status is an error
   1041      return JXL_DEC_ERROR;
   1042    }
   1043    PaddedBytes decoded_icc{&dec->memory_manager};
   1044    status = dec->icc_reader->Process(reader.get(), &decoded_icc);
   1045    if (status.code() == StatusCode::kNotEnoughBytes) {
   1046      return dec->RequestMoreInput();
   1047    }
   1048    if (!status) {
   1049      // Other non-successful status is an error
   1050      return JXL_DEC_ERROR;
   1051    }
   1052    if (decoded_icc.empty()) {
   1053      return JXL_DEC_ERROR;
   1054    }
   1055    IccBytes icc;
   1056    Bytes(decoded_icc).AppendTo(icc);
   1057    dec->metadata.m.color_encoding.SetICCRaw(std::move(icc));
   1058  }
   1059 
   1060  dec->got_all_headers = true;
   1061  JXL_API_RETURN_IF_ERROR(reader->JumpToByteBoundary());
   1062 
   1063  dec->AdvanceCodestream(reader->TotalBitsConsumed() / jxl::kBitsPerByte);
   1064  dec->codestream_bits_ahead = 0;
   1065 
   1066  if (!dec->passes_state) {
   1067    dec->passes_state =
   1068        jxl::make_unique<jxl::PassesDecoderState>(&dec->memory_manager);
   1069  }
   1070 
   1071  JXL_API_RETURN_IF_ERROR(
   1072      dec->passes_state->output_encoding_info.SetFromMetadata(dec->metadata));
   1073  if (dec->desired_intensity_target > 0) {
   1074    dec->passes_state->output_encoding_info.desired_intensity_target =
   1075        dec->desired_intensity_target;
   1076  }
   1077  dec->image_metadata = dec->metadata.m;
   1078 
   1079  return JXL_DEC_SUCCESS;
   1080 }
   1081 
   1082 JxlDecoderStatus JxlDecoderProcessSections(JxlDecoder* dec) {
   1083  Span<const uint8_t> span;
   1084  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
   1085  const auto& toc = dec->frame_dec->Toc();
   1086  size_t pos = 0;
   1087  std::vector<jxl::FrameDecoder::SectionInfo> section_info;
   1088  std::vector<jxl::FrameDecoder::SectionStatus> section_status;
   1089  for (size_t i = dec->next_section; i < toc.size(); ++i) {
   1090    if (dec->section_processed[i]) {
   1091      pos += toc[i].size;
   1092      continue;
   1093    }
   1094    size_t id = toc[i].id;
   1095    size_t size = toc[i].size;
   1096    if (OutOfBounds(pos, size, span.size())) {
   1097      break;
   1098    }
   1099    auto* br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size));
   1100    section_info.emplace_back(jxl::FrameDecoder::SectionInfo{br, id, i});
   1101    section_status.emplace_back();
   1102    pos += size;
   1103  }
   1104  jxl::Status status = dec->frame_dec->ProcessSections(
   1105      section_info.data(), section_info.size(), section_status.data());
   1106  bool out_of_bounds = false;
   1107  bool has_error = false;
   1108  for (const auto& info : section_info) {
   1109    if (!info.br->AllReadsWithinBounds()) {
   1110      // Mark out of bounds section, but keep closing and deleting the next
   1111      // ones as well.
   1112      out_of_bounds = true;
   1113    }
   1114    if (!info.br->Close()) has_error = true;
   1115    delete info.br;
   1116  }
   1117  if (has_error) {
   1118    return JXL_INPUT_ERROR("internal: bit-reader failed to close");
   1119  }
   1120  if (out_of_bounds) {
   1121    // If any bit reader indicates out of bounds, it's an error, not just
   1122    // needing more input, since we ensure only bit readers containing
   1123    // a complete section are provided to the FrameDecoder.
   1124    return JXL_INPUT_ERROR("frame out of bounds");
   1125  }
   1126  if (!status) {
   1127    return JXL_INPUT_ERROR("frame processing failed");
   1128  }
   1129  for (size_t i = 0; i < section_status.size(); ++i) {
   1130    auto status = section_status[i];
   1131    if (status == jxl::FrameDecoder::kDone) {
   1132      dec->section_processed[section_info[i].index] = 1;
   1133    } else if (status != jxl::FrameDecoder::kSkipped) {
   1134      return JXL_INPUT_ERROR("unexpected section status");
   1135    }
   1136  }
   1137  size_t completed_prefix_bytes = 0;
   1138  while (dec->next_section < dec->section_processed.size() &&
   1139         dec->section_processed[dec->next_section] == 1) {
   1140    completed_prefix_bytes += toc[dec->next_section].size;
   1141    ++dec->next_section;
   1142  }
   1143  dec->remaining_frame_size -= completed_prefix_bytes;
   1144  dec->AdvanceCodestream(completed_prefix_bytes);
   1145  return JXL_DEC_SUCCESS;
   1146 }
   1147 
   1148 // TODO(eustas): no CodecInOut -> no image size reinforcement -> possible OOM.
   1149 JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
   1150  // If no parallel runner is set, use the default
   1151  // TODO(lode): move this initialization to an appropriate location once the
   1152  // runner is used to decode pixels.
   1153  if (!dec->thread_pool) {
   1154    dec->thread_pool = jxl::make_unique<jxl::ThreadPool>(nullptr, nullptr);
   1155  }
   1156 
   1157  // No matter what events are wanted, the basic info is always required.
   1158  if (!dec->got_basic_info) {
   1159    JxlDecoderStatus status = JxlDecoderReadBasicInfo(dec);
   1160    if (status != JXL_DEC_SUCCESS) return status;
   1161  }
   1162 
   1163  if (dec->events_wanted & JXL_DEC_BASIC_INFO) {
   1164    dec->events_wanted &= ~JXL_DEC_BASIC_INFO;
   1165    return JXL_DEC_BASIC_INFO;
   1166  }
   1167 
   1168  if (!dec->events_wanted) {
   1169    dec->stage = DecoderStage::kCodestreamFinished;
   1170    return JXL_DEC_SUCCESS;
   1171  }
   1172 
   1173  if (!dec->icc_reader) {
   1174    dec->icc_reader = jxl::make_unique<ICCReader>(&dec->memory_manager);
   1175  }
   1176 
   1177  if (!dec->got_all_headers) {
   1178    JxlDecoderStatus status = JxlDecoderReadAllHeaders(dec);
   1179    if (status != JXL_DEC_SUCCESS) return status;
   1180  }
   1181 
   1182  if (dec->events_wanted & JXL_DEC_COLOR_ENCODING) {
   1183    dec->events_wanted &= ~JXL_DEC_COLOR_ENCODING;
   1184    return JXL_DEC_COLOR_ENCODING;
   1185  }
   1186 
   1187  if (!dec->events_wanted) {
   1188    dec->stage = DecoderStage::kCodestreamFinished;
   1189    return JXL_DEC_SUCCESS;
   1190  }
   1191 
   1192  dec->post_headers = true;
   1193 
   1194  if (!dec->got_preview_image && dec->metadata.m.have_preview) {
   1195    dec->preview_frame = true;
   1196  }
   1197 
   1198  // Handle frames
   1199  for (;;) {
   1200    bool parse_frames =
   1201        (dec->events_wanted &
   1202         (JXL_DEC_PREVIEW_IMAGE | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   1203    if (!parse_frames) {
   1204      break;
   1205    }
   1206    if (dec->frame_stage == FrameStage::kHeader && dec->is_last_total) {
   1207      break;
   1208    }
   1209    if (dec->frame_stage == FrameStage::kHeader) {
   1210 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1211      if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata ||
   1212          dec->recon_output_jpeg == JpegReconStage::kOutputting) {
   1213        // The image bundle contains the JPEG reconstruction frame, but the
   1214        // decoder is still waiting to decode an EXIF or XMP box. It's not
   1215        // implemented to decode additional frames during this, and a JPEG
   1216        // reconstruction image should have only one frame.
   1217        return JXL_API_ERROR(
   1218            "cannot decode a next frame after JPEG reconstruction frame");
   1219      }
   1220 #endif
   1221      if (!dec->ib) {
   1222        dec->ib = jxl::make_unique<jxl::ImageBundle>(&dec->memory_manager,
   1223                                                     &dec->image_metadata);
   1224      }
   1225 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1226      // If JPEG reconstruction is wanted and possible, set the jpeg_data of
   1227      // the ImageBundle.
   1228      if (!dec->jpeg_decoder.SetImageBundleJpegData(dec->ib.get()))
   1229        return JXL_DEC_ERROR;
   1230 #endif
   1231      dec->frame_dec = jxl::make_unique<FrameDecoder>(
   1232          dec->passes_state.get(), dec->metadata, dec->thread_pool.get(),
   1233          /*use_slow_rendering_pipeline=*/false);
   1234      dec->frame_header = jxl::make_unique<FrameHeader>(&dec->metadata);
   1235      Span<const uint8_t> span;
   1236      JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
   1237      auto reader = GetBitReader(span);
   1238      jxl::Status status = dec->frame_dec->InitFrame(
   1239          reader.get(), dec->ib.get(), dec->preview_frame);
   1240      if (!reader->AllReadsWithinBounds() ||
   1241          status.code() == StatusCode::kNotEnoughBytes) {
   1242        return dec->RequestMoreInput();
   1243      } else if (!status) {
   1244        return JXL_INPUT_ERROR("invalid frame header");
   1245      }
   1246      dec->AdvanceCodestream(reader->TotalBitsConsumed() / kBitsPerByte);
   1247      *dec->frame_header = dec->frame_dec->GetFrameHeader();
   1248      jxl::FrameDimensions frame_dim = dec->frame_header->ToFrameDimensions();
   1249      if (!CheckSizeLimit(dec, frame_dim.xsize_upsampled_padded,
   1250                          frame_dim.ysize_upsampled_padded)) {
   1251        return JXL_INPUT_ERROR("frame is too large");
   1252      }
   1253      int output_type =
   1254          dec->preview_frame ? JXL_DEC_PREVIEW_IMAGE : JXL_DEC_FULL_IMAGE;
   1255      bool output_needed = ((dec->events_wanted & output_type) != 0);
   1256      if (output_needed) {
   1257        JXL_API_RETURN_IF_ERROR(dec->frame_dec->InitFrameOutput());
   1258      }
   1259      dec->remaining_frame_size = dec->frame_dec->SumSectionSizes();
   1260 
   1261      dec->frame_stage = FrameStage::kTOC;
   1262      if (dec->preview_frame) {
   1263        if (!(dec->events_wanted & JXL_DEC_PREVIEW_IMAGE)) {
   1264          dec->frame_stage = FrameStage::kHeader;
   1265          dec->AdvanceCodestream(dec->remaining_frame_size);
   1266          dec->got_preview_image = true;
   1267          dec->preview_frame = false;
   1268        }
   1269        continue;
   1270      }
   1271 
   1272      int saved_as = FrameDecoder::SavedAs(*dec->frame_header);
   1273      // is last in entire codestream
   1274      dec->is_last_total = dec->frame_header->is_last;
   1275      // is last of current still
   1276      dec->is_last_of_still =
   1277          dec->is_last_total || dec->frame_header->animation_frame.duration > 0;
   1278      // is kRegularFrame and coalescing is disabled
   1279      dec->is_last_of_still |=
   1280          (!dec->coalescing &&
   1281           dec->frame_header->frame_type == FrameType::kRegularFrame);
   1282      const size_t internal_frame_index = dec->internal_frames;
   1283      const size_t external_frame_index = dec->external_frames;
   1284      if (dec->is_last_of_still) dec->external_frames++;
   1285      dec->internal_frames++;
   1286 
   1287      if (dec->skip_frames > 0) {
   1288        dec->skipping_frame = true;
   1289        if (dec->is_last_of_still) {
   1290          dec->skip_frames--;
   1291        }
   1292      } else {
   1293        dec->skipping_frame = false;
   1294      }
   1295 
   1296      if (external_frame_index >= dec->frame_external_to_internal.size()) {
   1297        dec->frame_external_to_internal.push_back(internal_frame_index);
   1298        if (dec->frame_external_to_internal.size() !=
   1299            external_frame_index + 1) {
   1300          return JXL_API_ERROR("internal");
   1301        }
   1302      }
   1303 
   1304      if (internal_frame_index >= dec->frame_refs.size()) {
   1305        // add the value 0xff (which means all references) to new slots: we only
   1306        // know the references of the frame at FinalizeFrame, and fill in the
   1307        // correct values there. As long as this information is not known, the
   1308        // worst case where the frame depends on all storage slots is assumed.
   1309        dec->frame_refs.emplace_back(FrameRef{0xFF, saved_as});
   1310        if (dec->frame_refs.size() != internal_frame_index + 1) {
   1311          return JXL_API_ERROR("internal");
   1312        }
   1313      }
   1314 
   1315      if (dec->skipping_frame) {
   1316        // Whether this frame could be referenced by any future frame: either
   1317        // because it's a frame saved for blending or patches, or because it's
   1318        // a DC frame.
   1319        bool referenceable =
   1320            dec->frame_header->CanBeReferenced() ||
   1321            dec->frame_header->frame_type == FrameType::kDCFrame;
   1322        if (internal_frame_index < dec->frame_required.size() &&
   1323            !dec->frame_required[internal_frame_index]) {
   1324          referenceable = false;
   1325        }
   1326        if (!referenceable) {
   1327          // Skip all decoding for this frame, since the user is skipping this
   1328          // frame and no future frames can reference it.
   1329          dec->frame_stage = FrameStage::kHeader;
   1330          dec->AdvanceCodestream(dec->remaining_frame_size);
   1331          continue;
   1332        }
   1333      }
   1334 
   1335      if ((dec->events_wanted & JXL_DEC_FRAME) && dec->is_last_of_still) {
   1336        // Only return this for the last of a series of stills: patches frames
   1337        // etc... before this one do not contain the correct information such
   1338        // as animation timing, ...
   1339        if (!dec->skipping_frame) {
   1340          return JXL_DEC_FRAME;
   1341        }
   1342      }
   1343    }
   1344 
   1345    if (dec->frame_stage == FrameStage::kTOC) {
   1346      dec->frame_dec->SetRenderSpotcolors(dec->render_spotcolors);
   1347      dec->frame_dec->SetCoalescing(dec->coalescing);
   1348 
   1349      if (!dec->preview_frame &&
   1350          (dec->events_wanted & JXL_DEC_FRAME_PROGRESSION)) {
   1351        dec->frame_prog_detail =
   1352            dec->frame_dec->SetPauseAtProgressive(dec->prog_detail);
   1353      } else {
   1354        dec->frame_prog_detail = JxlProgressiveDetail::kFrames;
   1355      }
   1356      dec->dc_frame_progression_done = false;
   1357 
   1358      dec->next_section = 0;
   1359      dec->section_processed.clear();
   1360      dec->section_processed.resize(dec->frame_dec->Toc().size(), 0);
   1361 
   1362      // If we don't need pixels, we can skip actually decoding the frames.
   1363      if (dec->preview_frame || (dec->events_wanted & JXL_DEC_FULL_IMAGE)) {
   1364        dec->frame_stage = FrameStage::kFull;
   1365      } else if (!dec->is_last_total) {
   1366        dec->frame_stage = FrameStage::kHeader;
   1367        dec->AdvanceCodestream(dec->remaining_frame_size);
   1368        continue;
   1369      } else {
   1370        break;
   1371      }
   1372    }
   1373 
   1374    if (dec->frame_stage == FrameStage::kFull) {
   1375      if (!dec->image_out_buffer_set) {
   1376        if (dec->preview_frame) {
   1377          return JXL_DEC_NEED_PREVIEW_OUT_BUFFER;
   1378        }
   1379        if (
   1380 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1381            (!dec->jpeg_decoder.IsOutputSet() ||
   1382             dec->ib->jpeg_data == nullptr) &&
   1383 #endif
   1384            dec->is_last_of_still && !dec->skipping_frame) {
   1385          // TODO(lode): remove the dec->is_last_of_still condition if the
   1386          // frame decoder needs the image buffer as working space for decoding
   1387          // non-visible or blending frames too
   1388          return JXL_DEC_NEED_IMAGE_OUT_BUFFER;
   1389        }
   1390      }
   1391 
   1392      if (dec->image_out_buffer_set) {
   1393        size_t xsize;
   1394        size_t ysize;
   1395        GetCurrentDimensions(dec, xsize, ysize);
   1396        size_t bits_per_sample = GetBitDepth(
   1397            dec->image_out_bit_depth, dec->metadata.m, dec->image_out_format);
   1398        dec->frame_dec->SetImageOutput(
   1399            PixelCallback{
   1400                dec->image_out_init_callback, dec->image_out_run_callback,
   1401                dec->image_out_destroy_callback, dec->image_out_init_opaque},
   1402            reinterpret_cast<uint8_t*>(dec->image_out_buffer),
   1403            dec->image_out_size, xsize, ysize, dec->image_out_format,
   1404            bits_per_sample, dec->unpremul_alpha, !dec->keep_orientation);
   1405        for (size_t i = 0; i < dec->extra_channel_output.size(); ++i) {
   1406          const auto& extra = dec->extra_channel_output[i];
   1407          size_t ec_bits_per_sample =
   1408              GetBitDepth(dec->image_out_bit_depth,
   1409                          dec->metadata.m.extra_channel_info[i], extra.format);
   1410          dec->frame_dec->AddExtraChannelOutput(extra.buffer, extra.buffer_size,
   1411                                                xsize, extra.format,
   1412                                                ec_bits_per_sample);
   1413        }
   1414      }
   1415 
   1416      size_t next_num_passes_to_pause = dec->frame_dec->NextNumPassesToPause();
   1417 
   1418      JXL_API_RETURN_IF_ERROR(JxlDecoderProcessSections(dec));
   1419 
   1420      bool all_sections_done = dec->frame_dec->HasDecodedAll();
   1421      bool got_dc_only = !all_sections_done && dec->frame_dec->HasDecodedDC();
   1422 
   1423      if (dec->frame_prog_detail >= JxlProgressiveDetail::kDC &&
   1424          !dec->dc_frame_progression_done && got_dc_only) {
   1425        dec->dc_frame_progression_done = true;
   1426        dec->downsampling_target = 8;
   1427        return JXL_DEC_FRAME_PROGRESSION;
   1428      }
   1429 
   1430      bool new_progression_step_done =
   1431          dec->frame_dec->NumCompletePasses() >= next_num_passes_to_pause;
   1432 
   1433      if (!all_sections_done &&
   1434          dec->frame_prog_detail >= JxlProgressiveDetail::kLastPasses &&
   1435          new_progression_step_done) {
   1436        dec->downsampling_target =
   1437            dec->frame_header->passes.GetDownsamplingTargetForCompletedPasses(
   1438                dec->frame_dec->NumCompletePasses());
   1439        return JXL_DEC_FRAME_PROGRESSION;
   1440      }
   1441 
   1442      if (!all_sections_done) {
   1443        // Not all sections have been processed yet
   1444        return dec->RequestMoreInput();
   1445      }
   1446 
   1447      if (!dec->preview_frame) {
   1448        size_t internal_index = dec->internal_frames - 1;
   1449        if (dec->frame_refs.size() <= internal_index) {
   1450          return JXL_API_ERROR("internal");
   1451        }
   1452        // Always fill this in, even if it was already written, it could be that
   1453        // this frame was skipped before and set to 255, while only now we know
   1454        // the true value.
   1455        dec->frame_refs[internal_index].reference =
   1456            dec->frame_dec->References();
   1457      }
   1458 
   1459      if (!dec->frame_dec->FinalizeFrame()) {
   1460        return JXL_INPUT_ERROR("decoding frame failed");
   1461      }
   1462 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1463      // If jpeg output was requested, we merely return the JXL_DEC_FULL_IMAGE
   1464      // status without outputting pixels.
   1465      if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
   1466        dec->frame_stage = FrameStage::kHeader;
   1467        dec->recon_output_jpeg = JpegReconStage::kSettingMetadata;
   1468        return JXL_DEC_FULL_IMAGE;
   1469      }
   1470 #endif
   1471      if (dec->preview_frame || dec->is_last_of_still) {
   1472        dec->image_out_buffer_set = false;
   1473        dec->extra_channel_output.clear();
   1474      }
   1475    }
   1476 
   1477    dec->frame_stage = FrameStage::kHeader;
   1478 
   1479    // The pixels have been output or are not needed, do not keep them in
   1480    // memory here.
   1481    dec->ib.reset();
   1482    if (dec->preview_frame) {
   1483      dec->got_preview_image = true;
   1484      dec->preview_frame = false;
   1485      dec->events_wanted &= ~JXL_DEC_PREVIEW_IMAGE;
   1486      return JXL_DEC_PREVIEW_IMAGE;
   1487    } else if (dec->is_last_of_still &&
   1488               (dec->events_wanted & JXL_DEC_FULL_IMAGE) &&
   1489               !dec->skipping_frame) {
   1490      return JXL_DEC_FULL_IMAGE;
   1491    }
   1492  }
   1493 
   1494  dec->stage = DecoderStage::kCodestreamFinished;
   1495  // Return success, this means there is nothing more to do.
   1496  return JXL_DEC_SUCCESS;
   1497 }
   1498 
   1499 }  // namespace
   1500 }  // namespace jxl
   1501 
   1502 JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data,
   1503                                    size_t size) {
   1504  if (dec->next_in) {
   1505    return JXL_API_ERROR("already set input, use JxlDecoderReleaseInput first");
   1506  }
   1507  if (dec->input_closed) {
   1508    return JXL_API_ERROR("input already closed");
   1509  }
   1510 
   1511  dec->next_in = data;
   1512  dec->avail_in = size;
   1513  return JXL_DEC_SUCCESS;
   1514 }
   1515 
   1516 size_t JxlDecoderReleaseInput(JxlDecoder* dec) {
   1517  size_t result = dec->avail_in;
   1518  dec->next_in = nullptr;
   1519  dec->avail_in = 0;
   1520  return result;
   1521 }
   1522 
   1523 void JxlDecoderCloseInput(JxlDecoder* dec) { dec->input_closed = true; }
   1524 
   1525 JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
   1526                                         size_t size) {
   1527 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1528  // JPEG reconstruction buffer can only set and updated before or during the
   1529  // first frame, the reconstruction box refers to the first frame and in
   1530  // theory multi-frame images should not be used with a jbrd box.
   1531  if (dec->internal_frames > 1) {
   1532    return JXL_API_ERROR("JPEG reconstruction only works for the first frame");
   1533  }
   1534  if (dec->jpeg_decoder.IsOutputSet()) {
   1535    return JXL_API_ERROR("Already set JPEG buffer");
   1536  }
   1537  return dec->jpeg_decoder.SetOutputBuffer(data, size);
   1538 #else
   1539  return JXL_API_ERROR("JPEG reconstruction is not supported.");
   1540 #endif
   1541 }
   1542 
   1543 size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec) {
   1544 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1545  return dec->jpeg_decoder.ReleaseOutputBuffer();
   1546 #else
   1547  return JXL_API_ERROR("JPEG reconstruction is not supported.");
   1548 #endif
   1549 }
   1550 
   1551 // Parses the header of the box, outputting the 4-character type and the box
   1552 // size, including header size, as stored in the box header.
   1553 // @param in current input bytes.
   1554 // @param size available input size.
   1555 // @param pos position in the input, must begin at the header of the box.
   1556 // @param file_pos position of pos since the start of the JXL file, rather than
   1557 // the current input, used for integer overflow checking.
   1558 // @param type the output box type.
   1559 // @param box_size output the total box size, including header, in bytes, or 0
   1560 // if it's a final unbounded box.
   1561 // @param header_size output size of the box header.
   1562 // @return JXL_DEC_SUCCESS if the box header was fully parsed. In that case the
   1563 // parsing position must be incremented by header_size bytes.
   1564 // JXL_DEC_NEED_MORE_INPUT if not enough input bytes available, in that case
   1565 // header_size indicates a lower bound for the known size the header has to be
   1566 // at least. JXL_DEC_ERROR if the box header is invalid.
   1567 static JxlDecoderStatus ParseBoxHeader(const uint8_t* in, size_t size,
   1568                                       size_t pos, size_t file_pos,
   1569                                       JxlBoxType type, uint64_t* box_size,
   1570                                       uint64_t* header_size) {
   1571  if (OutOfBounds(pos, 8, size)) {
   1572    *header_size = 8;
   1573    return JXL_DEC_NEED_MORE_INPUT;
   1574  }
   1575  size_t box_start = pos;
   1576  // Box size, including this header itself.
   1577  *box_size = LoadBE32(in + pos);
   1578  pos += 4;
   1579  memcpy(type, in + pos, 4);
   1580  pos += 4;
   1581  if (*box_size == 1) {
   1582    *header_size = 16;
   1583    if (OutOfBounds(pos, 8, size)) return JXL_DEC_NEED_MORE_INPUT;
   1584    *box_size = LoadBE64(in + pos);
   1585    pos += 8;
   1586  }
   1587  *header_size = pos - box_start;
   1588  if (*box_size > 0 && *box_size < *header_size) {
   1589    return JXL_INPUT_ERROR("invalid box size");
   1590  }
   1591  if (file_pos + *box_size < file_pos) {
   1592    return JXL_INPUT_ERROR("Box size overflow");
   1593  }
   1594  return JXL_DEC_SUCCESS;
   1595 }
   1596 
   1597 // This includes handling the codestream if it is not a box-based jxl file.
   1598 static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
   1599  // Box handling loop
   1600  for (;;) {
   1601    if (dec->box_stage != BoxStage::kHeader) {
   1602      dec->AdvanceInput(dec->header_size);
   1603      dec->header_size = 0;
   1604 #if JPEGXL_ENABLE_BOXES
   1605      if ((dec->events_wanted & JXL_DEC_BOX) &&
   1606          dec->box_out_buffer_set_current_box) {
   1607        uint8_t* next_out = dec->box_out_buffer + dec->box_out_buffer_pos;
   1608        size_t avail_out = dec->box_out_buffer_size - dec->box_out_buffer_pos;
   1609 
   1610        JxlDecoderStatus box_result = dec->box_content_decoder.Process(
   1611            dec->next_in, dec->avail_in,
   1612            dec->file_pos - dec->box_contents_begin, &next_out, &avail_out);
   1613        size_t produced =
   1614            next_out - (dec->box_out_buffer + dec->box_out_buffer_pos);
   1615        dec->box_out_buffer_pos += produced;
   1616 
   1617        if (box_result == JXL_DEC_BOX_COMPLETE &&
   1618            !(dec->events_wanted & JXL_DEC_BOX_COMPLETE)) {
   1619          box_result = JXL_DEC_SUCCESS;
   1620        }
   1621 
   1622        // Don't return JXL_DEC_NEED_MORE_INPUT: the box stages below, instead,
   1623        // handle the input progression, and the above only outputs the part of
   1624        // the box seen so far.
   1625        if (box_result != JXL_DEC_SUCCESS &&
   1626            box_result != JXL_DEC_NEED_MORE_INPUT) {
   1627          return box_result;
   1628        }
   1629      }
   1630 #endif
   1631 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1632      if (dec->store_exif == 1 || dec->store_xmp == 1) {
   1633        std::vector<uint8_t>& metadata =
   1634            (dec->store_exif == 1) ? dec->exif_metadata : dec->xmp_metadata;
   1635        for (;;) {
   1636          if (metadata.empty()) metadata.resize(64);
   1637          uint8_t* orig_next_out = metadata.data() + dec->recon_out_buffer_pos;
   1638          uint8_t* next_out = orig_next_out;
   1639          size_t avail_out = metadata.size() - dec->recon_out_buffer_pos;
   1640          JxlDecoderStatus box_result = dec->metadata_decoder.Process(
   1641              dec->next_in, dec->avail_in,
   1642              dec->file_pos - dec->box_contents_begin, &next_out, &avail_out);
   1643          size_t produced = next_out - orig_next_out;
   1644          dec->recon_out_buffer_pos += produced;
   1645          if (box_result == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
   1646            metadata.resize(metadata.size() * 2);
   1647          } else if (box_result == JXL_DEC_NEED_MORE_INPUT) {
   1648            break;  // box stage handling below will handle this instead
   1649          } else if (box_result == JXL_DEC_BOX_COMPLETE) {
   1650            size_t needed_size = (dec->store_exif == 1) ? dec->recon_exif_size
   1651                                                        : dec->recon_xmp_size;
   1652            if (dec->box_contents_unbounded &&
   1653                dec->recon_out_buffer_pos < needed_size) {
   1654              // Unbounded box, but we know the expected size due to the jbrd
   1655              // box's data. Treat this as the JXL_DEC_NEED_MORE_INPUT case.
   1656              break;
   1657            } else {
   1658              metadata.resize(dec->recon_out_buffer_pos);
   1659              if (dec->store_exif == 1) dec->store_exif = 2;
   1660              if (dec->store_xmp == 1) dec->store_xmp = 2;
   1661              break;
   1662            }
   1663          } else {
   1664            // error
   1665            return box_result;
   1666          }
   1667        }
   1668      }
   1669 #endif
   1670    }
   1671 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1672    if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata &&
   1673        !dec->JbrdNeedMoreBoxes()) {
   1674      jxl::jpeg::JPEGData* jpeg_data = dec->ib->jpeg_data.get();
   1675      if (dec->recon_exif_size) {
   1676        JxlDecoderStatus status = jxl::JxlToJpegDecoder::SetExif(
   1677            dec->exif_metadata.data(), dec->exif_metadata.size(), jpeg_data);
   1678        if (status != JXL_DEC_SUCCESS) return status;
   1679      }
   1680      if (dec->recon_xmp_size) {
   1681        JxlDecoderStatus status = jxl::JxlToJpegDecoder::SetXmp(
   1682            dec->xmp_metadata.data(), dec->xmp_metadata.size(), jpeg_data);
   1683        if (status != JXL_DEC_SUCCESS) return status;
   1684      }
   1685      dec->recon_output_jpeg = JpegReconStage::kOutputting;
   1686    }
   1687 
   1688    if (dec->recon_output_jpeg == JpegReconStage::kOutputting &&
   1689        !dec->JbrdNeedMoreBoxes()) {
   1690      JxlDecoderStatus status =
   1691          dec->jpeg_decoder.WriteOutput(*dec->ib->jpeg_data);
   1692      if (status != JXL_DEC_SUCCESS) return status;
   1693      dec->recon_output_jpeg = JpegReconStage::kNone;
   1694      dec->ib.reset();
   1695      if (dec->events_wanted & JXL_DEC_FULL_IMAGE) {
   1696        // Return the full image event here now, this may be delayed if this
   1697        // could only be done after decoding an exif or xmp box after the
   1698        // codestream.
   1699        return JXL_DEC_FULL_IMAGE;
   1700      }
   1701    }
   1702 #endif
   1703 
   1704    if (dec->box_stage == BoxStage::kHeader) {
   1705      if (!dec->have_container) {
   1706        if (dec->stage == DecoderStage::kCodestreamFinished)
   1707          return JXL_DEC_SUCCESS;
   1708        dec->box_stage = BoxStage::kCodestream;
   1709        dec->box_contents_unbounded = true;
   1710        continue;
   1711      }
   1712      if (dec->avail_in == 0) {
   1713        if (dec->stage != DecoderStage::kCodestreamFinished) {
   1714          // Not yet seen (all) codestream boxes.
   1715          return JXL_DEC_NEED_MORE_INPUT;
   1716        }
   1717 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1718        if (dec->JbrdNeedMoreBoxes()) {
   1719          return JXL_DEC_NEED_MORE_INPUT;
   1720        }
   1721 #endif
   1722        if (dec->input_closed) {
   1723          return JXL_DEC_SUCCESS;
   1724        }
   1725        if (!(dec->events_wanted & JXL_DEC_BOX)) {
   1726          // All codestream and jbrd metadata boxes finished, and no individual
   1727          // boxes requested by user, so no need to request any more input.
   1728          // This returns success for backwards compatibility, when
   1729          // JxlDecoderCloseInput and JXL_DEC_BOX did not exist, as well
   1730          // as for efficiency.
   1731          return JXL_DEC_SUCCESS;
   1732        }
   1733        // Even though we are exactly at a box end, there still may be more
   1734        // boxes. The user may call JxlDecoderCloseInput to indicate the input
   1735        // is finished and get success instead.
   1736        return JXL_DEC_NEED_MORE_INPUT;
   1737      }
   1738 
   1739      bool boxed_codestream_done =
   1740          ((dec->events_wanted & JXL_DEC_BOX) &&
   1741           dec->stage == DecoderStage::kCodestreamFinished &&
   1742 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1743           !dec->JbrdNeedMoreBoxes() &&
   1744 #endif
   1745           dec->last_codestream_seen);
   1746      if (boxed_codestream_done && dec->avail_in >= 2 &&
   1747          dec->next_in[0] == 0xff &&
   1748          dec->next_in[1] == jxl::kCodestreamMarker) {
   1749        // We detected the start of the next naked codestream, so we can return
   1750        // success here.
   1751        return JXL_DEC_SUCCESS;
   1752      }
   1753 
   1754      uint64_t box_size;
   1755      uint64_t header_size;
   1756      JxlDecoderStatus status =
   1757          ParseBoxHeader(dec->next_in, dec->avail_in, 0, dec->file_pos,
   1758                         dec->box_type, &box_size, &header_size);
   1759      if (status != JXL_DEC_SUCCESS) {
   1760        if (status == JXL_DEC_NEED_MORE_INPUT) {
   1761          dec->basic_info_size_hint =
   1762              InitialBasicInfoSizeHint() + header_size - dec->file_pos;
   1763        }
   1764        return status;
   1765      }
   1766      if (memcmp(dec->box_type, "brob", 4) == 0) {
   1767        if (dec->avail_in < header_size + 4) {
   1768          return JXL_DEC_NEED_MORE_INPUT;
   1769        }
   1770        memcpy(dec->box_decoded_type, dec->next_in + header_size,
   1771               sizeof(dec->box_decoded_type));
   1772      } else {
   1773        memcpy(dec->box_decoded_type, dec->box_type,
   1774               sizeof(dec->box_decoded_type));
   1775      }
   1776 
   1777      // Box order validity checks
   1778      // The signature box at box_count == 1 is not checked here since that's
   1779      // already done at the beginning.
   1780      dec->box_count++;
   1781      if (boxed_codestream_done && memcmp(dec->box_type, "JXL ", 4) == 0) {
   1782        // We detected the start of the next boxed stream, so we can return
   1783        // success here.
   1784        return JXL_DEC_SUCCESS;
   1785      }
   1786      if (dec->box_count == 2 && memcmp(dec->box_type, "ftyp", 4) != 0) {
   1787        return JXL_INPUT_ERROR("the second box must be the ftyp box");
   1788      }
   1789      if (memcmp(dec->box_type, "ftyp", 4) == 0 && dec->box_count != 2) {
   1790        return JXL_INPUT_ERROR("the ftyp box must come second");
   1791      }
   1792 
   1793      dec->box_contents_unbounded = (box_size == 0);
   1794      dec->box_contents_begin = dec->file_pos + header_size;
   1795      dec->box_contents_end =
   1796          dec->box_contents_unbounded ? 0 : (dec->file_pos + box_size);
   1797      dec->box_contents_size =
   1798          dec->box_contents_unbounded ? 0 : (box_size - header_size);
   1799      dec->box_size = box_size;
   1800      dec->header_size = header_size;
   1801 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1802      if (dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) {
   1803        // Initiate storing of Exif or XMP data for JPEG reconstruction
   1804        if (dec->store_exif == 0 &&
   1805            memcmp(dec->box_decoded_type, "Exif", 4) == 0) {
   1806          dec->store_exif = 1;
   1807          dec->recon_out_buffer_pos = 0;
   1808        }
   1809        if (dec->store_xmp == 0 &&
   1810            memcmp(dec->box_decoded_type, "xml ", 4) == 0) {
   1811          dec->store_xmp = 1;
   1812          dec->recon_out_buffer_pos = 0;
   1813        }
   1814      }
   1815 #endif
   1816 #if JPEGXL_ENABLE_BOXES
   1817      if (dec->events_wanted & JXL_DEC_BOX) {
   1818        bool decompress =
   1819            dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
   1820        dec->box_content_decoder.StartBox(
   1821            decompress, dec->box_contents_unbounded, dec->box_contents_size);
   1822      }
   1823 #endif
   1824 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1825      if (dec->store_exif == 1 || dec->store_xmp == 1) {
   1826        bool brob = memcmp(dec->box_type, "brob", 4) == 0;
   1827        dec->metadata_decoder.StartBox(brob, dec->box_contents_unbounded,
   1828                                       dec->box_contents_size);
   1829      }
   1830 #endif
   1831      if (memcmp(dec->box_type, "ftyp", 4) == 0) {
   1832        dec->box_stage = BoxStage::kFtyp;
   1833      } else if (memcmp(dec->box_type, "jxlc", 4) == 0) {
   1834        if (dec->last_codestream_seen) {
   1835          return JXL_INPUT_ERROR("there can only be one jxlc box");
   1836        }
   1837        dec->last_codestream_seen = true;
   1838        dec->box_stage = BoxStage::kCodestream;
   1839      } else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
   1840        dec->box_stage = BoxStage::kPartialCodestream;
   1841 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1842      } else if ((dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) &&
   1843                 memcmp(dec->box_type, "jbrd", 4) == 0) {
   1844        if (!(dec->events_wanted & JXL_DEC_JPEG_RECONSTRUCTION)) {
   1845          return JXL_INPUT_ERROR(
   1846              "multiple JPEG reconstruction boxes not supported");
   1847        }
   1848        dec->box_stage = BoxStage::kJpegRecon;
   1849 #endif
   1850      } else {
   1851        dec->box_stage = BoxStage::kSkip;
   1852      }
   1853 
   1854      if (dec->events_wanted & JXL_DEC_BOX) {
   1855        dec->box_event = true;
   1856        dec->box_out_buffer_set_current_box = false;
   1857        return JXL_DEC_BOX;
   1858      }
   1859    } else if (dec->box_stage == BoxStage::kFtyp) {
   1860      if (dec->box_contents_size < 12) {
   1861        return JXL_INPUT_ERROR("file type box too small");
   1862      }
   1863      if (dec->avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
   1864      if (memcmp(dec->next_in, "jxl ", 4) != 0) {
   1865        return JXL_INPUT_ERROR("file type box major brand must be \"jxl \"");
   1866      }
   1867      dec->AdvanceInput(4);
   1868      dec->box_stage = BoxStage::kSkip;
   1869    } else if (dec->box_stage == BoxStage::kPartialCodestream) {
   1870      if (dec->last_codestream_seen) {
   1871        return JXL_INPUT_ERROR("cannot have jxlp box after last jxlp box");
   1872      }
   1873      // TODO(lode): error if box is unbounded but last bit not set
   1874      if (dec->avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
   1875      if (!dec->box_contents_unbounded && dec->box_contents_size < 4) {
   1876        return JXL_INPUT_ERROR("jxlp box too small to contain index");
   1877      }
   1878      size_t jxlp_index = LoadBE32(dec->next_in);
   1879      // The high bit of jxlp_index indicates whether this is the last
   1880      // jxlp box.
   1881      if (jxlp_index & 0x80000000) {
   1882        dec->last_codestream_seen = true;
   1883      }
   1884      dec->AdvanceInput(4);
   1885      dec->box_stage = BoxStage::kCodestream;
   1886    } else if (dec->box_stage == BoxStage::kCodestream) {
   1887      JxlDecoderStatus status = jxl::JxlDecoderProcessCodestream(dec);
   1888 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1889      if (status == JXL_DEC_FULL_IMAGE) {
   1890        if (dec->recon_output_jpeg != JpegReconStage::kNone) {
   1891          continue;
   1892        }
   1893      }
   1894 #endif
   1895      if (status == JXL_DEC_NEED_MORE_INPUT) {
   1896        if (dec->file_pos == dec->box_contents_end &&
   1897            !dec->box_contents_unbounded) {
   1898          dec->box_stage = BoxStage::kHeader;
   1899          continue;
   1900        }
   1901      }
   1902 
   1903      if (status == JXL_DEC_SUCCESS) {
   1904 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1905        if (dec->JbrdNeedMoreBoxes()) {
   1906          dec->box_stage = BoxStage::kSkip;
   1907          continue;
   1908        }
   1909 #endif
   1910        if (dec->box_contents_unbounded) {
   1911          // Last box reached and codestream done, nothing more to do.
   1912          break;
   1913        }
   1914        if (dec->events_wanted & JXL_DEC_BOX) {
   1915          // Codestream done, but there may be more other boxes.
   1916          dec->box_stage = BoxStage::kSkip;
   1917          continue;
   1918        }
   1919      }
   1920      return status;
   1921 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   1922    } else if (dec->box_stage == BoxStage::kJpegRecon) {
   1923      if (!dec->jpeg_decoder.IsParsingBox()) {
   1924        // This is a new JPEG reconstruction metadata box.
   1925        dec->jpeg_decoder.StartBox(dec->box_contents_unbounded,
   1926                                   dec->box_contents_size);
   1927      }
   1928      const uint8_t* next_in = dec->next_in;
   1929      size_t avail_in = dec->avail_in;
   1930      JxlDecoderStatus recon_result =
   1931          dec->jpeg_decoder.Process(&next_in, &avail_in);
   1932      size_t consumed = next_in - dec->next_in;
   1933      dec->AdvanceInput(consumed);
   1934      if (recon_result == JXL_DEC_JPEG_RECONSTRUCTION) {
   1935        jxl::jpeg::JPEGData* jpeg_data = dec->jpeg_decoder.GetJpegData();
   1936        size_t num_exif = jxl::JxlToJpegDecoder::NumExifMarkers(*jpeg_data);
   1937        size_t num_xmp = jxl::JxlToJpegDecoder::NumXmpMarkers(*jpeg_data);
   1938        if (num_exif) {
   1939          if (num_exif > 1) {
   1940            return JXL_INPUT_ERROR(
   1941                "multiple exif markers for JPEG reconstruction not supported");
   1942          }
   1943          if (JXL_DEC_SUCCESS != jxl::JxlToJpegDecoder::ExifBoxContentSize(
   1944                                     *jpeg_data, &dec->recon_exif_size)) {
   1945            return JXL_INPUT_ERROR("invalid jbrd exif size");
   1946          }
   1947        }
   1948        if (num_xmp) {
   1949          if (num_xmp > 1) {
   1950            return JXL_INPUT_ERROR(
   1951                "multiple XMP markers for JPEG reconstruction not supported");
   1952          }
   1953          if (JXL_DEC_SUCCESS != jxl::JxlToJpegDecoder::XmlBoxContentSize(
   1954                                     *jpeg_data, &dec->recon_xmp_size)) {
   1955            return JXL_INPUT_ERROR("invalid jbrd XMP size");
   1956          }
   1957        }
   1958 
   1959        dec->box_stage = BoxStage::kHeader;
   1960        // If successful JPEG reconstruction, return the success if the user
   1961        // cares about it, otherwise continue.
   1962        if (dec->events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) {
   1963          dec->events_wanted &= ~JXL_DEC_JPEG_RECONSTRUCTION;
   1964          return JXL_DEC_JPEG_RECONSTRUCTION;
   1965        }
   1966      } else {
   1967        // If anything else, return the result.
   1968        return recon_result;
   1969      }
   1970 #endif
   1971    } else if (dec->box_stage == BoxStage::kSkip) {
   1972      if (dec->box_contents_unbounded) {
   1973        if (dec->input_closed) {
   1974          return JXL_DEC_SUCCESS;
   1975        }
   1976        if (!(dec->box_out_buffer_set)) {
   1977          // An unbounded box is always the last box. Not requesting box data,
   1978          // so return success even if JxlDecoderCloseInput was not called for
   1979          // backwards compatibility as well as efficiency since this box is
   1980          // being skipped.
   1981          return JXL_DEC_SUCCESS;
   1982        }
   1983        // Arbitrarily more bytes may follow, only JxlDecoderCloseInput can
   1984        // mark the end.
   1985        dec->AdvanceInput(dec->avail_in);
   1986        return JXL_DEC_NEED_MORE_INPUT;
   1987      }
   1988      // Amount of remaining bytes in the box that is being skipped.
   1989      size_t remaining = dec->box_contents_end - dec->file_pos;
   1990      if (dec->avail_in < remaining) {
   1991        // Indicate how many more bytes needed starting from next_in.
   1992        dec->basic_info_size_hint =
   1993            InitialBasicInfoSizeHint() + dec->box_contents_end - dec->file_pos;
   1994        // Don't have the full box yet, skip all we have so far
   1995        dec->AdvanceInput(dec->avail_in);
   1996        return JXL_DEC_NEED_MORE_INPUT;
   1997      } else {
   1998        // Full box available, skip all its remaining bytes
   1999        dec->AdvanceInput(remaining);
   2000        dec->box_stage = BoxStage::kHeader;
   2001      }
   2002    } else {
   2003      JXL_DEBUG_ABORT("Unreachable");
   2004    }
   2005  }
   2006  return JXL_DEC_SUCCESS;
   2007 }
   2008 
   2009 JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
   2010  if (dec->stage == DecoderStage::kInited) {
   2011    dec->stage = DecoderStage::kStarted;
   2012  }
   2013  if (dec->stage == DecoderStage::kError) {
   2014    return JXL_API_ERROR(
   2015        "Cannot keep using decoder after it encountered an error, use "
   2016        "JxlDecoderReset to reset it");
   2017  }
   2018 
   2019  if (!dec->got_signature) {
   2020    JxlSignature sig = JxlSignatureCheck(dec->next_in, dec->avail_in);
   2021    if (sig == JXL_SIG_INVALID) return JXL_INPUT_ERROR("invalid signature");
   2022    if (sig == JXL_SIG_NOT_ENOUGH_BYTES) {
   2023      if (dec->input_closed) {
   2024        return JXL_INPUT_ERROR("file too small for signature");
   2025      }
   2026      return JXL_DEC_NEED_MORE_INPUT;
   2027    }
   2028 
   2029    dec->got_signature = true;
   2030 
   2031    if (sig == JXL_SIG_CONTAINER) {
   2032      dec->have_container = true;
   2033    } else {
   2034      dec->last_codestream_seen = true;
   2035    }
   2036  }
   2037 
   2038  JxlDecoderStatus status = HandleBoxes(dec);
   2039 
   2040  if (status == JXL_DEC_NEED_MORE_INPUT && dec->input_closed) {
   2041    return JXL_INPUT_ERROR("premature end of input");
   2042  }
   2043 
   2044  // Even if the box handling returns success, certain types of
   2045  // data may be missing.
   2046  if (status == JXL_DEC_SUCCESS) {
   2047    if (dec->CanUseMoreCodestreamInput()) {
   2048      return JXL_INPUT_ERROR("codestream never finished");
   2049    }
   2050 #if JPEGXL_ENABLE_TRANSCODE_JPEG
   2051    if (dec->JbrdNeedMoreBoxes()) {
   2052      return JXL_INPUT_ERROR("missing metadata boxes for jpeg reconstruction");
   2053    }
   2054 #endif
   2055  }
   2056 
   2057  return status;
   2058 }
   2059 
   2060 // To ensure ABI forward-compatibility, this struct has a constant size.
   2061 static_assert(sizeof(JxlBasicInfo) == 204,
   2062              "JxlBasicInfo struct size should remain constant");
   2063 
   2064 JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
   2065                                        JxlBasicInfo* info) {
   2066  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
   2067 
   2068  if (info) {
   2069    memset(info, 0, sizeof(*info));
   2070 
   2071    const jxl::ImageMetadata& meta = dec->metadata.m;
   2072 
   2073    info->have_container = TO_JXL_BOOL(dec->have_container);
   2074    info->xsize = dec->metadata.size.xsize();
   2075    info->ysize = dec->metadata.size.ysize();
   2076    info->uses_original_profile = TO_JXL_BOOL(!meta.xyb_encoded);
   2077 
   2078    info->bits_per_sample = meta.bit_depth.bits_per_sample;
   2079    info->exponent_bits_per_sample = meta.bit_depth.exponent_bits_per_sample;
   2080 
   2081    info->have_preview = TO_JXL_BOOL(meta.have_preview);
   2082    info->have_animation = TO_JXL_BOOL(meta.have_animation);
   2083    info->orientation = static_cast<JxlOrientation>(meta.orientation);
   2084 
   2085    if (!dec->keep_orientation) {
   2086      if (info->orientation >= JXL_ORIENT_TRANSPOSE) {
   2087        std::swap(info->xsize, info->ysize);
   2088      }
   2089      info->orientation = JXL_ORIENT_IDENTITY;
   2090    }
   2091 
   2092    info->intensity_target = meta.IntensityTarget();
   2093    if (dec->desired_intensity_target > 0) {
   2094      info->intensity_target = dec->desired_intensity_target;
   2095    }
   2096    info->min_nits = meta.tone_mapping.min_nits;
   2097    info->relative_to_max_display =
   2098        TO_JXL_BOOL(meta.tone_mapping.relative_to_max_display);
   2099    info->linear_below = meta.tone_mapping.linear_below;
   2100 
   2101    const jxl::ExtraChannelInfo* alpha = meta.Find(jxl::ExtraChannel::kAlpha);
   2102    if (alpha != nullptr) {
   2103      info->alpha_bits = alpha->bit_depth.bits_per_sample;
   2104      info->alpha_exponent_bits = alpha->bit_depth.exponent_bits_per_sample;
   2105      info->alpha_premultiplied = TO_JXL_BOOL(alpha->alpha_associated);
   2106    } else {
   2107      info->alpha_bits = 0;
   2108      info->alpha_exponent_bits = 0;
   2109      info->alpha_premultiplied = 0;
   2110    }
   2111 
   2112    info->num_color_channels =
   2113        meta.color_encoding.GetColorSpace() == jxl::ColorSpace::kGray ? 1 : 3;
   2114 
   2115    info->num_extra_channels = meta.num_extra_channels;
   2116 
   2117    if (info->have_preview) {
   2118      info->preview.xsize = dec->metadata.m.preview_size.xsize();
   2119      info->preview.ysize = dec->metadata.m.preview_size.ysize();
   2120    }
   2121 
   2122    if (info->have_animation) {
   2123      info->animation.tps_numerator = dec->metadata.m.animation.tps_numerator;
   2124      info->animation.tps_denominator =
   2125          dec->metadata.m.animation.tps_denominator;
   2126      info->animation.num_loops = dec->metadata.m.animation.num_loops;
   2127      info->animation.have_timecodes =
   2128          TO_JXL_BOOL(dec->metadata.m.animation.have_timecodes);
   2129    }
   2130 
   2131    if (meta.have_intrinsic_size) {
   2132      info->intrinsic_xsize = dec->metadata.m.intrinsic_size.xsize();
   2133      info->intrinsic_ysize = dec->metadata.m.intrinsic_size.ysize();
   2134    } else {
   2135      info->intrinsic_xsize = info->xsize;
   2136      info->intrinsic_ysize = info->ysize;
   2137    }
   2138  }
   2139 
   2140  return JXL_DEC_SUCCESS;
   2141 }
   2142 
   2143 JxlDecoderStatus JxlDecoderGetExtraChannelInfo(const JxlDecoder* dec,
   2144                                               size_t index,
   2145                                               JxlExtraChannelInfo* info) {
   2146  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
   2147 
   2148  const std::vector<jxl::ExtraChannelInfo>& channels =
   2149      dec->metadata.m.extra_channel_info;
   2150 
   2151  if (index >= channels.size()) return JXL_DEC_ERROR;  // out of bounds
   2152  const jxl::ExtraChannelInfo& channel = channels[index];
   2153 
   2154  info->type = static_cast<JxlExtraChannelType>(channel.type);
   2155  info->bits_per_sample = channel.bit_depth.bits_per_sample;
   2156  info->exponent_bits_per_sample =
   2157      channel.bit_depth.floating_point_sample
   2158          ? channel.bit_depth.exponent_bits_per_sample
   2159          : 0;
   2160  info->dim_shift = channel.dim_shift;
   2161  info->name_length = channel.name.size();
   2162  info->alpha_premultiplied = TO_JXL_BOOL(channel.alpha_associated);
   2163  info->spot_color[0] = channel.spot_color[0];
   2164  info->spot_color[1] = channel.spot_color[1];
   2165  info->spot_color[2] = channel.spot_color[2];
   2166  info->spot_color[3] = channel.spot_color[3];
   2167  info->cfa_channel = channel.cfa_channel;
   2168 
   2169  return JXL_DEC_SUCCESS;
   2170 }
   2171 
   2172 JxlDecoderStatus JxlDecoderGetExtraChannelName(const JxlDecoder* dec,
   2173                                               size_t index, char* name,
   2174                                               size_t size) {
   2175  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
   2176 
   2177  const std::vector<jxl::ExtraChannelInfo>& channels =
   2178      dec->metadata.m.extra_channel_info;
   2179 
   2180  if (index >= channels.size()) return JXL_DEC_ERROR;  // out of bounds
   2181  const jxl::ExtraChannelInfo& channel = channels[index];
   2182 
   2183  // Also need null-termination character
   2184  if (channel.name.size() + 1 > size) return JXL_DEC_ERROR;
   2185 
   2186  memcpy(name, channel.name.c_str(), channel.name.size() + 1);
   2187 
   2188  return JXL_DEC_SUCCESS;
   2189 }
   2190 
   2191 namespace {
   2192 
   2193 // Gets the jxl::ColorEncoding for the desired target, and checks errors.
   2194 // Returns the object regardless of whether the actual color space is in ICC,
   2195 // but ensures that if the color encoding is not the encoding from the
   2196 // codestream header metadata, it cannot require ICC profile.
   2197 JxlDecoderStatus GetColorEncodingForTarget(
   2198    const JxlDecoder* dec, JxlColorProfileTarget target,
   2199    const jxl::ColorEncoding** encoding) {
   2200  if (!dec->got_all_headers) return JXL_DEC_NEED_MORE_INPUT;
   2201  *encoding = nullptr;
   2202  if (target == JXL_COLOR_PROFILE_TARGET_DATA && dec->metadata.m.xyb_encoded) {
   2203    *encoding = &dec->passes_state->output_encoding_info.color_encoding;
   2204  } else {
   2205    *encoding = &dec->metadata.m.color_encoding;
   2206  }
   2207  return JXL_DEC_SUCCESS;
   2208 }
   2209 }  // namespace
   2210 
   2211 JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile(
   2212    const JxlDecoder* dec, JxlColorProfileTarget target,
   2213    JxlColorEncoding* color_encoding) {
   2214  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
   2215  JxlDecoderStatus status =
   2216      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
   2217  if (status) return status;
   2218 
   2219  if (jxl_color_encoding->WantICC())
   2220    return JXL_DEC_ERROR;  // Indicate no encoded profile available.
   2221 
   2222  if (color_encoding) {
   2223    *color_encoding = jxl_color_encoding->ToExternal();
   2224  }
   2225 
   2226  return JXL_DEC_SUCCESS;
   2227 }
   2228 
   2229 JxlDecoderStatus JxlDecoderGetICCProfileSize(const JxlDecoder* dec,
   2230                                             JxlColorProfileTarget target,
   2231                                             size_t* size) {
   2232  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
   2233  JxlDecoderStatus status =
   2234      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
   2235  if (status != JXL_DEC_SUCCESS) return status;
   2236 
   2237  if (jxl_color_encoding->WantICC()) {
   2238    jxl::ColorSpace color_space =
   2239        dec->metadata.m.color_encoding.GetColorSpace();
   2240    if (color_space == jxl::ColorSpace::kUnknown ||
   2241        color_space == jxl::ColorSpace::kXYB) {
   2242      // This indicates there's no ICC profile available
   2243      // TODO(lode): for the XYB case, do we want to craft an ICC profile that
   2244      // represents XYB as an RGB profile? It may be possible, but not with
   2245      // only 1D transfer functions.
   2246      return JXL_DEC_ERROR;
   2247    }
   2248  }
   2249 
   2250  if (size) {
   2251    *size = jxl_color_encoding->ICC().size();
   2252  }
   2253 
   2254  return JXL_DEC_SUCCESS;
   2255 }
   2256 
   2257 JxlDecoderStatus JxlDecoderGetColorAsICCProfile(const JxlDecoder* dec,
   2258                                                JxlColorProfileTarget target,
   2259                                                uint8_t* icc_profile,
   2260                                                size_t size) {
   2261  size_t wanted_size;
   2262  // This also checks the NEED_MORE_INPUT and the unknown/xyb cases
   2263  JxlDecoderStatus status =
   2264      JxlDecoderGetICCProfileSize(dec, target, &wanted_size);
   2265  if (status != JXL_DEC_SUCCESS) return status;
   2266  if (size < wanted_size) return JXL_API_ERROR("ICC profile output too small");
   2267 
   2268  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
   2269  status = GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
   2270  if (status != JXL_DEC_SUCCESS) return status;
   2271 
   2272  memcpy(icc_profile, jxl_color_encoding->ICC().data(),
   2273         jxl_color_encoding->ICC().size());
   2274 
   2275  return JXL_DEC_SUCCESS;
   2276 }
   2277 
   2278 namespace {
   2279 
   2280 // Returns the amount of bits needed for getting memory buffer size, and does
   2281 // all error checking required for size checking and format validity.
   2282 JxlDecoderStatus PrepareSizeCheck(const JxlDecoder* dec,
   2283                                  const JxlPixelFormat* format, size_t* bits) {
   2284  if (!dec->got_basic_info) {
   2285    // Don't know image dimensions yet, cannot check for valid size.
   2286    return JXL_DEC_NEED_MORE_INPUT;
   2287  }
   2288  if (!dec->coalescing &&
   2289      (!dec->frame_header || dec->frame_stage == FrameStage::kHeader)) {
   2290    return JXL_API_ERROR("Don't know frame dimensions yet");
   2291  }
   2292  if (format->num_channels > 4) {
   2293    return JXL_API_ERROR("More than 4 channels not supported");
   2294  }
   2295 
   2296  *bits = BitsPerChannel(format->data_type);
   2297 
   2298  if (*bits == 0) {
   2299    return JXL_API_ERROR("Invalid/unsupported data type");
   2300  }
   2301 
   2302  return JXL_DEC_SUCCESS;
   2303 }
   2304 
   2305 }  // namespace
   2306 
   2307 size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec) {
   2308  return dec->downsampling_target;
   2309 }
   2310 
   2311 JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec) {
   2312  if (!dec->image_out_buffer_set) return JXL_DEC_ERROR;
   2313  if (dec->frame_stage != FrameStage::kFull) {
   2314    return JXL_DEC_ERROR;
   2315  }
   2316  JXL_DASSERT(dec->frame_dec);
   2317  if (!dec->frame_dec->HasDecodedDC()) {
   2318    // FrameDecoder::Flush currently requires DC to have been decoded already
   2319    // to work correctly.
   2320    return JXL_DEC_ERROR;
   2321  }
   2322 
   2323  if (!dec->frame_dec->Flush()) {
   2324    return JXL_DEC_ERROR;
   2325  }
   2326 
   2327  return JXL_DEC_SUCCESS;
   2328 }
   2329 
   2330 JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec,
   2331                                             const JxlCmsInterface cms) {
   2332  if (!dec->passes_state) {
   2333    dec->passes_state =
   2334        jxl::make_unique<jxl::PassesDecoderState>(&dec->memory_manager);
   2335  }
   2336  dec->passes_state->output_encoding_info.color_management_system = cms;
   2337  dec->passes_state->output_encoding_info.cms_set = true;
   2338  return JXL_DEC_SUCCESS;
   2339 }
   2340 
   2341 static JxlDecoderStatus GetMinSize(const JxlDecoder* dec,
   2342                                   const JxlPixelFormat* format,
   2343                                   size_t num_channels, size_t* min_size,
   2344                                   bool preview) {
   2345  size_t bits;
   2346  JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
   2347  if (status != JXL_DEC_SUCCESS) return status;
   2348  size_t xsize;
   2349  size_t ysize;
   2350  if (preview) {
   2351    xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation);
   2352    ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation);
   2353  } else {
   2354    GetCurrentDimensions(dec, xsize, ysize);
   2355  }
   2356  if (num_channels == 0) num_channels = format->num_channels;
   2357  size_t row_size =
   2358      jxl::DivCeil(xsize * num_channels * bits, jxl::kBitsPerByte);
   2359  size_t last_row_size = row_size;
   2360  if (format->align > 1) {
   2361    row_size = jxl::DivCeil(row_size, format->align) * format->align;
   2362  }
   2363  *min_size = row_size * (ysize - 1) + last_row_size;
   2364  return JXL_DEC_SUCCESS;
   2365 }
   2366 
   2367 JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
   2368    const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
   2369  if (format->num_channels < 3 &&
   2370      !dec->image_metadata.color_encoding.IsGray()) {
   2371    return JXL_API_ERROR("Number of channels is too low for color output");
   2372  }
   2373  return GetMinSize(dec, format, 0, size, true);
   2374 }
   2375 
   2376 JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer(
   2377    JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size) {
   2378  if (!dec->got_basic_info || !dec->metadata.m.have_preview ||
   2379      !(dec->orig_events_wanted & JXL_DEC_PREVIEW_IMAGE)) {
   2380    return JXL_API_ERROR("No preview out buffer needed at this time");
   2381  }
   2382  if (format->num_channels < 3 &&
   2383      !dec->image_metadata.color_encoding.IsGray()) {
   2384    return JXL_API_ERROR("Number of channels is too low for color output");
   2385  }
   2386 
   2387  size_t min_size;
   2388  // This also checks whether the format is valid and supported and basic info
   2389  // is available.
   2390  JxlDecoderStatus status =
   2391      JxlDecoderPreviewOutBufferSize(dec, format, &min_size);
   2392  if (status != JXL_DEC_SUCCESS) return status;
   2393 
   2394  if (size < min_size) return JXL_DEC_ERROR;
   2395 
   2396  dec->image_out_buffer_set = true;
   2397  dec->image_out_buffer = buffer;
   2398  dec->image_out_size = size;
   2399  dec->image_out_format = *format;
   2400 
   2401  return JXL_DEC_SUCCESS;
   2402 }
   2403 
   2404 JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize(
   2405    const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
   2406  if (format->num_channels < 3 &&
   2407      !dec->image_metadata.color_encoding.IsGray()) {
   2408    return JXL_API_ERROR("Number of channels is too low for color output");
   2409  }
   2410 
   2411  return GetMinSize(dec, format, 0, size, false);
   2412 }
   2413 
   2414 JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec,
   2415                                             const JxlPixelFormat* format,
   2416                                             void* buffer, size_t size) {
   2417  if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE)) {
   2418    return JXL_API_ERROR("No image out buffer needed at this time");
   2419  }
   2420  if (dec->image_out_buffer_set && !!dec->image_out_run_callback) {
   2421    return JXL_API_ERROR(
   2422        "Cannot change from image out callback to image out buffer");
   2423  }
   2424  if (format->num_channels < 3 &&
   2425      !dec->image_metadata.color_encoding.IsGray()) {
   2426    return JXL_API_ERROR("Number of channels is too low for color output");
   2427  }
   2428  size_t min_size;
   2429  // This also checks whether the format is valid and supported and basic info
   2430  // is available.
   2431  JxlDecoderStatus status =
   2432      JxlDecoderImageOutBufferSize(dec, format, &min_size);
   2433  if (status != JXL_DEC_SUCCESS) return status;
   2434 
   2435  if (size < min_size) return JXL_DEC_ERROR;
   2436 
   2437  dec->image_out_buffer_set = true;
   2438  dec->image_out_buffer = buffer;
   2439  dec->image_out_size = size;
   2440  dec->image_out_format = *format;
   2441 
   2442  return JXL_DEC_SUCCESS;
   2443 }
   2444 
   2445 JxlDecoderStatus JxlDecoderExtraChannelBufferSize(const JxlDecoder* dec,
   2446                                                  const JxlPixelFormat* format,
   2447                                                  size_t* size,
   2448                                                  uint32_t index) {
   2449  if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE)) {
   2450    return JXL_API_ERROR("No extra channel buffer needed at this time");
   2451  }
   2452 
   2453  if (index >= dec->metadata.m.num_extra_channels) {
   2454    return JXL_API_ERROR("Invalid extra channel index");
   2455  }
   2456 
   2457  return GetMinSize(dec, format, 1, size, false);
   2458 }
   2459 
   2460 JxlDecoderStatus JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec,
   2461                                                 const JxlPixelFormat* format,
   2462                                                 void* buffer, size_t size,
   2463                                                 uint32_t index) {
   2464  size_t min_size;
   2465  // This also checks whether the format and index are valid and supported and
   2466  // basic info is available.
   2467  JxlDecoderStatus status =
   2468      JxlDecoderExtraChannelBufferSize(dec, format, &min_size, index);
   2469  if (status != JXL_DEC_SUCCESS) return status;
   2470 
   2471  if (size < min_size) return JXL_DEC_ERROR;
   2472 
   2473  if (dec->extra_channel_output.size() <= index) {
   2474    dec->extra_channel_output.resize(dec->metadata.m.num_extra_channels,
   2475                                     {{}, nullptr, 0});
   2476  }
   2477  // Guaranteed correct thanks to check in JxlDecoderExtraChannelBufferSize.
   2478  JXL_DASSERT(dec->extra_channel_output.size() > index);
   2479 
   2480  dec->extra_channel_output[index].format = *format;
   2481  dec->extra_channel_output[index].format.num_channels = 1;
   2482  dec->extra_channel_output[index].buffer = buffer;
   2483  dec->extra_channel_output[index].buffer_size = size;
   2484 
   2485  return JXL_DEC_SUCCESS;
   2486 }
   2487 
   2488 JxlDecoderStatus JxlDecoderSetImageOutCallback(JxlDecoder* dec,
   2489                                               const JxlPixelFormat* format,
   2490                                               JxlImageOutCallback callback,
   2491                                               void* opaque) {
   2492  dec->simple_image_out_callback.callback = callback;
   2493  dec->simple_image_out_callback.opaque = opaque;
   2494  const auto init_callback =
   2495      +[](void* init_opaque, size_t num_threads, size_t num_pixels_per_thread) {
   2496        // No initialization to do, just reuse init_opaque as run_opaque.
   2497        return init_opaque;
   2498      };
   2499  const auto run_callback =
   2500      +[](void* run_opaque, size_t thread_id, size_t x, size_t y,
   2501          size_t num_pixels, const void* pixels) {
   2502        const auto* const simple_callback =
   2503            static_cast<const JxlDecoder::SimpleImageOutCallback*>(run_opaque);
   2504        simple_callback->callback(simple_callback->opaque, x, y, num_pixels,
   2505                                  pixels);
   2506      };
   2507  const auto destroy_callback = +[](void* run_opaque) {};
   2508  return JxlDecoderSetMultithreadedImageOutCallback(
   2509      dec, format, init_callback, run_callback,
   2510      /*destroy_callback=*/destroy_callback, &dec->simple_image_out_callback);
   2511 }
   2512 
   2513 JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback(
   2514    JxlDecoder* dec, const JxlPixelFormat* format,
   2515    JxlImageOutInitCallback init_callback, JxlImageOutRunCallback run_callback,
   2516    JxlImageOutDestroyCallback destroy_callback, void* init_opaque) {
   2517  if (dec->image_out_buffer_set && !!dec->image_out_buffer) {
   2518    return JXL_API_ERROR(
   2519        "Cannot change from image out buffer to image out callback");
   2520  }
   2521 
   2522  if (init_callback == nullptr || run_callback == nullptr ||
   2523      destroy_callback == nullptr) {
   2524    return JXL_API_ERROR("All callbacks are required");
   2525  }
   2526 
   2527  // Perform error checking for invalid format.
   2528  size_t bits_sink;
   2529  JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits_sink);
   2530  if (status != JXL_DEC_SUCCESS) return status;
   2531 
   2532  dec->image_out_buffer_set = true;
   2533  dec->image_out_init_callback = init_callback;
   2534  dec->image_out_run_callback = run_callback;
   2535  dec->image_out_destroy_callback = destroy_callback;
   2536  dec->image_out_init_opaque = init_opaque;
   2537  dec->image_out_format = *format;
   2538 
   2539  return JXL_DEC_SUCCESS;
   2540 }
   2541 
   2542 JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
   2543                                          JxlFrameHeader* header) {
   2544  if (!dec->frame_header || dec->frame_stage == FrameStage::kHeader) {
   2545    return JXL_API_ERROR("no frame header available");
   2546  }
   2547  const auto& metadata = dec->metadata.m;
   2548  memset(header, 0, sizeof(*header));
   2549  if (metadata.have_animation) {
   2550    header->duration = dec->frame_header->animation_frame.duration;
   2551    if (metadata.animation.have_timecodes) {
   2552      header->timecode = dec->frame_header->animation_frame.timecode;
   2553    }
   2554  }
   2555  header->name_length = dec->frame_header->name.size();
   2556  header->is_last = TO_JXL_BOOL(dec->frame_header->is_last);
   2557  size_t xsize;
   2558  size_t ysize;
   2559  GetCurrentDimensions(dec, xsize, ysize);
   2560  header->layer_info.xsize = xsize;
   2561  header->layer_info.ysize = ysize;
   2562  if (!dec->coalescing && dec->frame_header->custom_size_or_origin) {
   2563    header->layer_info.crop_x0 = dec->frame_header->frame_origin.x0;
   2564    header->layer_info.crop_y0 = dec->frame_header->frame_origin.y0;
   2565    header->layer_info.have_crop = JXL_TRUE;
   2566  } else {
   2567    header->layer_info.crop_x0 = 0;
   2568    header->layer_info.crop_y0 = 0;
   2569    header->layer_info.have_crop = JXL_FALSE;
   2570  }
   2571  if (!dec->keep_orientation && !dec->coalescing) {
   2572    // orient the crop offset
   2573    size_t W = dec->metadata.oriented_xsize(false);
   2574    size_t H = dec->metadata.oriented_ysize(false);
   2575    if (metadata.orientation > 4) {
   2576      std::swap(header->layer_info.crop_x0, header->layer_info.crop_y0);
   2577    }
   2578    size_t o = (metadata.orientation - 1) & 3;
   2579    if (o > 0 && o < 3) {
   2580      header->layer_info.crop_x0 = W - xsize - header->layer_info.crop_x0;
   2581    }
   2582    if (o > 1) {
   2583      header->layer_info.crop_y0 = H - ysize - header->layer_info.crop_y0;
   2584    }
   2585  }
   2586  if (dec->coalescing) {
   2587    header->layer_info.blend_info.blendmode = JXL_BLEND_REPLACE;
   2588    header->layer_info.blend_info.source = 0;
   2589    header->layer_info.blend_info.alpha = 0;
   2590    header->layer_info.blend_info.clamp = JXL_FALSE;
   2591    header->layer_info.save_as_reference = 0;
   2592  } else {
   2593    header->layer_info.blend_info.blendmode =
   2594        static_cast<JxlBlendMode>(dec->frame_header->blending_info.mode);
   2595    header->layer_info.blend_info.source =
   2596        dec->frame_header->blending_info.source;
   2597    header->layer_info.blend_info.alpha =
   2598        dec->frame_header->blending_info.alpha_channel;
   2599    header->layer_info.blend_info.clamp =
   2600        TO_JXL_BOOL(dec->frame_header->blending_info.clamp);
   2601    header->layer_info.save_as_reference = dec->frame_header->save_as_reference;
   2602  }
   2603  return JXL_DEC_SUCCESS;
   2604 }
   2605 
   2606 JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(const JxlDecoder* dec,
   2607                                                    size_t index,
   2608                                                    JxlBlendInfo* blend_info) {
   2609  if (!dec->frame_header || dec->frame_stage == FrameStage::kHeader) {
   2610    return JXL_API_ERROR("no frame header available");
   2611  }
   2612  const auto& metadata = dec->metadata.m;
   2613  if (index >= metadata.num_extra_channels) {
   2614    return JXL_API_ERROR("Invalid extra channel index");
   2615  }
   2616  blend_info->blendmode = static_cast<JxlBlendMode>(
   2617      dec->frame_header->extra_channel_blending_info[index].mode);
   2618  blend_info->source =
   2619      dec->frame_header->extra_channel_blending_info[index].source;
   2620  blend_info->alpha =
   2621      dec->frame_header->extra_channel_blending_info[index].alpha_channel;
   2622  blend_info->clamp =
   2623      TO_JXL_BOOL(dec->frame_header->extra_channel_blending_info[index].clamp);
   2624  return JXL_DEC_SUCCESS;
   2625 }
   2626 
   2627 JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec, char* name,
   2628                                        size_t size) {
   2629  if (!dec->frame_header || dec->frame_stage == FrameStage::kHeader) {
   2630    return JXL_API_ERROR("no frame header available");
   2631  }
   2632  if (size < dec->frame_header->name.size() + 1) {
   2633    return JXL_API_ERROR("too small frame name output buffer");
   2634  }
   2635  memcpy(name, dec->frame_header->name.c_str(),
   2636         dec->frame_header->name.size() + 1);
   2637 
   2638  return JXL_DEC_SUCCESS;
   2639 }
   2640 
   2641 JxlDecoderStatus JxlDecoderSetPreferredColorProfile(
   2642    JxlDecoder* dec, const JxlColorEncoding* color_encoding) {
   2643  return JxlDecoderSetOutputColorProfile(dec, color_encoding,
   2644                                         /*icc_data=*/nullptr, /*icc_size=*/0);
   2645 }
   2646 
   2647 JxlDecoderStatus JxlDecoderSetOutputColorProfile(
   2648    JxlDecoder* dec, const JxlColorEncoding* color_encoding,
   2649    const uint8_t* icc_data, size_t icc_size) {
   2650  if ((color_encoding != nullptr) && (icc_data != nullptr)) {
   2651    return JXL_API_ERROR("cannot set both color_encoding and icc_data");
   2652  }
   2653  if ((color_encoding == nullptr) && (icc_data == nullptr)) {
   2654    return JXL_API_ERROR("one of color_encoding and icc_data must be set");
   2655  }
   2656  if (!dec->got_all_headers) {
   2657    return JXL_API_ERROR("color info not yet available");
   2658  }
   2659  if (dec->post_headers) {
   2660    return JXL_API_ERROR("too late to set the color encoding");
   2661  }
   2662  if ((!dec->passes_state->output_encoding_info.cms_set) &&
   2663      (icc_data != nullptr)) {
   2664    return JXL_API_ERROR(
   2665        "must set color management system via JxlDecoderSetCms");
   2666  }
   2667  auto& output_encoding = dec->passes_state->output_encoding_info;
   2668  if (color_encoding) {
   2669    if (dec->image_metadata.color_encoding.IsGray() &&
   2670        color_encoding->color_space != JXL_COLOR_SPACE_GRAY &&
   2671        dec->image_out_buffer_set && dec->image_out_format.num_channels < 3) {
   2672      return JXL_API_ERROR("Number of channels is too low for color output");
   2673    }
   2674    if (color_encoding->color_space == JXL_COLOR_SPACE_UNKNOWN) {
   2675      return JXL_API_ERROR("Unknown output colorspace");
   2676    }
   2677    jxl::ColorEncoding c_out;
   2678    JXL_API_RETURN_IF_ERROR(c_out.FromExternal(*color_encoding));
   2679    JXL_API_RETURN_IF_ERROR(!c_out.ICC().empty());
   2680    if (!c_out.SameColorEncoding(output_encoding.color_encoding)) {
   2681      JXL_API_RETURN_IF_ERROR(output_encoding.MaybeSetColorEncoding(c_out));
   2682      dec->image_metadata.color_encoding = output_encoding.color_encoding;
   2683    }
   2684    return JXL_DEC_SUCCESS;
   2685  }
   2686  // icc_data != nullptr
   2687  // TODO(firsching): implement setting output color profile from icc_data.
   2688  jxl::ColorEncoding c_dst;
   2689  std::vector<uint8_t> padded_icc;
   2690  padded_icc.assign(icc_data, icc_data + icc_size);
   2691  if (!c_dst.SetICC(std::move(padded_icc),
   2692                    &output_encoding.color_management_system)) {
   2693    return JXL_API_ERROR(
   2694        "setting output color profile from icc_data not yet implemented.");
   2695  }
   2696  JXL_API_RETURN_IF_ERROR(
   2697      static_cast<int>(output_encoding.MaybeSetColorEncoding(c_dst)));
   2698 
   2699  return JXL_DEC_SUCCESS;
   2700 }
   2701 
   2702 JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget(
   2703    JxlDecoder* dec, float desired_intensity_target) {
   2704  if (desired_intensity_target < 0) {
   2705    return JXL_API_ERROR("negative intensity target requested");
   2706  }
   2707  dec->desired_intensity_target = desired_intensity_target;
   2708  return JXL_DEC_SUCCESS;
   2709 }
   2710 
   2711 JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec, uint8_t* data,
   2712                                        size_t size) {
   2713  if (dec->box_out_buffer_set) {
   2714    return JXL_API_ERROR("must release box buffer before setting it again");
   2715  }
   2716  if (!dec->box_event) {
   2717    return JXL_API_ERROR("can only set box buffer after box event");
   2718  }
   2719 
   2720  dec->box_out_buffer_set = true;
   2721  dec->box_out_buffer_set_current_box = true;
   2722  dec->box_out_buffer = data;
   2723  dec->box_out_buffer_size = size;
   2724  dec->box_out_buffer_pos = 0;
   2725  return JXL_DEC_SUCCESS;
   2726 }
   2727 
   2728 size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec) {
   2729  if (!dec->box_out_buffer_set) {
   2730    return 0;
   2731  }
   2732  size_t result = dec->box_out_buffer_size - dec->box_out_buffer_pos;
   2733  dec->box_out_buffer_set = false;
   2734  dec->box_out_buffer = nullptr;
   2735  dec->box_out_buffer_size = 0;
   2736  if (!dec->box_out_buffer_set_current_box) {
   2737    dec->box_out_buffer_begin = 0;
   2738  } else {
   2739    dec->box_out_buffer_begin += dec->box_out_buffer_pos;
   2740  }
   2741  dec->box_out_buffer_set_current_box = false;
   2742  return result;
   2743 }
   2744 
   2745 JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
   2746                                              JXL_BOOL decompress) {
   2747  // TODO(lode): return error if libbrotli is not compiled in the jxl decoding
   2748  // library
   2749  dec->decompress_boxes = FROM_JXL_BOOL(decompress);
   2750  return JXL_DEC_SUCCESS;
   2751 }
   2752 
   2753 JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, JxlBoxType type,
   2754                                      JXL_BOOL decompressed) {
   2755  if (!dec->box_event) {
   2756    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
   2757  }
   2758  if (decompressed) {
   2759    memcpy(type, dec->box_decoded_type, sizeof(dec->box_decoded_type));
   2760  } else {
   2761    memcpy(type, dec->box_type, sizeof(dec->box_type));
   2762  }
   2763 
   2764  return JXL_DEC_SUCCESS;
   2765 }
   2766 
   2767 JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
   2768                                         uint64_t* size) {
   2769  if (!dec->box_event) {
   2770    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
   2771  }
   2772  if (size) {
   2773    *size = dec->box_size;
   2774  }
   2775  return JXL_DEC_SUCCESS;
   2776 }
   2777 
   2778 JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec,
   2779                                              uint64_t* size) {
   2780  if (!dec->box_event) {
   2781    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
   2782  }
   2783  if (size) {
   2784    *size = dec->box_contents_size;
   2785  }
   2786  return JXL_DEC_SUCCESS;
   2787 }
   2788 
   2789 JxlDecoderStatus JxlDecoderSetProgressiveDetail(JxlDecoder* dec,
   2790                                                JxlProgressiveDetail detail) {
   2791  if (detail != kDC && detail != kLastPasses && detail != kPasses) {
   2792    return JXL_API_ERROR(
   2793        "Values other than kDC (%d), kLastPasses (%d) and kPasses (%d), "
   2794        "like %d are not implemented.",
   2795        kDC, kLastPasses, kPasses, detail);
   2796  }
   2797  dec->prog_detail = detail;
   2798  return JXL_DEC_SUCCESS;
   2799 }
   2800 
   2801 namespace {
   2802 
   2803 template <typename T>
   2804 JxlDecoderStatus VerifyOutputBitDepth(JxlBitDepth bit_depth, const T& metadata,
   2805                                      JxlPixelFormat format) {
   2806  uint32_t bits_per_sample = GetBitDepth(bit_depth, metadata, format);
   2807  if (bits_per_sample == 0) return JXL_API_ERROR("Invalid output bit depth");
   2808  if (format.data_type == JXL_TYPE_UINT8 && bits_per_sample > 8) {
   2809    return JXL_API_ERROR("Invalid bit depth %u for uint8 output",
   2810                         bits_per_sample);
   2811  } else if (format.data_type == JXL_TYPE_UINT16 && bits_per_sample > 16) {
   2812    return JXL_API_ERROR("Invalid bit depth %u for uint16 output",
   2813                         bits_per_sample);
   2814  }
   2815  return JXL_DEC_SUCCESS;
   2816 }
   2817 
   2818 }  // namespace
   2819 
   2820 JxlDecoderStatus JxlDecoderSetImageOutBitDepth(JxlDecoder* dec,
   2821                                               const JxlBitDepth* bit_depth) {
   2822  if (!dec->image_out_buffer_set) {
   2823    return JXL_API_ERROR("No image out buffer was set.");
   2824  }
   2825  JXL_API_RETURN_IF_ERROR(
   2826      VerifyOutputBitDepth(*bit_depth, dec->metadata.m, dec->image_out_format));
   2827  dec->image_out_bit_depth = *bit_depth;
   2828  return JXL_DEC_SUCCESS;
   2829 }