Coverage Report

Created: 2026-06-16 07:20

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
539k
bool OutOfBounds(size_t a, size_t b, size_t size) {
62
539k
  size_t pos = a + b;
63
539k
  if (pos > size) return true;
64
496k
  if (pos < a) return true;  // overflow happened
65
496k
  return false;
66
496k
}
67
68
521k
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
521k
  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
521k
  const size_t max_codestream_basic_info_size = 50;
82
83
521k
  return container_header_size + max_codestream_basic_info_size;
84
521k
}
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
294k
  (((JXL_IS_DEBUG_BUILD) &&                                                    \
95
294k
    ::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, ##__VA_ARGS__)), \
96
294k
   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
294k
#define JXL_INPUT_ERROR(format, ...) JXL_API_ERROR(format, ##__VA_ARGS__)
102
103
3.68M
JxlDecoderStatus ConvertStatus(JxlDecoderStatus status) { return status; }
104
105
2.04M
JxlDecoderStatus ConvertStatus(jxl::Status status) {
106
2.04M
  return status ? JXL_DEC_SUCCESS : JXL_DEC_ERROR;
107
2.04M
}
108
109
#define JXL_API_RETURN_IF_ERROR(expr)               \
110
5.72M
  {                                                 \
111
5.72M
    JxlDecoderStatus status_ = ConvertStatus(expr); \
112
5.72M
    if (status_ != JXL_DEC_SUCCESS) return status_; \
113
5.72M
  }
114
115
519k
JxlSignature ReadSignature(const uint8_t* buf, size_t len, size_t* pos) {
116
519k
  if (*pos >= len) return JXL_SIG_NOT_ENOUGH_BYTES;
117
118
519k
  buf += *pos;
119
519k
  len -= *pos;
120
121
  // JPEG XL codestream: 0xff 0x0a
122
519k
  if (len >= 1 && buf[0] == 0xff) {
123
512k
    if (len < 2) {
124
1
      return JXL_SIG_NOT_ENOUGH_BYTES;
125
512k
    } else if (buf[1] == jxl::kCodestreamMarker) {
126
512k
      *pos += 2;
127
512k
      return JXL_SIG_CODESTREAM;
128
512k
    } else {
129
4
      return JXL_SIG_INVALID;
130
4
    }
131
512k
  }
132
133
  // JPEG XL container
134
7.09k
  if (len >= 1 && buf[0] == 0) {
135
7.08k
    if (len < jxl::kJxlSignatureBox.size()) {
136
2
      return JXL_SIG_NOT_ENOUGH_BYTES;
137
7.08k
    } else if (memcmp(buf, jxl::kJxlSignatureBox.data(),
138
7.08k
                      jxl::kJxlSignatureBox.size()) == 0) {
139
7.06k
      *pos += jxl::kJxlSignatureBox.size();
140
7.06k
      return JXL_SIG_CONTAINER;
141
7.06k
    } else {
142
15
      return JXL_SIG_INVALID;
143
15
    }
144
7.08k
  }
145
146
12
  return JXL_SIG_INVALID;
147
7.09k
}
148
149
}  // namespace
150
151
6
uint32_t JxlDecoderVersion(void) {
152
6
  return JPEGXL_MAJOR_VERSION * 1000000 + JPEGXL_MINOR_VERSION * 1000 +
153
6
         JPEGXL_PATCH_VERSION;
154
6
}
155
156
519k
JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len) {
157
519k
  size_t pos = 0;
158
519k
  return ReadSignature(buf, len, &pos);
159
519k
}
160
161
namespace {
162
163
235k
size_t BitsPerChannel(JxlDataType data_type) {
164
235k
  switch (data_type) {
165
221k
    case JXL_TYPE_UINT8:
166
221k
      return 8;
167
14.1k
    case JXL_TYPE_UINT16:
168
14.1k
      return 16;
169
396
    case JXL_TYPE_FLOAT:
170
396
      return 32;
171
0
    case JXL_TYPE_FLOAT16:
172
0
      return 16;
173
0
    default:
174
0
      return 0;  // signals unhandled JxlDataType
175
235k
  }
176
235k
}
177
178
template <typename T>
179
uint32_t GetBitDepth(JxlBitDepth bit_depth, const T& metadata,
180
78.8k
                     JxlPixelFormat format) {
181
78.8k
  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
182
78.8k
    return BitsPerChannel(format.data_type);
183
78.8k
  } 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
78.8k
}
decode.cc:unsigned int (anonymous namespace)::GetBitDepth<jxl::ImageMetadata>(JxlBitDepth, jxl::ImageMetadata const&, JxlPixelFormat)
Line
Count
Source
180
78.8k
                     JxlPixelFormat format) {
181
78.8k
  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
182
78.8k
    return BitsPerChannel(format.data_type);
183
78.8k
  } 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
78.8k
}
Unexecuted instantiation: decode.cc:unsigned int (anonymous namespace)::GetBitDepth<jxl::ExtraChannelInfo>(JxlBitDepth, jxl::ExtraChannelInfo const&, JxlPixelFormat)
190
191
enum class DecoderStage : uint32_t {
192
  kInited,              // Decoder created, no JxlDecoderProcessInput called yet
193
  kStarted,             // Running JxlDecoderProcessInput calls
194
  kCodestreamFinished,  // Codestream done, but other boxes could still occur.
195
                        // This stage can also occur before having seen the
196
                        // entire codestream if the user didn't subscribe to any
197
                        // codestream events at all, e.g. only to box events,
198
                        // or, the user only subscribed to basic info, and only
199
                        // the header of the codestream was parsed.
200
  kError,               // Error occurred, decoder object no longer usable
201
};
202
203
enum class FrameStage : uint32_t {
204
  kHeader,  // Must parse frame header.
205
  kTOC,     // Must parse TOC
206
  kFull,    // Must parse full pixels
207
};
208
209
enum class BoxStage : uint32_t {
210
  kHeader,      // Parsing box header of the next box, or start of non-container
211
                // stream
212
  kFtyp,        // The ftyp box
213
  kSkip,        // Box whose contents are skipped
214
  kCodestream,  // Handling codestream box contents, or non-container stream
215
  kPartialCodestream,  // Handling the extra header of partial codestream box
216
  kBufferingJxlp,      // Reading an out-of-order jxlp box payload into buffer
217
  kJpegRecon,          // Handling jpeg reconstruction box
218
};
219
220
enum class JpegReconStage : uint32_t {
221
  kNone,             // Not outputting
222
  kSettingMetadata,  // Ready to output, must set metadata to the jpeg_data
223
  kOutputting,       // Currently outputting the JPEG bytes
224
};
225
226
// For each internal frame, which storage locations it references, and which
227
// storage locations it is stored in, using the bit mask as defined in
228
// FrameDecoder::References and FrameDecoder::SaveAs.
229
struct FrameRef {
230
  int reference;
231
  int saved_as;
232
};
233
234
/*
235
Given list of frame references to storage slots, and storage slots in which this
236
frame is saved, computes which frames are required to decode the frame at the
237
given index and any frames after it. The frames on which this depends are
238
returned as a vector of their indices, in no particular order. The given index
239
must be smaller than saved_as.size(), and references.size() must equal
240
saved_as.size(). Any frames beyond saved_as and references are considered
241
unknown future frames and must be treated as if something depends on them.
242
*/
243
std::vector<size_t> GetFrameDependencies(size_t index,
244
0
                                         const std::vector<FrameRef>& refs) {
245
0
  JXL_DASSERT(index < refs.size());
246
247
0
  std::vector<size_t> result;
248
249
0
  constexpr size_t kNumStorage = 8;
250
251
  // value which indicates nothing is stored in this storage slot
252
0
  const size_t invalid = refs.size();
253
  // for each of the 8 storage slots, a vector that translates frame index to
254
  // frame stored in this storage slot at this point, that is, the last
255
  // frame that was stored in this slot before or at this index.
256
0
  std::array<std::vector<size_t>, kNumStorage> storage;
257
0
  for (size_t s = 0; s < kNumStorage; ++s) {
258
0
    storage[s].resize(refs.size());
259
0
    int mask = 1 << s;
260
0
    size_t id = invalid;
261
0
    for (size_t i = 0; i < refs.size(); ++i) {
262
0
      if (refs[i].saved_as & mask) {
263
0
        id = i;
264
0
      }
265
0
      storage[s][i] = id;
266
0
    }
267
0
  }
268
269
0
  std::vector<char> seen(index + 1, 0);
270
0
  std::vector<size_t> stack;
271
0
  stack.push_back(index);
272
0
  seen[index] = 1;
273
274
  // For frames after index, assume they can depend on any of the 8 storage
275
  // slots, so push the frame for each stored reference to the stack and result.
276
  // All frames after index are treated as having unknown references and with
277
  // the possibility that there are more frames after the last known.
278
  // TODO(lode): take values of saved_as and references after index, and a
279
  // input flag indicating if they are all frames of the image, to further
280
  // optimize this.
281
0
  for (size_t s = 0; s < kNumStorage; ++s) {
282
0
    size_t frame_ref = storage[s][index];
283
0
    if (frame_ref == invalid) continue;
284
0
    if (seen[frame_ref]) continue;
285
0
    stack.push_back(frame_ref);
286
0
    seen[frame_ref] = 1;
287
0
    result.push_back(frame_ref);
288
0
  }
289
290
0
  while (!stack.empty()) {
291
0
    size_t frame_index = stack.back();
292
0
    stack.pop_back();
293
0
    if (frame_index == 0) continue;  // first frame cannot have references
294
0
    for (size_t s = 0; s < kNumStorage; ++s) {
295
0
      int mask = 1 << s;
296
0
      if (!(refs[frame_index].reference & mask)) continue;
297
0
      size_t frame_ref = storage[s][frame_index - 1];
298
0
      if (frame_ref == invalid) continue;
299
0
      if (seen[frame_ref]) continue;
300
0
      stack.push_back(frame_ref);
301
0
      seen[frame_ref] = 1;
302
0
      result.push_back(frame_ref);
303
0
    }
304
0
  }
305
306
0
  return result;
307
0
}
308
309
// Parameters for user-requested extra channel output.
310
struct ExtraChannelOutput {
311
  JxlPixelFormat format;
312
  void* buffer;
313
  size_t buffer_size;
314
};
315
316
}  // namespace
317
318
namespace jxl {
319
320
struct JxlDecoderFrameIndexBoxEntry {
321
  // OFFi: offset of start byte of this frame compared to start
322
  // byte of previous frame from this index in the JPEG XL codestream. For the
323
  // first frame, this is the offset from the first byte of the JPEG XL
324
  // codestream.
325
  uint64_t OFFi;
326
  // Ti: duration in ticks between the start of this frame and
327
  // the start of the next frame in the index. If this is the last frame in the
328
  // index, this is the duration in ticks between the start of this frame and
329
  // the end of the stream. A tick lasts TNUM / TDEN seconds.
330
  uint32_t Ti;
331
  // Fi: amount of frames the next frame in the index occurs
332
  // after this frame. If this is the last frame in the index, this is the
333
  // amount of frames after this frame in the remainder of the stream. Only
334
  // frames that are presented by the decoder are counted for this purpose, this
335
  // excludes frames that are not intended for display but for compositing with
336
  // other frames, such as frames that aren't the last frame with a duration of
337
  // 0 ticks.
338
  uint32_t Fi;
339
};
340
341
struct JxlDecoderFrameIndexBox {
342
0
  int64_t NF() const { return entries.size(); }
343
  int32_t TNUM = 1;
344
  int32_t TDEN = 1000;
345
346
  std::vector<JxlDecoderFrameIndexBoxEntry> entries;
347
348
  // That way we can ensure that every index box will have the first frame.
349
  // If the API user decides to mark it as an indexed frame, we call
350
  // the AddFrame again, this time with requested.
351
0
  void AddFrame(uint64_t OFFi, uint32_t Ti, uint32_t Fi) {
352
0
    JxlDecoderFrameIndexBoxEntry e;
353
0
    e.OFFi = OFFi;
354
0
    e.Ti = Ti;
355
0
    e.Fi = Fi;
356
0
    entries.push_back(e);
357
0
  }
358
};
359
360
}  // namespace jxl
361
362
// NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
363
struct JxlDecoder {
364
519k
  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
70
  bool InjectNextBufferedJxlpBox() {
560
70
    auto it = jxlp_ooo_buffer.find(next_jxlp_index);
561
70
    if (it == jxlp_ooo_buffer.end()) return false;
562
0
    auto& [data, is_last] = it->second;
563
0
    codestream_copy.insert(codestream_copy.end(), data.begin(), data.end());
564
0
    if (is_last) last_codestream_seen = true;
565
0
    next_jxlp_index++;
566
0
    jxlp_ooo_buffer.erase(it);
567
0
    return true;
568
70
  }
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
23.9k
  bool JbrdNeedMoreBoxes() const {
589
    // jbrd box wants exif but exif box not yet seen
590
23.9k
    if (store_exif < 2 && recon_exif_size > 0) return true;
591
    // jbrd box wants xmp but xmp box not yet seen
592
23.9k
    if (store_xmp < 2 && recon_xmp_size > 0) return true;
593
23.9k
    return false;
594
23.9k
  }
595
#endif
596
597
  const uint8_t* next_in;
598
  size_t avail_in;
599
  bool input_closed;
600
601
3.04M
  void AdvanceInput(size_t size) {
602
3.04M
    JXL_DASSERT(avail_in >= size);
603
3.04M
    next_in += size;
604
3.04M
    avail_in -= size;
605
3.04M
    file_pos += size;
606
3.04M
  }
607
608
4.01M
  size_t AvailableCodestream() const {
609
4.01M
    size_t avail_codestream = avail_in;
610
4.01M
    if (!box_contents_unbounded) {
611
1.85k
      avail_codestream =
612
1.85k
          std::min<size_t>(avail_codestream, box_contents_end - file_pos);
613
1.85k
    }
614
4.01M
    return avail_codestream;
615
4.01M
  }
616
617
1.76M
  void AdvanceCodestream(size_t size) {
618
1.76M
    size_t avail_codestream = AvailableCodestream();
619
1.76M
    if (codestream_copy.empty()) {
620
1.76M
      if (size <= avail_codestream) {
621
1.76M
        AdvanceInput(size);
622
1.76M
      } else {
623
155
        codestream_pos = size - avail_codestream;
624
155
        AdvanceInput(avail_codestream);
625
155
      }
626
1.76M
    } else {
627
605
      codestream_pos += size;
628
605
      if (codestream_pos + codestream_unconsumed >= codestream_copy.size()) {
629
291
        size_t advance = std::min(
630
291
            codestream_unconsumed,
631
291
            codestream_unconsumed + codestream_pos - codestream_copy.size());
632
291
        AdvanceInput(advance);
633
291
        codestream_pos -= std::min(codestream_pos, codestream_copy.size());
634
291
        codestream_unconsumed = 0;
635
291
        codestream_copy.clear();
636
291
      }
637
605
    }
638
1.76M
  }
639
640
103k
  JxlDecoderStatus RequestMoreInput() {
641
103k
    if (codestream_copy.empty()) {
642
102k
      size_t avail_codestream = AvailableCodestream();
643
102k
      codestream_copy.insert(codestream_copy.end(), next_in,
644
102k
                             next_in + avail_codestream);
645
102k
      AdvanceInput(avail_codestream);
646
102k
    } else {
647
1.14k
      AdvanceInput(codestream_unconsumed);
648
1.14k
      codestream_unconsumed = 0;
649
1.14k
    }
650
103k
    return JXL_DEC_NEED_MORE_INPUT;
651
103k
  }
652
653
2.14M
  JxlDecoderStatus GetCodestreamInput(jxl::Span<const uint8_t>* span) {
654
2.14M
    if (codestream_copy.empty() && codestream_pos > 0) {
655
213
      size_t avail_codestream = AvailableCodestream();
656
213
      size_t skip = std::min<size_t>(codestream_pos, avail_codestream);
657
213
      AdvanceInput(skip);
658
213
      codestream_pos -= skip;
659
213
      if (codestream_pos > 0) {
660
213
        return RequestMoreInput();
661
213
      }
662
213
    }
663
2.14M
    if (codestream_pos > codestream_copy.size()) {
664
0
      return JXL_API_ERROR("Internal: codestream_pos > codestream_copy.size()");
665
0
    }
666
2.14M
    if (codestream_unconsumed > codestream_copy.size()) {
667
0
      return JXL_API_ERROR(
668
0
          "Internal: codestream_unconsumed > codestream_copy.size()");
669
0
    }
670
2.14M
    size_t avail_codestream = AvailableCodestream();
671
2.14M
    if (codestream_copy.empty()) {
672
2.13M
      if (avail_codestream == 0) {
673
2.78k
        return RequestMoreInput();
674
2.78k
      }
675
2.13M
      *span = jxl::Bytes(next_in, avail_codestream);
676
2.13M
      return JXL_DEC_SUCCESS;
677
2.13M
    } else {
678
1.49k
      codestream_copy.insert(codestream_copy.end(),
679
1.49k
                             next_in + codestream_unconsumed,
680
1.49k
                             next_in + avail_codestream);
681
1.49k
      codestream_unconsumed = avail_codestream;
682
1.49k
      *span = jxl::Bytes(codestream_copy.data() + codestream_pos,
683
1.49k
                         codestream_copy.size() - codestream_pos);
684
1.49k
      return JXL_DEC_SUCCESS;
685
1.49k
    }
686
2.14M
  }
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
11.9k
  bool CanUseMoreCodestreamInput() const {
694
    // The decoder can set this to finished early if all relevant events were
695
    // processed, so this check works.
696
11.9k
    return stage != DecoderStage::kCodestreamFinished;
697
11.9k
  }
698
};
699
700
namespace {
701
702
633k
bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
703
633k
  if (xsize == 0 || ysize == 0) return true;
704
633k
  size_t padded_xsize = jxl::DivCeil(xsize, 32) * 32;
705
633k
  if (padded_xsize < xsize) return false;  // overflow
706
633k
  size_t num_pixels = padded_xsize * ysize;
707
633k
  if (num_pixels / padded_xsize != ysize) return false;  // overflow
708
633k
  return true;
709
633k
}
710
711
}  // namespace
712
713
// Resets the state that must be reset for both Rewind and Reset
714
519k
void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
715
519k
  dec->stage = DecoderStage::kInited;
