Coverage Report

Created: 2025-07-16 07:53

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