Coverage Report

Created: 2026-09-14 07:15

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