716
519k
  dec->got_signature = false;
717
519k
  dec->last_codestream_seen = false;
718
519k
  dec->got_codestream_signature = false;
719
519k
  dec->got_basic_info = false;
720
519k
  dec->got_transform_data = false;
721
519k
  dec->got_all_headers = false;
722
519k
  dec->post_headers = false;
723
519k
  if (dec->icc_reader) dec->icc_reader->Reset();
724
519k
  dec->got_preview_image = false;
725
519k
  dec->preview_frame = false;
726
519k
  dec->file_pos = 0;
727
519k
  dec->box_contents_begin = 0;
728
519k
  dec->box_contents_end = 0;
729
519k
  dec->box_contents_size = 0;
730
519k
  dec->box_size = 0;
731
519k
  dec->header_size = 0;
732
519k
  dec->box_contents_unbounded = false;
733
519k
  memset(dec->box_type, 0, sizeof(dec->box_type));
734
519k
  memset(dec->box_decoded_type, 0, sizeof(dec->box_decoded_type));
735
519k
  dec->box_event = false;
736
519k
  dec->box_stage = BoxStage::kHeader;
737
519k
  dec->jxl_file_format_version = 0;
738
519k
  dec->next_jxlp_index = 0;
739
519k
  dec->jxlp_ooo_buffer.clear();
740
519k
  dec->buffering_jxlp_index = 0;
741
519k
  dec->buffering_jxlp_is_last = false;
742
519k
  dec->box_out_buffer_set = false;
743
519k
  dec->box_out_buffer_set_current_box = false;
744
519k
  dec->box_out_buffer = nullptr;
745
519k
  dec->box_out_buffer_size = 0;
746
519k
  dec->box_out_buffer_begin = 0;
747
519k
  dec->box_out_buffer_pos = 0;
748
749
519k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
750
519k
  dec->exif_metadata.clear();
751
519k
  dec->xmp_metadata.clear();
752
519k
  dec->store_exif = 0;
753
519k
  dec->store_xmp = 0;
754
519k
  dec->recon_out_buffer_pos = 0;
755
519k
  dec->recon_exif_size = 0;
756
519k
  dec->recon_xmp_size = 0;
757
519k
  dec->recon_output_jpeg = JpegReconStage::kNone;
758
519k
#endif
759
760
519k
  dec->events_wanted = dec->orig_events_wanted;
761
519k
  dec->basic_info_size_hint = InitialBasicInfoSizeHint();
762
519k
  dec->have_container = false;
763
519k
  dec->box_count = 0;
764
519k
  dec->downsampling_target = 8;
765
519k
  dec->image_out_buffer_set = false;
766
519k
  dec->image_out_buffer = nullptr;
767
519k
  dec->image_out_init_callback = nullptr;
768
519k
  dec->image_out_run_callback = nullptr;
769
519k
  dec->image_out_destroy_callback = nullptr;
770
519k
  dec->image_out_init_opaque = nullptr;
771
519k
  dec->image_out_size = 0;
772
519k
  dec->image_out_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT;
773
519k
  dec->extra_channel_output.clear();
774
519k
  dec->next_in = nullptr;
775
519k
  dec->avail_in = 0;
776
519k
  dec->input_closed = false;
777
778
519k
  dec->passes_state.reset();
779
519k
  dec->frame_dec.reset();
780
519k
  dec->next_section = 0;
781
519k
  dec->section_processed.clear();
782
783
519k
  dec->ib.reset();
784
519k
  dec->metadata = jxl::CodecMetadata();
785
519k
  dec->image_metadata = dec->metadata.m;
786
519k
  dec->frame_header = jxl::make_unique<jxl::FrameHeader>(&dec->metadata);
787
788
519k
  dec->codestream_copy.clear();
789
519k
  dec->codestream_unconsumed = 0;
790
519k
  dec->codestream_pos = 0;
791
519k
  dec->codestream_bits_ahead = 0;
792
793
519k
  dec->frame_stage = FrameStage::kHeader;
794
519k
  dec->remaining_frame_size = 0;
795
519k
  dec->is_last_of_still = false;
796
519k
  dec->is_last_total = false;
797
519k
  dec->skip_frames = 0;
798
519k
  dec->skipping_frame = false;
799
519k
  dec->internal_frames = 0;
800
519k
  dec->external_frames = 0;
801
519k
}
802
803
519k
void JxlDecoderReset(JxlDecoder* dec) {
804
519k
  JxlDecoderRewindDecodingState(dec);
805
806
519k
  dec->thread_pool.reset();
807
519k
  dec->keep_orientation = false;
808
519k
  dec->unpremul_alpha = false;
809
519k
  dec->render_spotcolors = true;
810
519k
  dec->coalescing = true;
811
519k
  dec->desired_intensity_target = 0;
812
519k
  dec->orig_events_wanted = 0;
813
519k
  dec->events_wanted = 0;
814
519k
  dec->frame_refs.clear();
815
519k
  dec->frame_external_to_internal.clear();
816
519k
  dec->frame_required.clear();
817
519k
  dec->decompress_boxes = false;
818
519k
}
819
820
519k
JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
821
519k
  JxlMemoryManager local_memory_manager;
822
519k
  if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager))
