Coverage Report

Created: 2026-06-07 07:20

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