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