823
0
    return nullptr;
824
825
519k
  void* alloc =
826
519k
      jxl::MemoryManagerAlloc(&local_memory_manager, sizeof(JxlDecoder));
827
519k
  if (!alloc) return nullptr;
828
  // Placement new constructor on allocated memory
829
519k
  JxlDecoder* dec = new (alloc) JxlDecoder();
830
519k
  dec->memory_manager = local_memory_manager;
831
832
519k
  JxlDecoderReset(dec);
833
834
519k
  return dec;
835
519k
}
836
837
519k
void JxlDecoderDestroy(JxlDecoder* dec) {
838
519k
  if (dec) {
839
519k
    JxlMemoryManager local_memory_manager = dec->memory_manager;
840
    // Call destructor directly since custom free function is used.
841
519k
    dec->~JxlDecoder();
842
519k
    jxl::MemoryManagerFree(&local_memory_manager, dec);
843
519k
  }
844
519k
}
845
846
0
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
519k
                            void* parallel_runner_opaque) {
896
519k
  if (dec->stage != DecoderStage::kInited) {
897
0
    return JXL_API_ERROR(
898
0
        "JxlDecoderSetParallelRunner must be called before starting");
899
0
  }
900
519k
  dec->thread_pool = jxl::make_unique<jxl::ThreadPool>(parallel_runner,
901
519k
                                                       parallel_runner_opaque);
902
519k
  return JXL_DEC_SUCCESS;
903
519k
}
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
519k
JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, int events_wanted) {
911
519k
  if (dec->stage != DecoderStage::kInited) {
912
0
    return JXL_DEC_ERROR;  // Cannot subscribe to events after having started.
913
0
  }
914
519k
  if (events_wanted & 63) {
915
0
    return JXL_DEC_ERROR;  // Can only subscribe to informative events.
916
0
  }
917
519k
  dec->events_wanted = events_wanted;
918
519k
  dec->orig_events_wanted = events_wanted;
919
519k
  return JXL_DEC_SUCCESS;
920
519k
}
921
922
JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
923
519k
                                              JXL_BOOL skip_reorientation) {
924
519k
  if (dec->stage != DecoderStage::kInited) {
925
0
    return JXL_API_ERROR("Must set keep_orientation option before starting");
926
0
  }
927
519k
  dec->keep_orientation = FROM_JXL_BOOL(skip_reorientation);
928
519k
  return JXL_DEC_SUCCESS;
929
519k
}
930
931
JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec,
932
519k
                                                 JXL_BOOL unpremul_alpha) {
933
519k
  if (dec->stage != DecoderStage::kInited) {
934
0
    return JXL_API_ERROR("Must set unpremul_alpha option before starting");
935
0
  }
936
519k
  dec->unpremul_alpha = FROM_JXL_BOOL(unpremul_alpha);
937
519k
  return JXL_DEC_SUCCESS;
938
519k
}
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
235k
void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize) {
960
235k
  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
235k
  xsize = dec->metadata.oriented_xsize(dec->keep_orientation);
966
235k
  ysize = dec->metadata.oriented_ysize(dec->keep_orientation);
967
235k
  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
235k
}
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
1.33M
                            BitReader* reader, T* JXL_RESTRICT t) {
987
  // Use a copy of the bit reader because CanRead advances bits.
988
1.33M
  BitReader reader2(data);
989
1.33M
  reader2.SkipBits(reader->TotalBitsConsumed());
990
1.33M
  bool can_read = Bundle::CanRead(&reader2, t);
991
1.33M
  JXL_API_RETURN_IF_ERROR(reader2.Close());
992
993
1.33M
  if (!can_read) {
994
39.7k
    return dec->RequestMoreInput();
995
39.7k
  }
996
1.29M
  if (!Bundle::Read(reader, t)) {
997
56.4k
    return JXL_DEC_ERROR;
998
56.4k
  }
999
1.23M
  return JXL_DEC_SUCCESS;
1000
1.29M
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::SizeHeader>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::SizeHeader*)
Line
Count
Source
986
512k
                            BitReader* reader, T* JXL_RESTRICT t) {
987
  // Use a copy of the bit reader because CanRead advances bits.
988
512k
  BitReader reader2(data);
989
512k
  reader2.SkipBits(reader->TotalBitsConsumed());
990
512k
  bool can_read = Bundle::CanRead(&reader2, t);
991
512k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
992
993
512k
  if (!can_read) {
994
322
    return dec->RequestMoreInput();
995
322
  }
996
512k
  if (!Bundle::Read(reader, t)) {
997
0
    return JXL_DEC_ERROR;
998
0
  }
999
512k
  return JXL_DEC_SUCCESS;
1000
512k
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::ImageMetadata>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::ImageMetadata*)
Line
Count
Source
986
512k
                            BitReader* reader, T* JXL_RESTRICT t) {
987
  // Use a copy of the bit reader because CanRead advances bits.
988
512k
  BitReader reader2(data);
989
512k
  reader2.SkipBits(reader->TotalBitsConsumed());
990
512k
  bool can_read = Bundle::CanRead(&reader2, t);
991
512k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
992
993
512k
  if (!can_read) {
994
35.6k
    return dec->RequestMoreInput();
995
35.6k
  }
996
476k
  if (!Bundle::Read(reader, t)) {
997
56.1k
    return JXL_DEC_ERROR;
998
56.1k
  }
999
420k
  return JXL_DEC_SUCCESS;
1000
476k
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::CustomTransformData>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::CustomTransformData*)
Line
Count
Source
986
307k
                            BitReader* reader, T* JXL_RESTRICT t) {
987
  // Use a copy of the bit reader because CanRead advances bits.
988
307k
  BitReader reader2(data);
989
307k
  reader2.SkipBits(reader->TotalBitsConsumed());
990
307k
  bool can_read = Bundle::CanRead(&reader2, t);
991
307k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
992
993
307k
  if (!can_read) {
994
3.74k
    return dec->RequestMoreInput();
995
3.74k
  }
996
303k
  if (!Bundle::Read(reader, t)) {
997
245
    return JXL_DEC_ERROR;
998
245
  }
999
303k
  return JXL_DEC_SUCCESS;
1000
303k
}
1001
1002
std::unique_ptr<BitReader, std::function<void(BitReader*)>> GetBitReader(
1003
1.41M
    Span<const uint8_t> span) {
1004
1.41M
  BitReader* reader = new BitReader(span);
1005
1.41M
  return std::unique_ptr<BitReader, std::function<void(BitReader*)>>(
1006
1.41M
      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
1.41M
        (void)reader->AllReadsWithinBounds();
1012
1.41M
        (void)reader->Close();
1013
1.41M
        delete reader;
1014
1.41M
      });
1015
1.41M
}
1016
1017
513k
JxlDecoderStatus JxlDecoderReadBasicInfo(JxlDecoder* dec) {
1018
513k
  if (!dec->got_codestream_signature) {
1019
    // Check and skip the codestream signature
1020
512k
    Span<const uint8_t> span;
1021
512k
    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1022
512k
    if (span.size() < 2) {
1023
7
      return dec->RequestMoreInput();
1024
7
    }
1025
512k
    if (span.data()[0] != 0xff || span.data()[1] != jxl::kCodestreamMarker) {
1026
7
      return JXL_INPUT_ERROR("invalid signature");
1027
7
    }
1028
512k
    dec->got_codestream_signature = true;
1029
512k
    dec->AdvanceCodestream(2);
1030
512k
  }
1031
1032
513k
  Span<const uint8_t> span;
1033
513k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1034
512k
  auto reader = GetBitReader(span);
1035
512k
  JXL_API_RETURN_IF_ERROR(
1036
512k
      ReadBundle(dec, span, reader.get(), &dec->metadata.size));
1037
512k
  JXL_API_RETURN_IF_ERROR(
1038
512k
      ReadBundle(dec, span, reader.get(), &dec->metadata.m));
1039
420k
  size_t total_bits = reader->TotalBitsConsumed();
1040
420k
  dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
1041
420k
  dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
1042
420k
  dec->got_basic_info = true;
1043
420k
  dec->basic_info_size_hint = 0;
1044
420k
  dec->image_metadata = dec->metadata.m;
1045
420k
  JXL_DEBUG_V(2, "Decoded BasicInfo: %s", dec->metadata.DebugString().c_str());
1046
1047
420k
  if (!CheckSizeLimit(dec, dec->metadata.size.xsize(),
1048
420k
                      dec->metadata.size.ysize())) {
1049
0
    return JXL_INPUT_ERROR("image is too large");
1050
0
  }
1051
1052
420k
  return JXL_DEC_SUCCESS;
1053
420k
}
1054
1055
// Reads all codestream headers (but not frame headers)
1056
307k
JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) {
1057
307k
  if (!dec->got_transform_data) {
1058
307k
    Span<const uint8_t> span;
1059
307k
    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1060
307k
    auto reader = GetBitReader(span);
1061
307k
    reader->SkipBits(dec->codestream_bits_ahead);
1062
307k
    dec->metadata.transform_data.nonserialized_xyb_encoded =
1063
307k
        dec->metadata.m.xyb_encoded;
1064
307k
    JXL_API_RETURN_IF_ERROR(
1065
307k
        ReadBundle(dec, span, reader.get(), &dec->metadata.transform_data));
1066
303k
    size_t total_bits = reader->TotalBitsConsumed();
1067
303k
    dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
1068
303k
    dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
1069
303k
    dec->got_transform_data = true;
1070
303k
  }
1071
1072
303k
  Span<const uint8_t> span;
1073
303k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1074
303k
  auto reader = GetBitReader(span);
1075
303k
  reader->SkipBits(dec->codestream_bits_ahead);
1076
1077
303k
  if (dec->metadata.m.color_encoding.WantICC()) {
1078
52.6k
    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
52.6k
    if (!reader->AllReadsWithinBounds() ||
1084
44.1k
        status.code() == StatusCode::kNotEnoughBytes) {
1085
8.51k
      return dec->RequestMoreInput();
1086
8.51k
    }
1087
44.1k
    if (!status) {
1088
      // Other non-successful status is an error
1089
25.7k
      return JXL_DEC_ERROR;
1090
25.7k
    }
1091
18.4k
    PaddedBytes decoded_icc{&dec->memory_manager};
1092
18.4k
    status = dec->icc_reader->Process(reader.get(), &decoded_icc);
1093
18.4k
    if (status.code() == StatusCode::kNotEnoughBytes) {
1094
4.15k
      return dec->RequestMoreInput();
1095
4.15k
    }
1096
14.2k
    if (!status) {
1097
      // Other non-successful status is an error
1098
14.1k
      return JXL_DEC_ERROR;
1099
14.1k
    }
1100
100
    if (decoded_icc.empty()) {
1101
6
      return JXL_DEC_ERROR;
1102
6
    }
1103
94
    IccBytes icc;
1104
94
    Bytes(decoded_icc).AppendTo(icc);
1105
94
    dec->metadata.m.color_encoding.SetICCRaw(std::move(icc));
1106
94
  }
1107
1108
251k
  dec->got_all_headers = true;
1109
251k
  JXL_API_RETURN_IF_ERROR(reader->JumpToByteBoundary());
1110
1111
248k
  dec->AdvanceCodestream(reader->TotalBitsConsumed() / jxl::kBitsPerByte);
1112
248k
  dec->codestream_bits_ahead = 0;
1113
1114
248k
  if (!dec->passes_state) {
1115
248k
    dec->passes_state =
1116
248k
        jxl::make_unique<jxl::PassesDecoderState>(&dec->memory_manager);
1117
248k
  }
1118
1119
248k
  JXL_API_RETURN_IF_ERROR(
1120
248k
      dec->passes_state->output_encoding_info.SetFromMetadata(dec->metadata));
1121
248k
  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
248k
  dec->image_metadata = dec->metadata.m;
1126
1127
248k
  return JXL_DEC_SUCCESS;
1128
248k
}
1129
1130
211k
JxlDecoderStatus JxlDecoderProcessSections(JxlDecoder* dec) {
1131
211k
  Span<const uint8_t> span;
1132
211k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1133
210k
  const auto& toc = dec->frame_dec->Toc();
1134
210k
  size_t pos = 0;
1135
210k
  std::vector<jxl::FrameDecoder::SectionInfo> section_info;
1136
210k
  std::vector<jxl::FrameDecoder::SectionStatus> section_status;
1137
692k
  for (size_t i = dec->next_section; i < toc.size(); ++i) {
1138
524k
    if (dec->section_processed[i]) {
1139
182
      pos += toc[i].size;
1140
182
      continue;
1141
182
    }
1142
524k
    size_t id = toc[i].id;
1143
524k
    size_t size = toc[i].size;
1144
524k
    if (OutOfBounds(pos, size, span.size())) {
1145
42.0k
      break;
1146
42.0k
    }
1147
482k
    auto* br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size));
