Coverage Report

Created: 2024-09-08 07:14

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