Coverage Report

Created: 2026-11-12 07:35

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