1148
482k
    section_info.emplace_back(jxl::FrameDecoder::SectionInfo{br, id, i});
1149
482k
    section_status.emplace_back();
1150
482k
    pos += size;
1151
482k
  }
1152
210k
  jxl::Status status = dec->frame_dec->ProcessSections(
1153
210k
      section_info.data(), section_info.size(), section_status.data());
1154
210k
  bool out_of_bounds = false;
1155
210k
  bool has_error = false;
1156
482k
  for (const auto& info : section_info) {
1157
482k
    if (!info.br->AllReadsWithinBounds()) {
1158
      // Mark out of bounds section, but keep closing and deleting the next
1159
      // ones as well.
1160
109k
      out_of_bounds = true;
1161
109k
    }
1162
482k
    if (!info.br->Close()) has_error = true;
1163
482k
    delete info.br;
1164
482k
  }
1165
210k
  if (has_error) {
1166
0
    return JXL_INPUT_ERROR("internal: bit-reader failed to close");
1167
0
  }
1168
210k
  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
97.5k
    return JXL_INPUT_ERROR("frame out of bounds");
1173
97.5k
  }
1174
112k
  if (!status) {
1175
44.8k
    return JXL_INPUT_ERROR("frame processing failed");
1176
44.8k
  }
1177
160k
  for (size_t i = 0; i < section_status.size(); ++i) {
1178
92.9k
    auto s_status = section_status[i];
1179
92.9k
    if (s_status == jxl::FrameDecoder::kDone) {
1180
90.5k
      dec->section_processed[section_info[i].index] = 1;
1181
90.5k
    } else if (s_status != jxl::FrameDecoder::kSkipped) {
1182
0
      return JXL_INPUT_ERROR("unexpected section status");
1183
0
    }
1184
92.9k
  }
1185
67.9k
  size_t completed_prefix_bytes = 0;
1186
158k
  while (dec->next_section < dec->section_processed.size() &&
1187
104k
         dec->section_processed[dec->next_section] == 1) {
1188
90.3k
    completed_prefix_bytes += toc[dec->next_section].size;
1189
90.3k
    ++dec->next_section;
1190
90.3k
  }
1191
67.9k
  dec->remaining_frame_size -= completed_prefix_bytes;
1192
67.9k
  dec->AdvanceCodestream(completed_prefix_bytes);
1193
67.9k
  return JXL_DEC_SUCCESS;
1194
67.9k
}
1195
1196
// TODO(eustas): no CodecInOut -> no image size reinforcement -> possible OOM.
1197
1.16M
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
1.16M
  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
1.16M
  if (!dec->got_basic_info) {
1207
513k
    JxlDecoderStatus status = JxlDecoderReadBasicInfo(dec);
1208
513k
    if (status != JXL_DEC_SUCCESS) return status;
1209
513k
  }
1210
1211
1.06M
  if (dec->events_wanted & JXL_DEC_BASIC_INFO) {
1212
420k
    dec->events_wanted &= ~JXL_DEC_BASIC_INFO;
1213
420k
    return JXL_DEC_BASIC_INFO;
1214
420k
  }
1215
1216
647k
  if (!dec->events_wanted) {
1217
0
    dec->stage = DecoderStage::kCodestreamFinished;
1218
0
    return JXL_DEC_SUCCESS;
1219
0
  }
1220
1221
647k
  if (!dec->icc_reader) {
1222
307k
    dec->icc_reader = jxl::make_unique<ICCReader>(&dec->memory_manager);
1223
307k
  }
1224
1225
647k
  if (!dec->got_all_headers) {
1226
307k
    JxlDecoderStatus status = JxlDecoderReadAllHeaders(dec);
1227
307k
    if (status != JXL_DEC_SUCCESS) return status;
1228
307k
  }
1229
1230
588k
  if (dec->events_wanted & JXL_DEC_COLOR_ENCODING) {
1231
248k
    dec->events_wanted &= ~JXL_DEC_COLOR_ENCODING;
1232
248k
    return JXL_DEC_COLOR_ENCODING;
1233
248k
  }
1234
1235
339k
  if (!dec->events_wanted) {
1236
0
    dec->stage = DecoderStage::kCodestreamFinished;
1237
0
    return JXL_DEC_SUCCESS;
1238
0
  }
1239
1240
339k
  dec->post_headers = true;
1241
1242
339k
  if (!dec->got_preview_image && dec->metadata.m.have_preview) {
1243
1.48k
    dec->preview_frame = true;
1244
1.48k
  }
1245
1246
  // Handle frames
1247
382k
  for (;;) {
1248
382k
    bool parse_frames =
1249
382k
        (dec->events_wanted &
1250
382k
         (JXL_DEC_PREVIEW_IMAGE | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
1251
382k
    if (!parse_frames) {
1252
0
      break;
1253
0
    }
1254
382k
    if (dec->frame_stage == FrameStage::kHeader && dec->is_last_total) {
1255
11.9k
      break;
1256
11.9k
    }
1257
370k
    if (dec->frame_stage == FrameStage::kHeader) {
1258
291k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1259
291k
      if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata ||
1260
291k
          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
291k
#endif
1269
291k
      if (!dec->ib) {
1270
290k
        dec->ib = jxl::make_unique<jxl::ImageBundle>(&dec->memory_manager,
1271
290k
                                                     &dec->image_metadata);
1272
290k
      }
1273
291k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1274
      // If JPEG reconstruction is wanted and possible, set the jpeg_data of
1275
      // the ImageBundle.
1276
291k
      if (!dec->jpeg_decoder.SetImageBundleJpegData(dec->ib.get()))
1277
0
        return JXL_DEC_ERROR;
1278
291k
#endif
1279
291k
      dec->frame_dec = jxl::make_unique<FrameDecoder>(
1280
291k
          dec->passes_state.get(), dec->metadata, dec->thread_pool.get(),
1281
291k
          /*use_slow_rendering_pipeline=*/false);
1282
291k
      dec->frame_header = jxl::make_unique<FrameHeader>(&dec->metadata);
1283
291k
      Span<const uint8_t> span;
1284
291k
      JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1285
289k
      auto reader = GetBitReader(span);
1286
289k
      jxl::Status status = dec->frame_dec->InitFrame(
1287
289k
          reader.get(), dec->ib.get(), dec->preview_frame);
1288
289k
      if (!reader->AllReadsWithinBounds() ||
1289
265k
          status.code() == StatusCode::kNotEnoughBytes) {
1290
34.4k
        return dec->RequestMoreInput();
1291
255k
      } else if (!status) {
1292
42.7k
        return JXL_INPUT_ERROR("invalid frame header");
1293
42.7k
      }
1294
212k
      dec->AdvanceCodestream(reader->TotalBitsConsumed() / kBitsPerByte);
1295
212k
      *dec->frame_header = dec->frame_dec->GetFrameHeader();
1296
212k
      jxl::FrameDimensions frame_dim = dec->frame_header->ToFrameDimensions();
1297
212k
      if (!CheckSizeLimit(dec, frame_dim.xsize_upsampled_padded,
1298
212k
                          frame_dim.ysize_upsampled_padded)) {
1299
0
        return JXL_INPUT_ERROR("frame is too large");
1300
0
      }
1301
212k
      int output_type =
1302
212k
          dec->preview_frame ? JXL_DEC_PREVIEW_IMAGE : JXL_DEC_FULL_IMAGE;
1303
212k
      bool output_needed = ((dec->events_wanted & output_type) != 0);
1304
212k
      if (output_needed) {
1305
211k
        JXL_API_RETURN_IF_ERROR(dec->frame_dec->InitFrameOutput());
1306
210k
      }
1307
211k
      dec->remaining_frame_size = dec->frame_dec->SumSectionSizes();
1308
1309
211k
      dec->frame_stage = FrameStage::kTOC;
1310
211k
      if (dec->preview_frame) {
1311
820
        if (!(dec->events_wanted & JXL_DEC_PREVIEW_IMAGE)) {
1312
820
          dec->frame_stage = FrameStage::kHeader;
1313
820
          dec->AdvanceCodestream(dec->remaining_frame_size);
1314
820
          dec->got_preview_image = true;
1315
820
          dec->preview_frame = false;
1316
820
        }
1317
820
        continue;
1318
820
      }
1319
1320
210k
      int saved_as = FrameDecoder::SavedAs(*dec->frame_header);
1321
      // is last in entire codestream
1322
210k
      dec->is_last_total = dec->frame_header->is_last;
1323
      // is last of current still
1324
210k
      dec->is_last_of_still =
1325
210k
          dec->is_last_total || dec->frame_header->animation_frame.duration > 0;
1326
      // is kRegularFrame and coalescing is disabled
1327
210k
      dec->is_last_of_still |=
1328
210k
          (!dec->coalescing &&
1329
0
           dec->frame_header->frame_type == FrameType::kRegularFrame);
1330
210k
      const size_t internal_frame_index = dec->internal_frames;
1331
210k
      const size_t external_frame_index = dec->external_frames;
1332
210k
      if (dec->is_last_of_still) dec->external_frames++;
1333
210k
      dec->internal_frames++;
1334
1335
210k
      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
210k
      } else {
1341
210k
        dec->skipping_frame = false;
1342
210k
      }
1343
1344
210k
      if (external_frame_index >= dec->frame_external_to_internal.size()) {
1345
175k
        dec->frame_external_to_internal.push_back(internal_frame_index);
1346
175k
        if (dec->frame_external_to_internal.size() !=
1347
175k
            external_frame_index + 1) {
1348
0
          return JXL_API_ERROR("internal");
1349
0
        }
1350
175k
      }
1351
1352
210k
      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
210k
        dec->frame_refs.emplace_back(FrameRef{0xFF, saved_as});
1358
210k
        if (dec->frame_refs.size() != internal_frame_index + 1) {
1359
0
          return JXL_API_ERROR("internal");
1360
0
        }
1361
210k
      }
1362
1363
210k
      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
210k
      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
0
        if (!dec->skipping_frame) {
1388
0
          return JXL_DEC_FRAME;
1389
0
        }
1390
0
      }
1391
210k
    }
1392
1393
289k
    if (dec->frame_stage == FrameStage::kTOC) {
1394
210k
      dec->frame_dec->SetRenderSpotcolors(dec->render_spotcolors);
1395
210k
      dec->frame_dec->SetCoalescing(dec->coalescing);
1396
1397
210k
      if (!dec->preview_frame &&
1398
210k
          (dec->events_wanted & JXL_DEC_FRAME_PROGRESSION)) {
1399
0
        dec->frame_prog_detail =
1400
0
            dec->frame_dec->SetPauseAtProgressive(dec->prog_detail);
1401
210k
      } else {
1402
210k
        dec->frame_prog_detail = JxlProgressiveDetail::kFrames;
1403
210k
      }
1404
210k
      dec->dc_frame_progression_done = false;
1405
1406
210k
      dec->next_section = 0;
1407
210k
      dec->section_processed.clear();
1408
210k
      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
210k
      if (dec->preview_frame || (dec->events_wanted & JXL_DEC_FULL_IMAGE)) {
1412
210k
        dec->frame_stage = FrameStage::kFull;
1413
210k
      } else if (!dec->is_last_total) {
1414
0
        dec->frame_stage = FrameStage::kHeader;
1415
0
        dec->AdvanceCodestream(dec->remaining_frame_size);
1416
0
        continue;
1417
0
      } else {
1418
0
        break;
1419
0
      }
1420
210k
    }
