Coverage Report

Created: 2026-02-14 07:09

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