Coverage Report

Created: 2026-06-30 07:12

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