Coverage Report

Created: 2026-10-27 06:57

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