1421
1422
289k
    if (dec->frame_stage == FrameStage::kFull) {
1423
289k
      if (!dec->image_out_buffer_set) {
1424
210k
        if (dec->preview_frame) {
1425
0
          return JXL_DEC_NEED_PREVIEW_OUT_BUFFER;
1426
0
        }
1427
210k
        if (
1428
210k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1429
210k
            (!dec->jpeg_decoder.IsOutputSet() ||
1430
0
             dec->ib->jpeg_data == nullptr) &&
1431
210k
#endif
1432
210k
            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
78.5k
          return JXL_DEC_NEED_IMAGE_OUT_BUFFER;
1437
78.5k
        }
1438
210k
      }
1439
1440
211k
      if (dec->image_out_buffer_set) {
1441
78.8k
        size_t xsize;
1442
78.8k
        size_t ysize;
1443
78.8k
        GetCurrentDimensions(dec, xsize, ysize);
1444
78.8k
        size_t bits_per_sample = GetBitDepth(
1445
78.8k
            dec->image_out_bit_depth, dec->metadata.m, dec->image_out_format);
1446
78.8k
        dec->frame_dec->SetImageOutput(
1447
78.8k
            PixelCallback{
1448
78.8k
                dec->image_out_init_callback, dec->image_out_run_callback,
1449
78.8k
                dec->image_out_destroy_callback, dec->image_out_init_opaque},
1450
78.8k
            reinterpret_cast<uint8_t*>(dec->image_out_buffer),
1451
78.8k
            dec->image_out_size, xsize, ysize, dec->image_out_format,
1452
78.8k
            bits_per_sample, dec->unpremul_alpha, !dec->keep_orientation);
1453
78.8k
        for (size_t i = 0; i < dec->extra_channel_output.size(); ++i) {
1454
0
          const auto& extra = dec->extra_channel_output[i];
1455
0
          size_t ec_bits_per_sample =
1456
0
              GetBitDepth(dec->image_out_bit_depth,
1457
0
                          dec->metadata.m.extra_channel_info[i], extra.format);
1458
0
          dec->frame_dec->AddExtraChannelOutput(extra.buffer, extra.buffer_size,
1459
0
                                                xsize, extra.format,
1460
0
                                                ec_bits_per_sample);
1461
0
        }
1462
78.8k
      }
1463
1464
211k
      size_t next_num_passes_to_pause = dec->frame_dec->NextNumPassesToPause();
1465
1466
211k
      JXL_API_RETURN_IF_ERROR(JxlDecoderProcessSections(dec));
1467
1468
67.9k
      bool all_sections_done = dec->frame_dec->HasDecodedAll();
1469
67.9k
      bool got_dc_only = !all_sections_done && dec->frame_dec->HasDecodedDC();
1470
1471
67.9k
      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
67.9k
      bool new_progression_step_done =
1479
67.9k
          dec->frame_dec->NumCompletePasses() >= next_num_passes_to_pause;
1480
1481
67.9k
      if (!all_sections_done &&
1482
13.8k
          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
67.9k
      if (!all_sections_done) {
1491
        // Not all sections have been processed yet
1492
13.8k
        return dec->RequestMoreInput();
1493
13.8k
      }
1494
1495
54.1k
      if (!dec->preview_frame) {
1496
54.1k
        size_t internal_index = dec->internal_frames - 1;
1497
54.1k
        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
54.1k
        dec->frame_refs[internal_index].reference =
1504
54.1k
            dec->frame_dec->References();
1505
54.1k
      }
1506
1507
54.1k
      if (!dec->frame_dec->FinalizeFrame()) {
1508
0
        return JXL_INPUT_ERROR("decoding frame failed");
1509
0
      }
1510
54.1k
#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
54.1k
      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
54.1k
#endif
1519
54.1k
      if (dec->preview_frame || dec->is_last_of_still) {
1520
12.0k
        dec->image_out_buffer_set = false;
1521
12.0k
        dec->extra_channel_output.clear();
1522
12.0k
      }
1523
54.1k
    }
1524
1525
54.1k
    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
54.1k
    dec->ib.reset();
1530
54.1k
    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
54.1k
    } else if (dec->is_last_of_still &&
1536
12.0k
               (dec->events_wanted & JXL_DEC_FULL_IMAGE) &&
1537
12.0k
               !dec->skipping_frame) {
1538
12.0k
      return JXL_DEC_FULL_IMAGE;
1539
12.0k
    }
1540
54.1k
  }
1541
1542
11.9k
  dec->stage = DecoderStage::kCodestreamFinished;
1543
  // Return success, this means there is nothing more to do.
1544
11.9k
  return JXL_DEC_SUCCESS;
1545
339k
}
1546
1547
}  // namespace
1548
}  // namespace jxl
1549
1550
JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data,
1551
521k
                                    size_t size) {
1552
521k
  if (dec->next_in) {
1553
0
    return JXL_API_ERROR("already set input, use JxlDecoderReleaseInput first");
1554
0
  }
1555
521k
  if (dec->input_closed) {
1556
0
    return JXL_API_ERROR("input already closed");
1557
0
  }
1558
1559
521k
  dec->next_in = data;
1560
521k
  dec->avail_in = size;
1561
521k
  return JXL_DEC_SUCCESS;
1562
521k
}
1563
1564
521k
size_t JxlDecoderReleaseInput(JxlDecoder* dec) {
1565
521k
  size_t result = dec->avail_in;
1566
521k
  dec->next_in = nullptr;
1567
521k
  dec->avail_in = 0;
1568
521k
  return result;
1569
521k
}
1570
1571
519k
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
14.3k
                                       uint64_t* header_size) {
1619
14.3k
  if (OutOfBounds(pos, 8, size)) {
1620
1.56k
    *header_size = 8;
1621
1.56k
    return JXL_DEC_NEED_MORE_INPUT;
1622
1.56k
  }
1623
12.8k
  size_t box_start = pos;
1624
  // Box size, including this header itself.
1625
12.8k
  *box_size = LoadBE32(in + pos);
1626
12.8k
  pos += 4;
1627
12.8k
  memcpy(type, in + pos, 4);
1628
12.8k
  pos += 4;
1629
12.8k
  if (*box_size == 1) {
1630
1.34k
    *header_size = 16;
1631
1.34k
    if (OutOfBounds(pos, 8, size)) return JXL_DEC_NEED_MORE_INPUT;
1632
1.34k
    *box_size = LoadBE64(in + pos);
1633
1.34k
    pos += 8;
1634
1.34k
  }
1635
12.8k
  *header_size = pos - box_start;
1636
12.8k
  if (*box_size > 0 && *box_size < *header_size) {
1637
67
    return JXL_INPUT_ERROR("invalid box size");
1638
67
  }
1639
12.7k
  if (file_pos + *box_size < file_pos) {
1640
148
    return JXL_INPUT_ERROR("Box size overflow");
1641
148
  }
1642
12.6k
  return JXL_DEC_SUCCESS;
1643
12.7k
}
1644
1645
// This includes handling the codestream if it is not a box-based jxl file.
1646
1.17M
static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
1647
  // Box handling loop
