Coverage Report

Created: 2025-08-11 08:01

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