Coverage Report

Created: 2025-09-08 07:52

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