1648
1.69M
  for (;;) {
1649
1.69M
    if (dec->box_stage != BoxStage::kHeader) {
1650
1.17M
      dec->AdvanceInput(dec->header_size);
1651
1.17M
      dec->header_size = 0;
1652
1.17M
#if JPEGXL_ENABLE_BOXES
1653
1.17M
      if ((dec->events_wanted & JXL_DEC_BOX) && dec->box_event &&
1654
8.80k
          !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.74k
        dec->box_event = false;
1659
8.74k
      }
1660
1.17M
      if ((dec->events_wanted & JXL_DEC_BOX) &&
1661
1.17M
          dec->box_out_buffer_set_current_box) {
1662
59
        uint8_t* next_out = dec->box_out_buffer + dec->box_out_buffer_pos;
1663
59
        size_t avail_out = dec->box_out_buffer_size - dec->box_out_buffer_pos;
1664
1665
59
        JxlDecoderStatus box_result = dec->box_content_decoder.Process(
1666
59
            dec->next_in, dec->avail_in,
1667
59
            dec->file_pos - dec->box_contents_begin, &next_out, &avail_out);
1668
59
        size_t produced =
1669
59
            next_out - (dec->box_out_buffer + dec->box_out_buffer_pos);
1670
59
        dec->box_out_buffer_pos += produced;
1671
1672
59
        if (box_result == JXL_DEC_BOX_COMPLETE &&
1673
47
            !(dec->events_wanted & JXL_DEC_BOX_COMPLETE)) {
1674
47
          box_result = JXL_DEC_SUCCESS;
1675
47
        }
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
59
        if (box_result != JXL_DEC_SUCCESS &&
1681
12
            box_result != JXL_DEC_NEED_MORE_INPUT) {
1682
0
          return box_result;
1683
0
        }
1684
59
      }
1685
1.17M
#endif
1686
1.17M
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1687
1.17M
      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
1.17M
#endif
1725
1.17M
    }
1726
1.69M
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1727
1.69M
    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
1.69M
    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
1.69M
#endif
1758
1759
1.69M
    if (dec->box_stage == BoxStage::kHeader) {
1760
527k
      if (!dec->have_container) {
1761
512k
        if (dec->stage == DecoderStage::kCodestreamFinished)
1762
0
          return JXL_DEC_SUCCESS;
1763
512k
        dec->box_stage = BoxStage::kCodestream;
1764
512k
        dec->box_contents_unbounded = true;
1765
512k
        continue;
1766
512k
      }
1767
14.6k
      if (dec->avail_in == 0) {
1768
270
        if (dec->stage != DecoderStage::kCodestreamFinished) {
1769
          // Not yet seen (all) codestream boxes.
1770
255
          return JXL_DEC_NEED_MORE_INPUT;
1771
255
        }
1772
15
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1773
15
        if (dec->JbrdNeedMoreBoxes()) {
1774
0
          return JXL_DEC_NEED_MORE_INPUT;
1775
0
        }
1776
15
#endif
1777
15
        if (dec->input_closed) {
1778
15
          return JXL_DEC_SUCCESS;
1779
15
        }
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
14.3k
      bool boxed_codestream_done =
1795
14.3k
          ((dec->events_wanted & JXL_DEC_BOX) &&
1796
14.3k
           dec->stage == DecoderStage::kCodestreamFinished &&
1797
38
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1798
38
           !dec->JbrdNeedMoreBoxes() &&
1799
38
#endif
1800
38
           dec->last_codestream_seen);
1801
14.3k
      if (boxed_codestream_done && dec->avail_in >= 2 &&
1802
37
          dec->next_in[0] == 0xff &&
1803
2
          dec->next_in[1] == jxl::kCodestreamMarker) {
1804
        // We detected the start of the next naked codestream, so we can return
1805
        // success here.
1806
1
        return JXL_DEC_SUCCESS;
1807
1
      }
1808
1809
14.3k
      uint64_t box_size;
1810
14.3k
      uint64_t header_size;
1811
14.3k
      JxlDecoderStatus status =
1812
14.3k
          ParseBoxHeader(dec->next_in, dec->avail_in, 0, dec->file_pos,
1813
14.3k
                         dec->box_type, &box_size, &header_size);
1814
14.3k
      if (status != JXL_DEC_SUCCESS) {
1815
1.78k
        if (status == JXL_DEC_NEED_MORE_INPUT) {
1816
1.56k
          dec->basic_info_size_hint =
1817
1.56k
              InitialBasicInfoSizeHint() + header_size - dec->file_pos;
1818
1.56k
        }
1819
1.78k
        return status;
1820
1.78k
      }
1821
12.6k
      if (memcmp(dec->box_type, "brob", 4) == 0) {
1822
38
        if (dec->avail_in < header_size + 4) {
1823
3
          return JXL_DEC_NEED_MORE_INPUT;
1824
3
        }
1825
35
        memcpy(dec->box_decoded_type, dec->next_in + header_size,
1826
35
               sizeof(dec->box_decoded_type));
1827
12.5k
      } else {
1828
12.5k
        memcpy(dec->box_decoded_type, dec->box_type,
1829
12.5k
               sizeof(dec->box_decoded_type));
1830
12.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
12.6k
      dec->box_count++;
1836
12.6k
      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
1
        return JXL_DEC_SUCCESS;
1840
1
      }
1841
12.6k
      if (dec->box_count == 2 && memcmp(dec->box_type, "ftyp", 4) != 0) {
1842
3.79k
        return JXL_INPUT_ERROR("the second box must be the ftyp box");
1843
3.79k
      }
1844
8.80k
      if (memcmp(dec->box_type, "ftyp", 4) == 0 && dec->box_count != 2) {
1845
1
        return JXL_INPUT_ERROR("the ftyp box must come second");
1846
1
      }
1847
1848
8.80k
      dec->box_contents_unbounded = (box_size == 0);
1849
8.80k
      dec->box_contents_begin = dec->file_pos + header_size;
1850
8.80k
      dec->box_contents_end =
1851
8.80k
          dec->box_contents_unbounded ? 0 : (dec->file_pos + box_size);
1852
8.80k
      dec->box_contents_size =
1853
8.80k
          dec->box_contents_unbounded ? 0 : (box_size - header_size);
1854
8.80k
      dec->box_size = box_size;
1855
8.80k
      dec->header_size = header_size;
1856
8.80k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1857
8.80k
      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
8.80k
#endif
1871
8.80k
#if JPEGXL_ENABLE_BOXES
1872
8.80k
      if (dec->events_wanted & JXL_DEC_BOX) {
1873
8.80k
        bool decompress =
1874
8.80k
            dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
1875
8.80k
        dec->box_content_decoder.StartBox(
1876
8.80k
            decompress, dec->box_contents_unbounded, dec->box_contents_size);
1877
8.80k
      }
1878
8.80k
#endif
1879
8.80k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1880
8.80k
      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
8.80k
#endif
1886
8.80k
      if (memcmp(dec->box_type, "ftyp", 4) == 0) {
1887
1.24k
        dec->box_stage = BoxStage::kFtyp;
1888
7.56k
      } else if (memcmp(dec->box_type, "jxlc", 4) == 0) {
1889
104
        if (dec->last_codestream_seen) {
1890
1
          return JXL_INPUT_ERROR("there can only be one jxlc box");
1891
1
        }
1892
103
        dec->last_codestream_seen = true;
1893
103
        dec->box_stage = BoxStage::kCodestream;
1894
7.45k
      } else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
1895
176
        dec->box_stage = BoxStage::kPartialCodestream;
1896
176
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1897
7.28k
      } 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
7.28k
      } else {
1906
7.28k
        dec->box_stage = BoxStage::kSkip;
1907
7.28k
      }
1908
1909
8.80k
      if (dec->events_wanted & JXL_DEC_BOX) {
1910
8.80k
        dec->box_event = true;
1911
8.80k
        dec->box_out_buffer_set_current_box = false;
1912
8.80k
        return JXL_DEC_BOX;
1913
8.80k
      }
1914
1.17M
    } else if (dec->box_stage == BoxStage::kFtyp) {
1915
1.24k
      if (dec->box_contents_size < 12) {
1916
119
        return JXL_INPUT_ERROR("file type box too small");
1917
119
      }
1918
1.12k
      if (dec->avail_in < 8) return JXL_DEC_NEED_MORE_INPUT;
1919
955
      if (memcmp(dec->next_in, "jxl ", 4) != 0) {
1920
178
        return JXL_INPUT_ERROR("file type box major brand must be \"jxl \"");
1921
178
      }
1922
777
      uint32_t version = LoadBE32(dec->next_in + 4);
1923
777
      if (version > 1) {
1924
534
        return JXL_INPUT_ERROR(
1925
534
            "unknown jxl file format version %u (known versions: 0, 1)",
1926
534
            version);
1927
534
      }
1928
243
      dec->jxl_file_format_version = version;
1929
243
      dec->AdvanceInput(8);
1930
243
      dec->box_stage = BoxStage::kSkip;
1931
1.16M
    } else if (dec->box_stage == BoxStage::kPartialCodestream) {
1932
176
      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
175
      if (dec->avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
1937
174
      if (!dec->box_contents_unbounded && dec->box_contents_size < 4) {
1938
3
        return JXL_INPUT_ERROR("jxlp box too small to contain index");
1939
3
      }
1940
171
      uint32_t jxlp_index = LoadBE32(dec->next_in);
1941
171
      uint32_t counter = jxlp_index & 0x7FFFFFFF;
1942
171
      bool is_last = (jxlp_index & 0x80000000) != 0;
1943
1944
171
      if (counter < dec->next_jxlp_index) {
1945
2
        return JXL_INPUT_ERROR(
1946
2
            "jxlp box index %u is a duplicate (already processed)", counter);
1947
2
      }
1948
169
      dec->AdvanceInput(4);  // consume the 4-byte jxlp counter header
1949
1950
169
      if (counter == dec->next_jxlp_index) {
1951
115
        dec->next_jxlp_index++;
1952
115
        if (is_last) dec->last_codestream_seen = true;
1953
115
        dec->box_stage = BoxStage::kCodestream;
1954
115
      } 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
41
        auto insert_result = dec->jxlp_ooo_buffer.emplace(
1963
41
            counter, std::make_pair(std::vector<uint8_t>(), is_last));
1964
41
        if (!insert_result.second) {
1965
6
          return JXL_INPUT_ERROR("duplicate jxlp box index %u", counter);
1966
6
        }
1967
35
        dec->buffering_jxlp_index = counter;
1968
35
        dec->buffering_jxlp_is_last = is_last;
1969
35
        dec->box_stage = BoxStage::kBufferingJxlp;
1970
35
      } else {
1971
13
        return JXL_INPUT_ERROR(
1972
13
            "jxlp box index %u is out of order (expected %u); out-of-order "
1973
13
            "jxlp boxes require file format version 1 in the ftyp box",
1974
13
            counter, dec->next_jxlp_index);
1975
13
      }
1976
1.16M
    } else if (dec->box_stage == BoxStage::kCodestream) {
1977
1.16M
      JxlDecoderStatus status = jxl::JxlDecoderProcessCodestream(dec);
1978
1.16M
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1979
1.16M
      if (status == JXL_DEC_FULL_IMAGE) {
1980
12.0k
        if (dec->recon_output_jpeg != JpegReconStage::kNone) {
1981
0
          continue;
1982
0
        }
1983
12.0k
      }
1984
1.16M
#endif
1985
1.16M
      if (status == JXL_DEC_NEED_MORE_INPUT) {
1986
103k
        if (dec->file_pos == dec->box_contents_end &&
1987
70
            !dec->box_contents_unbounded) {
1988
          // Physical box exhausted; inject the next buffered OOO box if ready.
1989
70
          if (dec->InjectNextBufferedJxlpBox()) continue;
1990
70
          dec->box_stage = BoxStage::kHeader;
1991
70
          continue;
1992
70
        }
1993
103k
      }
1994
1995
1.16M
      if (status == JXL_DEC_SUCCESS) {
1996
11.9k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1997
11.9k
        if (dec->JbrdNeedMoreBoxes()) {
1998
0
          dec->box_stage = BoxStage::kSkip;
1999
0
          continue;
2000
0
        }
2001
11.9k
#endif
2002
11.9k
        if (dec->box_contents_unbounded) {
2003
          // Last box reached and codestream done, nothing more to do.
2004
11.9k
          break;
2005
11.9k
        }
2006
37
        if (dec->events_wanted & JXL_DEC_BOX) {
2007
          // Codestream done, but there may be more other boxes.
2008
37
          dec->box_stage = BoxStage::kSkip;
2009
37
          continue;
2010
37
        }
2011
37
      }
2012
1.14M
      return status;
2013
1.16M
    } else if (dec->box_stage == BoxStage::kBufferingJxlp) {
2014
35
      size_t remaining =
2015
35
          dec->box_contents_unbounded
2016
35
              ? dec->avail_in
2017
35
              : std::min<size_t>(dec->avail_in,
2018
34
                                 dec->box_contents_end - dec->file_pos);
2019
35
      auto& entry = dec->jxlp_ooo_buffer[dec->buffering_jxlp_index];
2020
35
      entry.first.insert(entry.first.end(), dec->next_in,
2021
35
                         dec->next_in + remaining);
2022
35
      dec->AdvanceInput(remaining);
2023
2024
35
      bool box_done = !dec->box_contents_unbounded &&
2025
34
                      dec->file_pos >= dec->box_contents_end;
2026
35
      if (!box_done) {
2027
4
        return JXL_DEC_NEED_MORE_INPUT;
2028
4
      }
2029
      // Box fully buffered; parse the next box header.
2030
31
      dec->box_stage = BoxStage::kHeader;
2031
31
#if JPEGXL_ENABLE_TRANSCODE_JPEG
2032
7.58k
    } 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
7.58k
    } else if (dec->box_stage == BoxStage::kSkip) {
2082
7.58k
      if (dec->box_contents_unbounded) {
2083
8
        if (dec->input_closed) {
2084
7
          return JXL_DEC_SUCCESS;
2085
7
        }
2086
1
        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
1
          return JXL_DEC_SUCCESS;
2092
1
        }
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
1
      }
2098
      // Amount of remaining bytes in the box that is being skipped.
2099
7.57k
      size_t remaining = dec->box_contents_end - dec->file_pos;
2100
7.57k
      if (dec->avail_in < remaining) {
2101
        // Indicate how many more bytes needed starting from next_in.
2102
89
        dec->basic_info_size_hint =
2103
89
            InitialBasicInfoSizeHint() + dec->box_contents_end - dec->file_pos;
2104
        // Don't have the full box yet, skip all we have so far
2105
89
        dec->AdvanceInput(dec->avail_in);
2106
89
        return JXL_DEC_NEED_MORE_INPUT;
2107
7.48k
      } else {
2108
        // Full box available, skip all its remaining bytes
2109
7.48k
        dec->AdvanceInput(remaining);
2110
7.48k
        dec->box_stage = BoxStage::kHeader;
2111
7.48k
      }
2112
7.57k
    } else {
2113
0
      JXL_DEBUG_ABORT("Unreachable");
2114
0
    }
2115
1.69M
  }
2116
11.9k
  return JXL_DEC_SUCCESS;
2117
1.17M
}
2118
2119
1.17M
JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
2120
1.17M
  if (dec->stage == DecoderStage::kInited) {
2121
519k
    dec->stage = DecoderStage::kStarted;
2122
519k
  }
2123
1.17M
  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
