libjxl

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

decode.cc (105995B)


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