Coverage Report

Created: 2026-06-14 06:57

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