Coverage Report

Created: 2026-03-31 06:56

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