1.17M
  if (!dec->got_signature) {
2130
519k
    JxlSignature sig = JxlSignatureCheck(dec->next_in, dec->avail_in);
2131
519k
    if (sig == JXL_SIG_INVALID) return JXL_INPUT_ERROR("invalid signature");
2132
519k
    if (sig == JXL_SIG_NOT_ENOUGH_BYTES) {
2133
3
      if (dec->input_closed) {
2134
3
        return JXL_INPUT_ERROR("file too small for signature");
2135
3
      }
2136
0
      return JXL_DEC_NEED_MORE_INPUT;
2137
3
    }
2138
2139
519k
    dec->got_signature = true;
2140
2141
519k
    if (sig == JXL_SIG_CONTAINER) {
2142
7.06k
      dec->have_container = true;
2143
512k
    } else {
2144
512k
      dec->last_codestream_seen = true;
2145
512k
    }
2146
519k
  }
2147
2148
1.17M
  JxlDecoderStatus status = HandleBoxes(dec);
2149
2150
1.17M
  if (status == JXL_DEC_NEED_MORE_INPUT && dec->input_closed) {
2151
104k
    return JXL_INPUT_ERROR("premature end of input");
2152
104k
  }
2153
2154
  // Even if the box handling returns success, certain types of
2155
  // data may be missing.
2156
1.07M
  if (status == JXL_DEC_SUCCESS) {
2157
11.9k
    if (dec->CanUseMoreCodestreamInput()) {
2158
4
      return JXL_INPUT_ERROR("codestream never finished");
2159
4
    }
2160
11.9k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
2161
11.9k
    if (dec->JbrdNeedMoreBoxes()) {
2162
0
      return JXL_INPUT_ERROR("missing metadata boxes for jpeg reconstruction");
2163
0
    }
2164
11.9k
#endif
2165
11.9k
  }
2166
2167
1.07M
  return status;
2168
1.07M
}
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
420k
                                        JxlBasicInfo* info) {
2176
420k
  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
2177
2178
420k
  if (info) {
2179
420k
    memset(info, 0, sizeof(*info));
2180
2181
420k
    const jxl::ImageMetadata& meta = dec->metadata.m;
2182
2183
420k
    info->have_container = TO_JXL_BOOL(dec->have_container);
2184
420k
    info->xsize = dec->metadata.size.xsize();
2185
420k
    info->ysize = dec->metadata.size.ysize();
2186
420k
    info->uses_original_profile = TO_JXL_BOOL(!meta.xyb_encoded);
2187
2188
420k
    info->bits_per_sample = meta.bit_depth.bits_per_sample;
2189
420k
    info->exponent_bits_per_sample = meta.bit_depth.exponent_bits_per_sample;
2190
2191
420k
    info->have_preview = TO_JXL_BOOL(meta.have_preview);
2192
420k
    info->have_animation = TO_JXL_BOOL(meta.have_animation);
2193
420k
    info->orientation = static_cast<JxlOrientation>(meta.orientation);
2194
2195
420k
    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
420k
    info->intensity_target = meta.IntensityTarget();
2203
420k
    if (dec->desired_intensity_target > 0) {
2204
0
      info->intensity_target = dec->desired_intensity_target;
2205
0
    }
2206
420k
    info->min_nits = meta.tone_mapping.min_nits;
2207
420k
    info->relative_to_max_display =
2208
420k
        TO_JXL_BOOL(meta.tone_mapping.relative_to_max_display);
2209
420k
    info->linear_below = meta.tone_mapping.linear_below;
2210
2211
420k
    const jxl::ExtraChannelInfo* alpha = meta.Find(jxl::ExtraChannel::kAlpha);
2212
420k
    if (alpha != nullptr) {
2213
18.2k
      info->alpha_bits = alpha->bit_depth.bits_per_sample;
2214
18.2k
      info->alpha_exponent_bits = alpha->bit_depth.exponent_bits_per_sample;
2215
18.2k
      info->alpha_premultiplied = TO_JXL_BOOL(alpha->alpha_associated);
2216
402k
    } else {
2217
402k
      info->alpha_bits = 0;
2218
402k
      info->alpha_exponent_bits = 0;
2219
402k
      info->alpha_premultiplied = 0;
2220
402k
    }
2221
2222
420k
    info->num_color_channels =
2223
420k
        meta.color_encoding.GetColorSpace() == jxl::ColorSpace::kGray ? 1 : 3;
2224
2225
420k
    info->num_extra_channels = meta.num_extra_channels;
2226
2227
420k
    if (info->have_preview) {
2228
5.98k
      info->preview.xsize = dec->metadata.m.preview_size.xsize();
2229
5.98k
      info->preview.ysize = dec->metadata.m.preview_size.ysize();
2230
5.98k
    }
2231
2232
420k
    if (info->have_animation) {
2233
856
      info->animation.tps_numerator = dec->metadata.m.animation.tps_numerator;
2234
856
      info->animation.tps_denominator =
2235
856
          dec->metadata.m.animation.tps_denominator;
2236
856
      info->animation.num_loops = dec->metadata.m.animation.num_loops;
2237
856
      info->animation.have_timecodes =
2238
856
          TO_JXL_BOOL(dec->metadata.m.animation.have_timecodes);
2239
856
    }
2240
2241
420k
    if (meta.have_intrinsic_size) {
2242
7.20k
      info->intrinsic_xsize = dec->metadata.m.intrinsic_size.xsize();
2243
7.20k
      info->intrinsic_ysize = dec->metadata.m.intrinsic_size.ysize();
2244
413k
    } else {
2245
413k
      info->intrinsic_xsize = info->xsize;
2246
413k
      info->intrinsic_ysize = info->ysize;
2247
413k
    }
2248
420k
  }
2249
2250
420k
  return JXL_DEC_SUCCESS;
2251
420k
}
2252
2253
JxlDecoderStatus JxlDecoderGetExtraChannelInfo(const JxlDecoder* dec,
2254
                                               size_t index,
2255
41.5k
                                               JxlExtraChannelInfo* info) {
2256
41.5k
  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
2257
2258
41.5k
  const std::vector<jxl::ExtraChannelInfo>& channels =
2259
41.5k
      dec->metadata.m.extra_channel_info;
2260
2261
41.5k
  if (index >= channels.size()) return JXL_DEC_ERROR;  // out of bounds
2262
41.5k
  const jxl::ExtraChannelInfo& channel = channels[index];
2263
2264
41.5k
  info->type = static_cast<JxlExtraChannelType>(channel.type);
2265
41.5k
  info->bits_per_sample = channel.bit_depth.bits_per_sample;
2266
41.5k
  info->exponent_bits_per_sample =
2267
41.5k
      channel.bit_depth.floating_point_sample
2268
41.5k
          ? channel.bit_depth.exponent_bits_per_sample
2269
41.5k
          : 0;
2270
41.5k
  info->dim_shift = channel.dim_shift;
2271
41.5k
  info->name_length = channel.name.size();
2272
41.5k
  info->alpha_premultiplied = TO_JXL_BOOL(channel.alpha_associated);
2273
41.5k
  info->spot_color[0] = channel.spot_color[0];
2274
41.5k
  info->spot_color[1] = channel.spot_color[1];
2275
41.5k
  info->spot_color[2] = channel.spot_color[2];
2276
41.5k
  info->spot_color[3] = channel.spot_color[3];
2277
41.5k
  info->cfa_channel = channel.cfa_channel;
2278
2279
41.5k
  return JXL_DEC_SUCCESS;
2280
41.5k
}
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
993k
    const jxl::ColorEncoding** encoding) {
2310
993k
  if (!dec->got_all_headers) return JXL_DEC_NEED_MORE_INPUT;
2311
993k
  *encoding = nullptr;
2312
993k
  if (target == JXL_COLOR_PROFILE_TARGET_DATA && dec->metadata.m.xyb_encoded) {
2313
85.6k
    *encoding = &dec->passes_state->output_encoding_info.color_encoding;
2314
908k
  } else {
2315
908k
    *encoding = &dec->metadata.m.color_encoding;
2316
908k
  }
2317
993k
  return JXL_DEC_SUCCESS;
2318
993k
}
2319
}  // namespace
2320
2321
JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile(
2322
    const JxlDecoder* dec, JxlColorProfileTarget target,
2323
248k
    JxlColorEncoding* color_encoding) {
2324
248k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2325
248k
  JxlDecoderStatus status =
2326
248k
      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2327
248k
  if (status) return status;
2328
2329
248k
  if (jxl_color_encoding->WantICC())
2330
80
    return JXL_DEC_ERROR;  // Indicate no encoded profile available.
2331
2332
248k
  if (color_encoding) {
2333
248k
    *color_encoding = jxl_color_encoding->ToExternal();
2334
248k
  }
2335
2336
248k
  return JXL_DEC_SUCCESS;
2337
248k
}
2338
2339
JxlDecoderStatus JxlDecoderGetICCProfileSize(const JxlDecoder* dec,
2340
                                             JxlColorProfileTarget target,
2341
496k
                                             size_t* size) {
2342
496k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2343
496k
  JxlDecoderStatus status =
2344
496k
      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2345
496k
  if (status != JXL_DEC_SUCCESS) return status;
2346
2347
496k
  if (jxl_color_encoding->WantICC()) {
2348
174
    jxl::ColorSpace color_space =
2349
174
        dec->metadata.m.color_encoding.GetColorSpace();
2350
174
    if (color_space == jxl::ColorSpace::kUnknown ||
2351
171
        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
6
      return JXL_DEC_ERROR;
2357
6
    }
2358
174
  }
2359
2360
496k
  if (size) {
2361
496k
    *size = jxl_color_encoding->ICC().size();
2362
496k
  }
2363
2364
496k
  return JXL_DEC_SUCCESS;
2365
496k
}
2366
2367
JxlDecoderStatus JxlDecoderGetColorAsICCProfile(const JxlDecoder* dec,
2368
                                                JxlColorProfileTarget target,
2369
                                                uint8_t* icc_profile,
2370
248k
                                                size_t size) {
2371
248k
  size_t wanted_size;
2372
  // This also checks the NEED_MORE_INPUT and the unknown/xyb cases
2373
248k
  JxlDecoderStatus status =
2374
248k
      JxlDecoderGetICCProfileSize(dec, target, &wanted_size);
2375
248k
  if (status != JXL_DEC_SUCCESS) return status;
2376
248k
  if (size < wanted_size) return JXL_API_ERROR("ICC profile output too small");
2377
2378
248k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2379
248k
  status = GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2380
248k
  if (status != JXL_DEC_SUCCESS) return status;
2381
2382
248k
  memcpy(icc_profile, jxl_color_encoding->ICC().data(),
2383
248k
         jxl_color_encoding->ICC().size());
2384
2385
248k
  return JXL_DEC_SUCCESS;
2386
248k
}
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
157k
                                  const JxlPixelFormat* format, size_t* bits) {
2394
157k
  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
157k
  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
157k
  if (format->num_channels > 4) {
2403
0
    return JXL_API_ERROR("More than 4 channels not supported");
2404
0
  }
2405
2406
157k
  *bits = BitsPerChannel(format->data_type);
2407
2408
157k
  if (*bits == 0) {
2409
0
    return JXL_API_ERROR("Invalid/unsupported data type");
2410
0
  }
2411
2412
157k
  return JXL_DEC_SUCCESS;
2413
157k
}
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
0
                                             const JxlCmsInterface cms) {
2442
0
  if (!dec->passes_state) {
2443
0
    dec->passes_state =
2444
0
        jxl::make_unique<jxl::PassesDecoderState>(&dec->memory_manager);
2445
0
  }
2446
0
  dec->passes_state->output_encoding_info.color_management_system = cms;
2447
0
  dec->passes_state->output_encoding_info.cms_set = true;
2448
0
  return JXL_DEC_SUCCESS;
2449
0
}
2450
2451
static JxlDecoderStatus GetMinSize(const JxlDecoder* dec,
2452
                                   const JxlPixelFormat* format,
2453
                                   size_t num_channels, size_t* min_size,
2454
157k
                                   bool preview) {
2455
157k
  size_t bits;
2456
157k
  JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
2457
157k
  if (status != JXL_DEC_SUCCESS) return status;
2458
157k
  size_t xsize;
2459
157k
  size_t ysize;
2460
157k
  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
157k
  } else {
2464
157k
    GetCurrentDimensions(dec, xsize, ysize);
2465
157k
  }
2466
157k
  if (num_channels == 0) num_channels = format->num_channels;
2467
157k
  size_t row_bits;
2468
157k
  if (!jxl::SafeMul<size_t>(xsize, num_channels, row_bits) ||
2469
157k
      !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
157k
  size_t row_size = jxl::DivCeil(row_bits, jxl::kBitsPerByte);
2473
157k
  const size_t last_row_size = row_size;
2474
157k
  if (format->align > 1) {
2475
0
    size_t aligned_rows = jxl::DivCeil(row_size, format->align);
2476
0
    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
0
  }
2481
2482
157k
  size_t total = 0;
2483
157k
  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
157k
  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
157k
  return JXL_DEC_SUCCESS;
2490
157k
}
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
157k
    const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
2532
157k
  if (format->num_channels < 3 &&
2533
446
      !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
157k
  return GetMinSize(dec, format, 0, size, false);
2538
157k
}
2539
2540
JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec,
2541
                                             const JxlPixelFormat* format,
2542
78.5k
                                             void* buffer, size_t size) {
2543
78.5k
  if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE) ||
2544
78.5k
      dec->preview_frame) {
2545
0
    return JXL_API_ERROR("No image out buffer needed at this time");
2546
0
  }
2547
78.5k
  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
78.5k
  if (format->num_channels < 3 &&
2552
223
      !dec->image_metadata.color_encoding.IsGray()) {
2553
0
    return JXL_API_ERROR("Number of channels is too low for color output");
2554
0
  }
2555
78.5k
  size_t min_size;
2556
  // This also checks whether the format is valid and supported and basic info
2557
  // is available.
2558
78.5k
  JxlDecoderStatus status =
2559
78.5k
      JxlDecoderImageOutBufferSize(dec, format, &min_size);
2560
78.5k
  if (status != JXL_DEC_SUCCESS) return status;
2561
2562
78.5k
  if (size < min_size) return JXL_DEC_ERROR;
2563
2564
78.5k
  dec->image_out_buffer_set = true;
2565
78.5k
  dec->image_out_buffer = buffer;
2566
78.5k
  dec->image_out_size = size;
2567
78.5k
  dec->image_out_format = *format;
2568
2569
78.5k
  return JXL_DEC_SUCCESS;
2570
78.5k
}
2571
2572
JxlDecoderStatus JxlDecoderExtraChannelBufferSize(const JxlDecoder* dec,
2573
                                                  const JxlPixelFormat* format,
2574
                                                  size_t* size,
2575
0
                                                  uint32_t index) {
2576
0
  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
0
  if (index >= dec->metadata.m.num_extra_channels) {
2581
0
    return JXL_API_ERROR("Invalid extra channel index");
2582
0
  }
2583
2584
0
  return GetMinSize(dec, format, 1, size, false);
2585
0
}
2586
2587
JxlDecoderStatus JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec,
2588
                                                 const JxlPixelFormat* format,
2589
                                                 void* buffer, size_t size,
2590
0
                                                 uint32_t index) {
2591
0
  size_t min_size;
2592
  // This also checks whether the format and index are valid and supported and
2593
  // basic info is available.
2594
0
  JxlDecoderStatus status =
2595
0
      JxlDecoderExtraChannelBufferSize(dec, format, &min_size, index);
2596
0
  if (status != JXL_DEC_SUCCESS) return status;
2597
2598
0
  if (size < min_size) return JXL_DEC_ERROR;
2599
2600
0
  if (dec->extra_channel_output.size() <= index) {
2601
0
    dec->extra_channel_output.resize(dec->metadata.m.num_extra_channels,
2602
0
                                     {{}, nullptr, 0});
2603
0
  }
2604
  // Guaranteed correct thanks to check in JxlDecoderExtraChannelBufferSize.
2605
0
  JXL_DASSERT(dec->extra_channel_output.size() > index);
2606
2607
0
  dec->extra_channel_output[index].format = *format;
2608
0
  dec->extra_channel_output[index].format.num_channels = 1;
2609
0
  dec->extra_channel_output[index].buffer = buffer;
2610
0
  dec->extra_channel_output[index].buffer_size = size;
2611
2612
0
  return JXL_DEC_SUCCESS;
2613
0
}
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
0
                                          JxlFrameHeader* header) {
2671
0
  if (!dec->frame_header || dec->frame_stage == FrameStage::kHeader) {
2672
0
    return JXL_API_ERROR("no frame header available");
2673
0
  }
2674
0
  const auto& metadata = dec->metadata.m;
2675
0
  memset(header, 0, sizeof(*header));
2676
0
  if (metadata.have_animation) {
2677
0
    header->duration = dec->frame_header->animation_frame.duration;
2678
0
    if (metadata.animation.have_timecodes) {
2679
0
      header->timecode = dec->frame_header->animation_frame.timecode;
2680
0
    }
2681
0
  }
2682
0
  header->name_length = dec->frame_header->name.size();
2683
0
  header->is_last = TO_JXL_BOOL(dec->frame_header->is_last);
2684
0
  size_t xsize;
2685
0
  size_t ysize;
2686
0
  GetCurrentDimensions(dec, xsize, ysize);
2687
0
  header->layer_info.xsize = xsize;
2688
0
  header->layer_info.ysize = ysize;
2689
0
  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
0
  } else {
2694
0
    header->layer_info.crop_x0 = 0;
2695
0
    header->layer_info.crop_y0 = 0;
2696
0
    header->layer_info.have_crop = JXL_FALSE;
2697
0
  }
2698
0
  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
0
  if (dec->coalescing) {
2714
0
    header->layer_info.blend_info.blendmode = JXL_BLEND_REPLACE;
2715
0
    header->layer_info.blend_info.source = 0;
2716
0
    header->layer_info.blend_info.alpha = 0;
2717
0
    header->layer_info.blend_info.clamp = JXL_FALSE;
2718
0
    header->layer_info.save_as_reference = 0;
2719
0
  } 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
0
  return JXL_DEC_SUCCESS;
2731
0
}
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
0
    JxlDecoder* dec, const JxlColorEncoding* color_encoding) {
2770
0
  return JxlDecoderSetOutputColorProfile(dec, color_encoding,
2771
0
                                         /*icc_data=*/nullptr, /*icc_size=*/0);
2772
0
}
2773
2774
JxlDecoderStatus JxlDecoderSetOutputColorProfile(
2775
    JxlDecoder* dec, const JxlColorEncoding* color_encoding,
2776
0
    const uint8_t* icc_data, size_t icc_size) {
2777
0
  if ((color_encoding != nullptr) && (icc_data != nullptr)) {
2778
0
    return JXL_API_ERROR("cannot set both color_encoding and icc_data");
2779
0
  }
2780
0
  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
0
  if (!dec->got_all_headers) {
2784
0
    return JXL_API_ERROR("color info not yet available");
2785
0
  }
2786
0
  if (dec->post_headers) {
2787
0
    return JXL_API_ERROR("too late to set the color encoding");
2788
0
  }
2789
0
  auto& output_encoding = dec->passes_state->output_encoding_info;
2790
0
  auto& orig_encoding = dec->image_metadata.color_encoding;
2791
0
  jxl::ColorEncoding c_out;
2792
0
  bool same_encoding = false;
2793
0
  if (color_encoding) {
2794
0
    JXL_API_RETURN_IF_ERROR(c_out.FromExternal(*color_encoding));
2795
0
    same_encoding = c_out.SameColorEncoding(output_encoding.color_encoding);
2796
0
  }
2797
0
  if ((!dec->passes_state->output_encoding_info.cms_set) &&
2798
0
      (icc_data != nullptr ||
2799
0
       (!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
0
  if (!orig_encoding.HaveFields() &&
2804
0
      dec->passes_state->output_encoding_info.cms_set) {
2805
0
    std::vector<uint8_t> tmp_icc = orig_encoding.ICC();
2806
0
    JXL_API_RETURN_IF_ERROR(orig_encoding.SetICC(
2807
0
        std::move(tmp_icc), &output_encoding.color_management_system));
2808
0
    output_encoding.orig_color_encoding = orig_encoding;
2809
0
  }
2810
0
  if (color_encoding) {
2811
0
    if (dec->image_metadata.color_encoding.IsGray() &&
2812
0
        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
0
    if (color_encoding->color_space == JXL_COLOR_SPACE_UNKNOWN) {
2817
0
      return JXL_API_ERROR("Unknown output colorspace");
2818
0
    }
2819
0
    JXL_API_RETURN_IF_ERROR(!c_out.ICC().empty());
2820
0
    JXL_API_RETURN_IF_ERROR(output_encoding.MaybeSetColorEncoding(c_out));
2821
0
    dec->image_metadata.color_encoding = output_encoding.color_encoding;
2822
0
    return JXL_DEC_SUCCESS;
2823
0
  }
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
59
                                        size_t size) {
2851
59
  if (dec->box_out_buffer_set) {
2852
0
    return JXL_API_ERROR("must release box buffer before setting it again");
2853
0
  }
2854
59
  if (!dec->box_event) {
2855
0
    return JXL_API_ERROR("can only set box buffer after box event");
2856
0
  }
2857
2858
59
  dec->box_out_buffer_set = true;
2859
59
  dec->box_out_buffer_set_current_box = true;
2860
59
  dec->box_out_buffer = data;
2861
59
  dec->box_out_buffer_size = size;
2862
59
  dec->box_out_buffer_pos = 0;
2863
59
  return JXL_DEC_SUCCESS;
2864
59
}
2865
2866
415k
size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec) {
2867
415k
  if (!dec->box_out_buffer_set) {
2868
415k
    return 0;
2869
415k
  }
2870
59
  size_t result = dec->box_out_buffer_size - dec->box_out_buffer_pos;
2871
59
  dec->box_out_buffer_set = false;
2872
59
  dec->box_out_buffer = nullptr;
2873
59
  dec->box_out_buffer_size = 0;
2874
59
  if (!dec->box_out_buffer_set_current_box) {
2875
43
    dec->box_out_buffer_begin = 0;
2876
43
  } else {
2877
16
    dec->box_out_buffer_begin += dec->box_out_buffer_pos;
2878
16
  }
2879
59
  dec->box_out_buffer_set_current_box = false;
2880
59
  return result;
2881
415k
}
2882
2883
JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
2884
0
                                              JXL_BOOL decompress) {
2885
  // TODO(lode): return error if libbrotli is not compiled in the jxl decoding
2886
  // library
2887
0
  dec->decompress_boxes = FROM_JXL_BOOL(decompress);
2888
0
  return JXL_DEC_SUCCESS;
2889
0
}
2890
2891
JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, JxlBoxType type,
2892
8.80k
                                      JXL_BOOL decompressed) {
2893
8.80k
  if (!dec->box_event) {
2894
0
    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
2895
0
  }
2896
8.80k
  if (decompressed) {
2897
0
    memcpy(type, dec->box_decoded_type, sizeof(dec->box_decoded_type));
2898
8.80k
  } else {
2899
8.80k
    memcpy(type, dec->box_type, sizeof(dec->box_type));
2900
8.80k
  }
2901
2902
8.80k
  return JXL_DEC_SUCCESS;
2903
8.80k
}
2904
2905
JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
2906
8.80k
                                         uint64_t* size) {
2907
8.80k
  if (!dec->box_event) {
2908
0
    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
2909
0
  }
2910
8.80k
  if (size) {
2911
8.80k
    *size = dec->box_size;
2912
8.80k
  }
2913
8.80k
  return JXL_DEC_SUCCESS;
2914
8.80k
}
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
}