Coverage Report

Created: 2026-05-16 07:22

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
53.6k
bool OutOfBounds(size_t a, size_t b, size_t size) {
61
53.6k
  size_t pos = a + b;
62
53.6k
  if (pos > size) return true;
63
52.0k
  if (pos < a) return true;  // overflow happened
64
52.0k
  return false;
65
52.0k
}
66
67
19.0k
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.0k
  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.0k
  const size_t max_codestream_basic_info_size = 50;
81
82
19.0k
  return container_header_size + max_codestream_basic_info_size;
83
19.0k
}
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.2k
  (((JXL_IS_DEBUG_BUILD) &&                                                    \
94
14.2k
    ::jxl::Debug(("%s:%d: " format "\n"), __FILE__, __LINE__, ##__VA_ARGS__)), \
95
14.2k
   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.2k
#define JXL_INPUT_ERROR(format, ...) JXL_API_ERROR(format, ##__VA_ARGS__)
101
102
258k
JxlDecoderStatus ConvertStatus(JxlDecoderStatus status) { return status; }
103
104
123k
JxlDecoderStatus ConvertStatus(jxl::Status status) {
105
123k
  return status ? JXL_DEC_SUCCESS : JXL_DEC_ERROR;
106
123k
}
107
108
#define JXL_API_RETURN_IF_ERROR(expr)               \
109
382k
  {                                                 \
110
382k
    JxlDecoderStatus status_ = ConvertStatus(expr); \
111
382k
    if (status_ != JXL_DEC_SUCCESS) return status_; \
112
382k
  }
113
114
37.8k
JxlSignature ReadSignature(const uint8_t* buf, size_t len, size_t* pos) {
115
37.8k
  if (*pos >= len) return JXL_SIG_NOT_ENOUGH_BYTES;
116
117
18.9k
  buf += *pos;
118
18.9k
  len -= *pos;
119
120
  // JPEG XL codestream: 0xff 0x0a
121
18.9k
  if (len >= 1 && buf[0] == 0xff) {
122
18.4k
    if (len < 2) {
123
1
      return JXL_SIG_NOT_ENOUGH_BYTES;
124
18.4k
    } else if (buf[1] == jxl::kCodestreamMarker) {
125
18.4k
      *pos += 2;
126
18.4k
      return JXL_SIG_CODESTREAM;
127
18.4k
    } else {
128
6
      return JXL_SIG_INVALID;
129
6
    }
130
18.4k
  }
131
132
  // JPEG XL container
133
445
  if (len >= 1 && buf[0] == 0) {
134
434
    if (len < 12) {
135
6
      return JXL_SIG_NOT_ENOUGH_BYTES;
136
428
    } else if (buf[1] == 0 && buf[2] == 0 && buf[3] == 0xC && buf[4] == 'J' &&
137
420
               buf[5] == 'X' && buf[6] == 'L' && buf[7] == ' ' &&
138
417
               buf[8] == 0xD && buf[9] == 0xA && buf[10] == 0x87 &&
139
414
               buf[11] == 0xA) {
140
413
      *pos += 12;
141
413
      return JXL_SIG_CONTAINER;
142
413
    } else {
143
15
      return JXL_SIG_INVALID;
144
15
    }
145
434
  }
146
147
11
  return JXL_SIG_INVALID;
148
445
}
149
150
}  // namespace
151
152
27
uint32_t JxlDecoderVersion(void) {
153
27
  return JPEGXL_MAJOR_VERSION * 1000000 + JPEGXL_MINOR_VERSION * 1000 +
154
27
         JPEGXL_PATCH_VERSION;
155
27
}
156
157
37.8k
JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len) {
158
37.8k
  size_t pos = 0;
159
37.8k
  return ReadSignature(buf, len, &pos);
160
37.8k
}
161
162
namespace {
163
164
20.0k
size_t BitsPerChannel(JxlDataType data_type) {
165
20.0k
  switch (data_type) {
166
14.5k
    case JXL_TYPE_UINT8:
167
14.5k
      return 8;
168
4.49k
    case JXL_TYPE_UINT16:
169
4.49k
      return 16;
170
1.00k
    case JXL_TYPE_FLOAT:
171
1.00k
      return 32;
172
0
    case JXL_TYPE_FLOAT16:
173
0
      return 16;
174
0
    default:
175
0
      return 0;  // signals unhandled JxlDataType
176
20.0k
  }
177
20.0k
}
178
179
template <typename T>
180
uint32_t GetBitDepth(JxlBitDepth bit_depth, const T& metadata,
181
6.93k
                     JxlPixelFormat format) {
182
6.93k
  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
183
6.93k
    return BitsPerChannel(format.data_type);
184
6.93k
  } 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
6.93k
}
decode.cc:unsigned int (anonymous namespace)::GetBitDepth<jxl::ImageMetadata>(JxlBitDepth, jxl::ImageMetadata const&, JxlPixelFormat)
Line
Count
Source
181
6.93k
                     JxlPixelFormat format) {
182
6.93k
  if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) {
183
6.93k
    return BitsPerChannel(format.data_type);
184
6.93k
  } 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
6.93k
}
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
18.9k
  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
145
  bool InjectNextBufferedJxlpBox() {
561
145
    auto it = jxlp_ooo_buffer.find(next_jxlp_index);
562
145
    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
145
  }
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.76k
  bool JbrdNeedMoreBoxes() const {
590
    // jbrd box wants exif but exif box not yet seen
591
5.76k
    if (store_exif < 2 && recon_exif_size > 0) return true;
592
    // jbrd box wants xmp but xmp box not yet seen
593
5.76k
    if (store_xmp < 2 && recon_xmp_size > 0) return true;
594
5.76k
    return false;
595
5.76k
  }
596
#endif
597
598
  const uint8_t* next_in;
599
  size_t avail_in;
600
  bool input_closed;
601
602
237k
  void AdvanceInput(size_t size) {
603
237k
    JXL_DASSERT(avail_in >= size);
604
237k
    next_in += size;
605
237k
    avail_in -= size;
606
237k
    file_pos += size;
607
237k
  }
608
609
320k
  size_t AvailableCodestream() const {
610
320k
    size_t avail_codestream = avail_in;
611
320k
    if (!box_contents_unbounded) {
612
1.83k
      avail_codestream =
613
1.83k
          std::min<size_t>(avail_codestream, box_contents_end - file_pos);
614
1.83k
    }
615
320k
    return avail_codestream;
616
320k
  }
617
618
149k
  void AdvanceCodestream(size_t size) {
619
149k
    size_t avail_codestream = AvailableCodestream();
620
149k
    if (codestream_copy.empty()) {
621
148k
      if (size <= avail_codestream) {
622
148k
        AdvanceInput(size);
623
148k
      } else {
624
124
        codestream_pos = size - avail_codestream;
625
124
        AdvanceInput(avail_codestream);
626
124
      }
627
148k
    } else {
628
608
      codestream_pos += size;
629
608
      if (codestream_pos + codestream_unconsumed >= codestream_copy.size()) {
630
8
        size_t advance = std::min(
631
8
            codestream_unconsumed,
632
8
            codestream_unconsumed + codestream_pos - codestream_copy.size());
633
8
        AdvanceInput(advance);
634
8
        codestream_pos -= std::min(codestream_pos, codestream_copy.size());
635
8
        codestream_unconsumed = 0;
636
8
        codestream_copy.clear();
637
8
      }
638
608
    }
639
149k
  }
640
641
12.7k
  JxlDecoderStatus RequestMoreInput() {
642
12.7k
    if (codestream_copy.empty()) {
643
7.46k
      size_t avail_codestream = AvailableCodestream();
644
7.46k
      codestream_copy.insert(codestream_copy.end(), next_in,
645
7.46k
                             next_in + avail_codestream);
646
7.46k
      AdvanceInput(avail_codestream);
647
7.46k
    } else {
648
5.31k
      AdvanceInput(codestream_unconsumed);
649
5.31k
      codestream_unconsumed = 0;
650
5.31k
    }
651
12.7k
    return JXL_DEC_NEED_MORE_INPUT;
652
12.7k
  }
653
654
163k
  JxlDecoderStatus GetCodestreamInput(jxl::Span<const uint8_t>* span) {
655
163k
    if (codestream_copy.empty() && codestream_pos > 0) {
656
248
      size_t avail_codestream = AvailableCodestream();
657
248
      size_t skip = std::min<size_t>(codestream_pos, avail_codestream);
658
248
      AdvanceInput(skip);
659
248
      codestream_pos -= skip;
660
248
      if (codestream_pos > 0) {
661
248
        return RequestMoreInput();
662
248
      }
663
248
    }
664
163k
    if (codestream_pos > codestream_copy.size()) {
665
0
      return JXL_API_ERROR("Internal: codestream_pos > codestream_copy.size()");
666
0
    }
667
163k
    if (codestream_unconsumed > codestream_copy.size()) {
668
0
      return JXL_API_ERROR(
669
0
          "Internal: codestream_unconsumed > codestream_copy.size()");
670
0
    }
671
163k
    size_t avail_codestream = AvailableCodestream();
672
163k
    if (codestream_copy.empty()) {
673
158k
      if (avail_codestream == 0) {
674
1.76k
        return RequestMoreInput();
675
1.76k
      }
676
156k
      *span = jxl::Bytes(next_in, avail_codestream);
677
156k
      return JXL_DEC_SUCCESS;
678
158k
    } else {
679
5.32k
      codestream_copy.insert(codestream_copy.end(),
680
5.32k
                             next_in + codestream_unconsumed,
681
5.32k
                             next_in + avail_codestream);
682
5.32k
      codestream_unconsumed = avail_codestream;
683
5.32k
      *span = jxl::Bytes(codestream_copy.data() + codestream_pos,
684
5.32k
                         codestream_copy.size() - codestream_pos);
685
5.32k
      return JXL_DEC_SUCCESS;
686
5.32k
    }
687
163k
  }
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.87k
  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.87k
    return stage != DecoderStage::kCodestreamFinished;
698
2.87k
  }
699
};
700
701
namespace {
702
703
61.0k
bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
704
61.0k
  if (xsize == 0 || ysize == 0) return true;
705
61.0k
  size_t padded_xsize = jxl::DivCeil(xsize, 32) * 32;
706
61.0k
  if (padded_xsize < xsize) return false;  // overflow
707
61.0k
  size_t num_pixels = padded_xsize * ysize;
708
61.0k
  if (num_pixels / padded_xsize != ysize) return false;  // overflow
709
61.0k
  return true;
710
61.0k
}
711
712
}  // namespace
713
714
// Resets the state that must be reset for both Rewind and Reset
715
18.9k
void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
716
18.9k
  dec->stage = DecoderStage::kInited;
717
18.9k
  dec->got_signature = false;
718
18.9k
  dec->last_codestream_seen = false;
719
18.9k
  dec->got_codestream_signature = false;
720
18.9k
  dec->got_basic_info = false;
721
18.9k
  dec->got_transform_data = false;
722
18.9k
  dec->got_all_headers = false;
723
18.9k
  dec->post_headers = false;
724
18.9k
  if (dec->icc_reader) dec->icc_reader->Reset();
725
18.9k
  dec->got_preview_image = false;
726
18.9k
  dec->preview_frame = false;
727
18.9k
  dec->file_pos = 0;
728
18.9k
  dec->box_contents_begin = 0;
729
18.9k
  dec->box_contents_end = 0;
730
18.9k
  dec->box_contents_size = 0;
731
18.9k
  dec->box_size = 0;
732
18.9k
  dec->header_size = 0;
733
18.9k
  dec->box_contents_unbounded = false;
734
18.9k
  memset(dec->box_type, 0, sizeof(dec->box_type));
735
18.9k
  memset(dec->box_decoded_type, 0, sizeof(dec->box_decoded_type));
736
18.9k
  dec->box_event = false;
737
18.9k
  dec->box_stage = BoxStage::kHeader;
738
18.9k
  dec->jxl_file_format_version = 0;
739
18.9k
  dec->next_jxlp_index = 0;
740
18.9k
  dec->jxlp_ooo_buffer.clear();
741
18.9k
  dec->buffering_jxlp_index = 0;
742
18.9k
  dec->buffering_jxlp_is_last = false;
743
18.9k
  dec->box_out_buffer_set = false;
744
18.9k
  dec->box_out_buffer_set_current_box = false;
745
18.9k
  dec->box_out_buffer = nullptr;
746
18.9k
  dec->box_out_buffer_size = 0;
747
18.9k
  dec->box_out_buffer_begin = 0;
748
18.9k
  dec->box_out_buffer_pos = 0;
749
750
18.9k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
751
18.9k
  dec->exif_metadata.clear();
752
18.9k
  dec->xmp_metadata.clear();
753
18.9k
  dec->store_exif = 0;
754
18.9k
  dec->store_xmp = 0;
755
18.9k
  dec->recon_out_buffer_pos = 0;
756
18.9k
  dec->recon_exif_size = 0;
757
18.9k
  dec->recon_xmp_size = 0;
758
18.9k
  dec->recon_output_jpeg = JpegReconStage::kNone;
759
18.9k
#endif
760
761
18.9k
  dec->events_wanted = dec->orig_events_wanted;
762
18.9k
  dec->basic_info_size_hint = InitialBasicInfoSizeHint();
763
18.9k
  dec->have_container = false;
764
18.9k
  dec->box_count = 0;
765
18.9k
  dec->downsampling_target = 8;
766
18.9k
  dec->image_out_buffer_set = false;
767
18.9k
  dec->image_out_buffer = nullptr;
768
18.9k
  dec->image_out_init_callback = nullptr;
769
18.9k
  dec->image_out_run_callback = nullptr;
770
18.9k
  dec->image_out_destroy_callback = nullptr;
771
18.9k
  dec->image_out_init_opaque = nullptr;
772
18.9k
  dec->image_out_size = 0;
773
18.9k
  dec->image_out_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT;
774
18.9k
  dec->extra_channel_output.clear();
775
18.9k
  dec->next_in = nullptr;
776
18.9k
  dec->avail_in = 0;
777
18.9k
  dec->input_closed = false;
778
779
18.9k
  dec->passes_state.reset();
780
18.9k
  dec->frame_dec.reset();
781
18.9k
  dec->next_section = 0;
782
18.9k
  dec->section_processed.clear();
783
784
18.9k
  dec->ib.reset();
785
18.9k
  dec->metadata = jxl::CodecMetadata();
786
18.9k
  dec->image_metadata = dec->metadata.m;
787
18.9k
  dec->frame_header = jxl::make_unique<jxl::FrameHeader>(&dec->metadata);
788
789
18.9k
  dec->codestream_copy.clear();
790
18.9k
  dec->codestream_unconsumed = 0;
791
18.9k
  dec->codestream_pos = 0;
792
18.9k
  dec->codestream_bits_ahead = 0;
793
794
18.9k
  dec->frame_stage = FrameStage::kHeader;
795
18.9k
  dec->remaining_frame_size = 0;
796
18.9k
  dec->is_last_of_still = false;
797
18.9k
  dec->is_last_total = false;
798
18.9k
  dec->skip_frames = 0;
799
18.9k
  dec->skipping_frame = false;
800
18.9k
  dec->internal_frames = 0;
801
18.9k
  dec->external_frames = 0;
802
18.9k
}
803
804
18.9k
void JxlDecoderReset(JxlDecoder* dec) {
805
18.9k
  JxlDecoderRewindDecodingState(dec);
806
807
18.9k
  dec->thread_pool.reset();
808
18.9k
  dec->keep_orientation = false;
809
18.9k
  dec->unpremul_alpha = false;
810
18.9k
  dec->render_spotcolors = true;
811
18.9k
  dec->coalescing = true;
812
18.9k
  dec->desired_intensity_target = 0;
813
18.9k
  dec->orig_events_wanted = 0;
814
18.9k
  dec->events_wanted = 0;
815
18.9k
  dec->frame_refs.clear();
816
18.9k
  dec->frame_external_to_internal.clear();
817
18.9k
  dec->frame_required.clear();
818
18.9k
  dec->decompress_boxes = false;
819
18.9k
}
820
821
18.9k
JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
822
18.9k
  JxlMemoryManager local_memory_manager;
823
18.9k
  if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager))
824
0
    return nullptr;
825
826
18.9k
  void* alloc =
827
18.9k
      jxl::MemoryManagerAlloc(&local_memory_manager, sizeof(JxlDecoder));
828
18.9k
  if (!alloc) return nullptr;
829
  // Placement new constructor on allocated memory
830
18.9k
  JxlDecoder* dec = new (alloc) JxlDecoder();
831
18.9k
  dec->memory_manager = local_memory_manager;
832
833
18.9k
  JxlDecoderReset(dec);
834
835
18.9k
  return dec;
836
18.9k
}
837
838
18.9k
void JxlDecoderDestroy(JxlDecoder* dec) {
839
18.9k
  if (dec) {
840
18.9k
    JxlMemoryManager local_memory_manager = dec->memory_manager;
841
    // Call destructor directly since custom free function is used.
842
18.9k
    dec->~JxlDecoder();
843
18.9k
    jxl::MemoryManagerFree(&local_memory_manager, dec);
844
18.9k
  }
845
18.9k
}
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
16.8k
                            void* parallel_runner_opaque) {
897
16.8k
  if (dec->stage != DecoderStage::kInited) {
898
0
    return JXL_API_ERROR(
899
0
        "JxlDecoderSetParallelRunner must be called before starting");
900
0
  }
901
16.8k
  dec->thread_pool = jxl::make_unique<jxl::ThreadPool>(parallel_runner,
902
16.8k
                                                       parallel_runner_opaque);
903
16.8k
  return JXL_DEC_SUCCESS;
904
16.8k
}
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
18.9k
JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, int events_wanted) {
912
18.9k
  if (dec->stage != DecoderStage::kInited) {
913
0
    return JXL_DEC_ERROR;  // Cannot subscribe to events after having started.
914
0
  }
915
18.9k
  if (events_wanted & 63) {
916
0
    return JXL_DEC_ERROR;  // Can only subscribe to informative events.
917
0
  }
918
18.9k
  dec->events_wanted = events_wanted;
919
18.9k
  dec->orig_events_wanted = events_wanted;
920
18.9k
  return JXL_DEC_SUCCESS;
921
18.9k
}
922
923
JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
924
18.9k
                                              JXL_BOOL skip_reorientation) {
925
18.9k
  if (dec->stage != DecoderStage::kInited) {
926
0
    return JXL_API_ERROR("Must set keep_orientation option before starting");
927
0
  }
928
18.9k
  dec->keep_orientation = FROM_JXL_BOOL(skip_reorientation);
929
18.9k
  return JXL_DEC_SUCCESS;
930
18.9k
}
931
932
JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec,
933
18.9k
                                                 JXL_BOOL unpremul_alpha) {
934
18.9k
  if (dec->stage != DecoderStage::kInited) {
935
0
    return JXL_API_ERROR("Must set unpremul_alpha option before starting");
936
0
  }
937
18.9k
  dec->unpremul_alpha = FROM_JXL_BOOL(unpremul_alpha);
938
18.9k
  return JXL_DEC_SUCCESS;
939
18.9k
}
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.0k
void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize) {
961
27.0k
  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.0k
  xsize = dec->metadata.oriented_xsize(dec->keep_orientation);
967
27.0k
  ysize = dec->metadata.oriented_ysize(dec->keep_orientation);
968
27.0k
  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.0k
}
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
56.2k
                            BitReader* reader, T* JXL_RESTRICT t) {
988
  // Use a copy of the bit reader because CanRead advances bits.
989
56.2k
  BitReader reader2(data);
990
56.2k
  reader2.SkipBits(reader->TotalBitsConsumed());
991
56.2k
  bool can_read = Bundle::CanRead(&reader2, t);
992
56.2k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
993
994
56.2k
  if (!can_read) {
995
2.17k
    return dec->RequestMoreInput();
996
2.17k
  }
997
54.0k
  if (!Bundle::Read(reader, t)) {
998
223
    return JXL_DEC_ERROR;
999
223
  }
1000
53.8k
  return JXL_DEC_SUCCESS;
1001
54.0k
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::SizeHeader>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::SizeHeader*)
Line
Count
Source
987
19.4k
                            BitReader* reader, T* JXL_RESTRICT t) {
988
  // Use a copy of the bit reader because CanRead advances bits.
989
19.4k
  BitReader reader2(data);
990
19.4k
  reader2.SkipBits(reader->TotalBitsConsumed());
991
19.4k
  bool can_read = Bundle::CanRead(&reader2, t);
992
19.4k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
993
994
19.4k
  if (!can_read) {
995
29
    return dec->RequestMoreInput();
996
29
  }
997
19.4k
  if (!Bundle::Read(reader, t)) {
998
0
    return JXL_DEC_ERROR;
999
0
  }
1000
19.4k
  return JXL_DEC_SUCCESS;
1001
19.4k
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::ImageMetadata>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::ImageMetadata*)
Line
Count
Source
987
19.4k
                            BitReader* reader, T* JXL_RESTRICT t) {
988
  // Use a copy of the bit reader because CanRead advances bits.
989
19.4k
  BitReader reader2(data);
990
19.4k
  reader2.SkipBits(reader->TotalBitsConsumed());
991
19.4k
  bool can_read = Bundle::CanRead(&reader2, t);
992
19.4k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
993
994
19.4k
  if (!can_read) {
995
1.68k
    return dec->RequestMoreInput();
996
1.68k
  }
997
17.7k
  if (!Bundle::Read(reader, t)) {
998
219
    return JXL_DEC_ERROR;
999
219
  }
1000
17.5k
  return JXL_DEC_SUCCESS;
1001
17.7k
}
decode.cc:JxlDecoderStatus jxl::(anonymous namespace)::ReadBundle<jxl::CustomTransformData>(JxlDecoder*, jxl::Span<unsigned char const>, jxl::BitReader*, jxl::CustomTransformData*)
Line
Count
Source
987
17.3k
                            BitReader* reader, T* JXL_RESTRICT t) {
988
  // Use a copy of the bit reader because CanRead advances bits.
989
17.3k
  BitReader reader2(data);
990
17.3k
  reader2.SkipBits(reader->TotalBitsConsumed());
991
17.3k
  bool can_read = Bundle::CanRead(&reader2, t);
992
17.3k
  JXL_API_RETURN_IF_ERROR(reader2.Close());
993
994
17.3k
  if (!can_read) {
995
462
    return dec->RequestMoreInput();
996
462
  }
997
16.9k
  if (!Bundle::Read(reader, t)) {
998
4
    return JXL_DEC_ERROR;
999
4
  }
1000
16.9k
  return JXL_DEC_SUCCESS;
1001
16.9k
}
1002
1003
std::unique_ptr<BitReader, std::function<void(BitReader*)>> GetBitReader(
1004
104k
    Span<const uint8_t> span) {
1005
104k
  BitReader* reader = new BitReader(span);
1006
104k
  return std::unique_ptr<BitReader, std::function<void(BitReader*)>>(
1007
104k
      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
104k
        (void)reader->AllReadsWithinBounds();
1013
104k
        (void)reader->Close();
1014
104k
        delete reader;
1015
104k
      });
1016
104k
}
1017
1018
19.4k
JxlDecoderStatus JxlDecoderReadBasicInfo(JxlDecoder* dec) {
1019
19.4k
  if (!dec->got_codestream_signature) {
1020
    // Check and skip the codestream signature
1021
18.6k
    Span<const uint8_t> span;
1022
18.6k
    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1023
18.6k
    if (span.size() < 2) {
1024
8
      return dec->RequestMoreInput();
1025
8
    }
1026
18.6k
    if (span.data()[0] != 0xff || span.data()[1] != jxl::kCodestreamMarker) {
1027
2
      return JXL_INPUT_ERROR("invalid signature");
1028
2
    }
1029
18.6k
    dec->got_codestream_signature = true;
1030
18.6k
    dec->AdvanceCodestream(2);
1031
18.6k
  }
1032
1033
19.4k
  Span<const uint8_t> span;
1034
19.4k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1035
19.4k
  auto reader = GetBitReader(span);
1036
19.4k
  JXL_API_RETURN_IF_ERROR(
1037
19.4k
      ReadBundle(dec, span, reader.get(), &dec->metadata.size));
1038
19.4k
  JXL_API_RETURN_IF_ERROR(
1039
19.4k
      ReadBundle(dec, span, reader.get(), &dec->metadata.m));
1040
17.5k
  size_t total_bits = reader->TotalBitsConsumed();
1041
17.5k
  dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
1042
17.5k
  dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
1043
17.5k
  dec->got_basic_info = true;
1044
17.5k
  dec->basic_info_size_hint = 0;
1045
17.5k
  dec->image_metadata = dec->metadata.m;
1046
17.5k
  JXL_DEBUG_V(2, "Decoded BasicInfo: %s", dec->metadata.DebugString().c_str());
1047
1048
17.5k
  if (!CheckSizeLimit(dec, dec->metadata.size.xsize(),
1049
17.5k
                      dec->metadata.size.ysize())) {
1050
0
    return JXL_INPUT_ERROR("image is too large");
1051
0
  }
1052
1053
17.5k
  return JXL_DEC_SUCCESS;
1054
17.5k
}
1055
1056
// Reads all codestream headers (but not frame headers)
1057
18.3k
JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) {
1058
18.3k
  if (!dec->got_transform_data) {
1059
17.4k
    Span<const uint8_t> span;
1060
17.4k
    JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1061
17.3k
    auto reader = GetBitReader(span);
1062
17.3k
    reader->SkipBits(dec->codestream_bits_ahead);
1063
17.3k
    dec->metadata.transform_data.nonserialized_xyb_encoded =
1064
17.3k
        dec->metadata.m.xyb_encoded;
1065
17.3k
    JXL_API_RETURN_IF_ERROR(
1066
17.3k
        ReadBundle(dec, span, reader.get(), &dec->metadata.transform_data));
1067
16.9k
    size_t total_bits = reader->TotalBitsConsumed();
1068
16.9k
    dec->AdvanceCodestream(total_bits / jxl::kBitsPerByte);
1069
16.9k
    dec->codestream_bits_ahead = total_bits % jxl::kBitsPerByte;
1070
16.9k
    dec->got_transform_data = true;
1071
16.9k
  }
1072
1073
17.8k
  Span<const uint8_t> span;
1074
17.8k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1075
17.7k
  auto reader = GetBitReader(span);
1076
17.7k
  reader->SkipBits(dec->codestream_bits_ahead);
1077
1078
17.7k
  if (dec->metadata.m.color_encoding.WantICC()) {
1079
2.86k
    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.86k
    if (!reader->AllReadsWithinBounds() ||
1085
1.57k
        status.code() == StatusCode::kNotEnoughBytes) {
1086
1.28k
      return dec->RequestMoreInput();
1087
1.28k
    }
1088
1.57k
    if (!status) {
1089
      // Other non-successful status is an error
1090
215
      return JXL_DEC_ERROR;
1091
215
    }
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
484
      return dec->RequestMoreInput();
1096
484
    }
1097
876
    if (!status) {
1098
      // Other non-successful status is an error
1099
839
      return JXL_DEC_ERROR;
1100
839
    }
1101
37
    if (decoded_icc.empty()) {
1102
2
      return JXL_DEC_ERROR;
1103
2
    }
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
14.9k
  dec->got_all_headers = true;
1110
14.9k
  JXL_API_RETURN_IF_ERROR(reader->JumpToByteBoundary());
1111
1112
14.9k
  dec->AdvanceCodestream(reader->TotalBitsConsumed() / jxl::kBitsPerByte);
1113
14.9k
  dec->codestream_bits_ahead = 0;
1114
1115
14.9k
  if (!dec->passes_state) {
1116
14.9k
    dec->passes_state =
1117
14.9k
        jxl::make_unique<jxl::PassesDecoderState>(&dec->memory_manager);
1118
14.9k
  }
1119
1120
14.9k
  JXL_API_RETURN_IF_ERROR(
1121
14.9k
      dec->passes_state->output_encoding_info.SetFromMetadata(dec->metadata));
1122
14.9k
  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
14.9k
  dec->image_metadata = dec->metadata.m;
1127
1128
14.9k
  return JXL_DEC_SUCCESS;
1129
14.9k
}
1130
1131
38.4k
JxlDecoderStatus JxlDecoderProcessSections(JxlDecoder* dec) {
1132
38.4k
  Span<const uint8_t> span;
1133
38.4k
  JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1134
38.2k
  const auto& toc = dec->frame_dec->Toc();
1135
38.2k
  size_t pos = 0;
1136
38.2k
  std::vector<jxl::FrameDecoder::SectionInfo> section_info;
1137
38.2k
  std::vector<jxl::FrameDecoder::SectionStatus> section_status;
1138
88.8k
  for (size_t i = dec->next_section; i < toc.size(); ++i) {
1139
52.1k
    if (dec->section_processed[i]) {
1140
40
      pos += toc[i].size;
1141
40
      continue;
1142
40
    }
1143
52.1k
    size_t id = toc[i].id;
1144
52.1k
    size_t size = toc[i].size;
1145
52.1k
    if (OutOfBounds(pos, size, span.size())) {
1146
1.56k
      break;
1147
1.56k
    }
1148
50.5k
    auto* br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size));
1149
50.5k
    section_info.emplace_back(jxl::FrameDecoder::SectionInfo{br, id, i});
1150
50.5k
    section_status.emplace_back();
1151
50.5k
    pos += size;
1152
50.5k
  }
1153
38.2k
  jxl::Status status = dec->frame_dec->ProcessSections(
1154
38.2k
      section_info.data(), section_info.size(), section_status.data());
1155
38.2k
  bool out_of_bounds = false;
1156
38.2k
  bool has_error = false;
1157
50.5k
  for (const auto& info : section_info) {
1158
50.5k
    if (!info.br->AllReadsWithinBounds()) {
1159
      // Mark out of bounds section, but keep closing and deleting the next
1160
      // ones as well.
1161
5.09k
      out_of_bounds = true;
1162
5.09k
    }
1163
50.5k
    if (!info.br->Close()) has_error = true;
1164
50.5k
    delete info.br;
1165
50.5k
  }
1166
38.2k
  if (has_error) {
1167
0
    return JXL_INPUT_ERROR("internal: bit-reader failed to close");
1168
0
  }
1169
38.2k
  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
4.97k
    return JXL_INPUT_ERROR("frame out of bounds");
1174
4.97k
  }
1175
33.2k
  if (!status) {
1176
1.40k
    return JXL_INPUT_ERROR("frame processing failed");
1177
1.40k
  }
1178
73.3k
  for (size_t i = 0; i < section_status.size(); ++i) {
1179
41.4k
    auto s_status = section_status[i];
1180
41.4k
    if (s_status == jxl::FrameDecoder::kDone) {
1181
41.2k
      dec->section_processed[section_info[i].index] = 1;
1182
41.2k
    } else if (s_status != jxl::FrameDecoder::kSkipped) {
1183
0
      return JXL_INPUT_ERROR("unexpected section status");
1184
0
    }
1185
41.4k
  }
1186
31.8k
  size_t completed_prefix_bytes = 0;
1187
73.0k
  while (dec->next_section < dec->section_processed.size() &&
1188
42.4k
         dec->section_processed[dec->next_section] == 1) {
1189
41.1k
    completed_prefix_bytes += toc[dec->next_section].size;
1190
41.1k
    ++dec->next_section;
1191
41.1k
  }
1192
31.8k
  dec->remaining_frame_size -= completed_prefix_bytes;
1193
31.8k
  dec->AdvanceCodestream(completed_prefix_bytes);
1194
31.8k
  return JXL_DEC_SUCCESS;
1195
31.8k
}
1196
1197
// TODO(eustas): no CodecInOut -> no image size reinforcement -> possible OOM.
1198
72.8k
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
72.8k
  if (!dec->thread_pool) {
1203
1.97k
    dec->thread_pool = jxl::make_unique<jxl::ThreadPool>(nullptr, nullptr);
1204
1.97k
  }
1205
1206
  // No matter what events are wanted, the basic info is always required.
1207
72.8k
  if (!dec->got_basic_info) {
1208
19.4k
    JxlDecoderStatus status = JxlDecoderReadBasicInfo(dec);
1209
19.4k
    if (status != JXL_DEC_SUCCESS) return status;
1210
19.4k
  }
1211
1212
70.9k
  if (dec->events_wanted & JXL_DEC_BASIC_INFO) {
1213
17.5k
    dec->events_wanted &= ~JXL_DEC_BASIC_INFO;
1214
17.5k
    return JXL_DEC_BASIC_INFO;
1215
17.5k
  }
1216
1217
53.4k
  if (!dec->events_wanted) {
1218
0
    dec->stage = DecoderStage::kCodestreamFinished;
1219
0
    return JXL_DEC_SUCCESS;
1220
0
  }
1221
1222
53.4k
  if (!dec->icc_reader) {
1223
17.1k
    dec->icc_reader = jxl::make_unique<ICCReader>(&dec->memory_manager);
1224
17.1k
  }
1225
1226
53.4k
  if (!dec->got_all_headers) {
1227
18.3k
    JxlDecoderStatus status = JxlDecoderReadAllHeaders(dec);
1228
18.3k
    if (status != JXL_DEC_SUCCESS) return status;
1229
18.3k
  }
1230
1231
50.0k
  if (dec->events_wanted & JXL_DEC_COLOR_ENCODING) {
1232
13.9k
    dec->events_wanted &= ~JXL_DEC_COLOR_ENCODING;
1233
13.9k
    return JXL_DEC_COLOR_ENCODING;
1234
13.9k
  }
1235
1236
36.0k
  if (!dec->events_wanted) {
1237
0
    dec->stage = DecoderStage::kCodestreamFinished;
1238
0
    return JXL_DEC_SUCCESS;
1239
0
  }
1240
1241
36.0k
  dec->post_headers = true;
1242
1243
36.0k
  if (!dec->got_preview_image && dec->metadata.m.have_preview) {
1244
195
    dec->preview_frame = true;
1245
195
  }
1246
1247
  // Handle frames
1248
69.2k
  for (;;) {
1249
69.2k
    bool parse_frames =
1250
69.2k
        (dec->events_wanted &
1251
69.2k
         (JXL_DEC_PREVIEW_IMAGE | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
1252
69.2k
    if (!parse_frames) {
1253
0
      break;
1254
0
    }
1255
69.2k
    if (dec->frame_stage == FrameStage::kHeader && dec->is_last_total) {
1256
2.85k
      break;
1257
2.85k
    }
1258
66.3k
    if (dec->frame_stage == FrameStage::kHeader) {
1259
52.0k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1260
52.0k
      if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata ||
1261
52.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
52.0k
#endif
1270
52.0k
      if (!dec->ib) {
1271
42.6k
        dec->ib = jxl::make_unique<jxl::ImageBundle>(&dec->memory_manager,
1272
42.6k
                                                     &dec->image_metadata);
1273
42.6k
      }
1274
52.0k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1275
      // If JPEG reconstruction is wanted and possible, set the jpeg_data of
1276
      // the ImageBundle.
1277
52.0k
      if (!dec->jpeg_decoder.SetImageBundleJpegData(dec->ib.get()))
1278
0
        return JXL_DEC_ERROR;
1279
52.0k
#endif
1280
52.0k
      dec->frame_dec = jxl::make_unique<FrameDecoder>(
1281
52.0k
          dec->passes_state.get(), dec->metadata, dec->thread_pool.get(),
1282
52.0k
          /*use_slow_rendering_pipeline=*/false);
1283
52.0k
      dec->frame_header = jxl::make_unique<FrameHeader>(&dec->metadata);
1284
52.0k
      Span<const uint8_t> span;
1285
52.0k
      JXL_API_RETURN_IF_ERROR(dec->GetCodestreamInput(&span));
1286
50.3k
      auto reader = GetBitReader(span);
1287
50.3k
      jxl::Status status = dec->frame_dec->InitFrame(
1288
50.3k
          reader.get(), dec->ib.get(), dec->preview_frame);
1289
50.3k
      if (!reader->AllReadsWithinBounds() ||
1290
45.3k
          status.code() == StatusCode::kNotEnoughBytes) {
1291
5.56k
        return dec->RequestMoreInput();
1292
44.7k
      } else if (!status) {
1293
1.25k
        return JXL_INPUT_ERROR("invalid frame header");
1294
1.25k
      }
1295
43.4k
      dec->AdvanceCodestream(reader->TotalBitsConsumed() / kBitsPerByte);
1296
43.4k
      *dec->frame_header = dec->frame_dec->GetFrameHeader();
1297
43.4k
      jxl::FrameDimensions frame_dim = dec->frame_header->ToFrameDimensions();
1298
43.4k
      if (!CheckSizeLimit(dec, frame_dim.xsize_upsampled_padded,
1299
43.4k
                          frame_dim.ysize_upsampled_padded)) {
1300
0
        return JXL_INPUT_ERROR("frame is too large");
1301
0
      }
1302
43.4k
      int output_type =
1303
43.4k
          dec->preview_frame ? JXL_DEC_PREVIEW_IMAGE : JXL_DEC_FULL_IMAGE;
1304
43.4k
      bool output_needed = ((dec->events_wanted & output_type) != 0);
1305
43.4k
      if (output_needed) {
1306
37.7k
        JXL_API_RETURN_IF_ERROR(dec->frame_dec->InitFrameOutput());
1307
37.6k
      }
1308
43.4k
      dec->remaining_frame_size = dec->frame_dec->SumSectionSizes();
1309
1310
43.4k
      dec->frame_stage = FrameStage::kTOC;
1311
43.4k
      if (dec->preview_frame) {
1312
117
        if (!(dec->events_wanted & JXL_DEC_PREVIEW_IMAGE)) {
1313
117
          dec->frame_stage = FrameStage::kHeader;
1314
117
          dec->AdvanceCodestream(dec->remaining_frame_size);
1315
117
          dec->got_preview_image = true;
1316
117
          dec->preview_frame = false;
1317
117
        }
1318
117
        continue;
1319
117
      }
1320
1321
43.3k
      int saved_as = FrameDecoder::SavedAs(*dec->frame_header);
1322
      // is last in entire codestream
1323
43.3k
      dec->is_last_total = dec->frame_header->is_last;
1324
      // is last of current still
1325
43.3k
      dec->is_last_of_still =
1326
43.3k
          dec->is_last_total || dec->frame_header->animation_frame.duration > 0;
1327
      // is kRegularFrame and coalescing is disabled
1328
43.3k
      dec->is_last_of_still |=
1329
43.3k
          (!dec->coalescing &&
1330
0
           dec->frame_header->frame_type == FrameType::kRegularFrame);
1331
43.3k
      const size_t internal_frame_index = dec->internal_frames;
1332
43.3k
      const size_t external_frame_index = dec->external_frames;
1333
43.3k
      if (dec->is_last_of_still) dec->external_frames++;
1334
43.3k
      dec->internal_frames++;
1335
1336
43.3k
      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
43.3k
      } else {
1342
43.3k
        dec->skipping_frame = false;
1343
43.3k
      }
1344
1345
43.3k
      if (external_frame_index >= dec->frame_external_to_internal.size()) {
1346
12.7k
        dec->frame_external_to_internal.push_back(internal_frame_index);
1347
12.7k
        if (dec->frame_external_to_internal.size() !=
1348
12.7k
            external_frame_index + 1) {
1349
0
          return JXL_API_ERROR("internal");
1350
0
        }
1351
12.7k
      }
1352
1353
43.3k
      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
43.3k
        dec->frame_refs.emplace_back(FrameRef{0xFF, saved_as});
1359
43.3k
        if (dec->frame_refs.size() != internal_frame_index + 1) {
1360
0
          return JXL_API_ERROR("internal");
1361
0
        }
1362
43.3k
      }
1363
1364
43.3k
      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
43.3k
      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
6.91k
        if (!dec->skipping_frame) {
1389
6.91k
          return JXL_DEC_FRAME;
1390
6.91k
        }
1391
6.91k
      }
1392
43.3k
    }
1393
1394
50.8k
    if (dec->frame_stage == FrameStage::kTOC) {
1395
43.3k
      dec->frame_dec->SetRenderSpotcolors(dec->render_spotcolors);
1396
43.3k
      dec->frame_dec->SetCoalescing(dec->coalescing);
1397
1398
43.3k
      if (!dec->preview_frame &&
1399
43.3k
          (dec->events_wanted & JXL_DEC_FRAME_PROGRESSION)) {
1400
0
        dec->frame_prog_detail =
1401
0
            dec->frame_dec->SetPauseAtProgressive(dec->prog_detail);
1402
43.3k
      } else {
1403
43.3k
        dec->frame_prog_detail = JxlProgressiveDetail::kFrames;
1404
43.3k
      }
1405
43.3k
      dec->dc_frame_progression_done = false;
1406
1407
43.3k
      dec->next_section = 0;
1408
43.3k
      dec->section_processed.clear();
1409
43.3k
      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
43.3k
      if (dec->preview_frame || (dec->events_wanted & JXL_DEC_FULL_IMAGE)) {
1413
37.6k
        dec->frame_stage = FrameStage::kFull;
1414
37.6k
      } else if (!dec->is_last_total) {
1415
5.63k
        dec->frame_stage = FrameStage::kHeader;
1416
5.63k
        dec->AdvanceCodestream(dec->remaining_frame_size);
1417
5.63k
        continue;
1418
5.63k
      } else {
1419
21
        break;
1420
21
      }
1421
43.3k
    }
1422
1423
45.1k
    if (dec->frame_stage == FrameStage::kFull) {
1424
45.1k
      if (!dec->image_out_buffer_set) {
1425
38.2k
        if (dec->preview_frame) {
1426
0
          return JXL_DEC_NEED_PREVIEW_OUT_BUFFER;
1427
0
        }
1428
38.2k
        if (
1429
38.2k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1430
38.2k
            (!dec->jpeg_decoder.IsOutputSet() ||
1431
0
             dec->ib->jpeg_data == nullptr) &&
1432
38.2k
#endif
1433
38.2k
            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.72k
          return JXL_DEC_NEED_IMAGE_OUT_BUFFER;
1438
6.72k
        }
1439
38.2k
      }
1440
1441
38.4k
      if (dec->image_out_buffer_set) {
1442
6.93k
        size_t xsize;
1443
6.93k
        size_t ysize;
1444
6.93k
        GetCurrentDimensions(dec, xsize, ysize);
1445
6.93k
        size_t bits_per_sample = GetBitDepth(
1446
6.93k
            dec->image_out_bit_depth, dec->metadata.m, dec->image_out_format);
1447
6.93k
        dec->frame_dec->SetImageOutput(
1448
6.93k
            PixelCallback{
1449
6.93k
                dec->image_out_init_callback, dec->image_out_run_callback,
1450
6.93k
                dec->image_out_destroy_callback, dec->image_out_init_opaque},
1451
6.93k
            reinterpret_cast<uint8_t*>(dec->image_out_buffer),
1452
6.93k
            dec->image_out_size, xsize, ysize, dec->image_out_format,
1453
6.93k
            bits_per_sample, dec->unpremul_alpha, !dec->keep_orientation);
1454
6.93k
        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
6.93k
      }
1464
1465
38.4k
      size_t next_num_passes_to_pause = dec->frame_dec->NextNumPassesToPause();
1466
1467
38.4k
      JXL_API_RETURN_IF_ERROR(JxlDecoderProcessSections(dec));
1468
1469
31.8k
      bool all_sections_done = dec->frame_dec->HasDecodedAll();
1470
31.8k
      bool got_dc_only = !all_sections_done && dec->frame_dec->HasDecodedDC();
1471
1472
31.8k
      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
31.8k
      bool new_progression_step_done =
1480
31.8k
          dec->frame_dec->NumCompletePasses() >= next_num_passes_to_pause;
1481
1482
31.8k
      if (!all_sections_done &&
1483
1.25k
          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
31.8k
      if (!all_sections_done) {
1492
        // Not all sections have been processed yet
1493
1.25k
        return dec->RequestMoreInput();
1494
1.25k
      }
1495
1496
30.5k
      if (!dec->preview_frame) {
1497
30.5k
        size_t internal_index = dec->internal_frames - 1;
1498
30.5k
        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
30.5k
        dec->frame_refs[internal_index].reference =
1505
30.5k
            dec->frame_dec->References();
1506
30.5k
      }
1507
1508
30.5k
      if (!dec->frame_dec->FinalizeFrame()) {
1509
0
        return JXL_INPUT_ERROR("decoding frame failed");
1510
0
      }
1511
30.5k
#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
30.5k
      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
30.5k
#endif
1520
30.5k
      if (dec->preview_frame || dec->is_last_of_still) {
1521
3.17k
        dec->image_out_buffer_set = false;
1522
3.17k
        dec->extra_channel_output.clear();
1523
3.17k
      }
1524
30.5k
    }
1525
1526
30.5k
    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
30.5k
    dec->ib.reset();
1531
30.5k
    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
30.5k
    } else if (dec->is_last_of_still &&
1537
3.17k
               (dec->events_wanted & JXL_DEC_FULL_IMAGE) &&
1538
3.17k
               !dec->skipping_frame) {
1539
3.17k
      return JXL_DEC_FULL_IMAGE;
1540
3.17k
    }
1541
30.5k
  }
1542
1543
2.87k
  dec->stage = DecoderStage::kCodestreamFinished;
1544
  // Return success, this means there is nothing more to do.
1545
2.87k
  return JXL_DEC_SUCCESS;
1546
36.0k
}
1547
1548
}  // namespace
1549
}  // namespace jxl
1550
1551
JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data,
1552
18.9k
                                    size_t size) {
1553
18.9k
  if (dec->next_in) {
1554
0
    return JXL_API_ERROR("already set input, use JxlDecoderReleaseInput first");
1555
0
  }
1556
18.9k
  if (dec->input_closed) {
1557
0
    return JXL_API_ERROR("input already closed");
1558
0
  }
1559
1560
18.9k
  dec->next_in = data;
1561
18.9k
  dec->avail_in = size;
1562
18.9k
  return JXL_DEC_SUCCESS;
1563
18.9k
}
1564
1565
25.3k
size_t JxlDecoderReleaseInput(JxlDecoder* dec) {
1566
25.3k
  size_t result = dec->avail_in;
1567
25.3k
  dec->next_in = nullptr;
1568
25.3k
  dec->avail_in = 0;
1569
25.3k
  return result;
1570
25.3k
}
1571
1572
6.46k
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.46k
                                       uint64_t* header_size) {
1620
1.46k
  if (OutOfBounds(pos, 8, size)) {
1621
30
    *header_size = 8;
1622
30
    return JXL_DEC_NEED_MORE_INPUT;
1623
30
  }
1624
1.43k
  size_t box_start = pos;
1625
  // Box size, including this header itself.
1626
1.43k
  *box_size = LoadBE32(in + pos);
1627
1.43k
  pos += 4;
1628
1.43k
  memcpy(type, in + pos, 4);
1629
1.43k
  pos += 4;
1630
1.43k
  if (*box_size == 1) {
1631
100
    *header_size = 16;
1632
100
    if (OutOfBounds(pos, 8, size)) return JXL_DEC_NEED_MORE_INPUT;
1633
97
    *box_size = LoadBE64(in + pos);
1634
97
    pos += 8;
1635
97
  }
1636
1.42k
  *header_size = pos - box_start;
1637
1.42k
  if (*box_size > 0 && *box_size < *header_size) {
1638
3
    return JXL_INPUT_ERROR("invalid box size");
1639
3
  }
1640
1.42k
  if (file_pos + *box_size < file_pos) {
1641
1
    return JXL_INPUT_ERROR("Box size overflow");
1642
1
  }
1643
1.42k
  return JXL_DEC_SUCCESS;
1644
1.42k
}
1645
1646
// This includes handling the codestream if it is not a box-based jxl file.
1647
74.5k
static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
1648
  // Box handling loop
1649
94.4k
  for (;;) {
1650
94.4k
    if (dec->box_stage != BoxStage::kHeader) {
1651
74.4k
      dec->AdvanceInput(dec->header_size);
1652
74.4k
      dec->header_size = 0;
1653
74.4k
#if JPEGXL_ENABLE_BOXES
1654
74.4k
      if ((dec->events_wanted & JXL_DEC_BOX) && dec->box_event &&
1655
1.28k
          !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.15k
        dec->box_event = false;
1660
1.15k
      }
1661
74.4k
      if ((dec->events_wanted & JXL_DEC_BOX) &&
1662
74.4k
          dec->box_out_buffer_set_current_box) {
1663
127
        uint8_t* next_out = dec->box_out_buffer + dec->box_out_buffer_pos;
1664
127
        size_t avail_out = dec->box_out_buffer_size - dec->box_out_buffer_pos;
1665
1666
127
        JxlDecoderStatus box_result = dec->box_content_decoder.Process(
1667
127
            dec->next_in, dec->avail_in,
1668
127
            dec->file_pos - dec->box_contents_begin, &next_out, &avail_out);
1669
127
        size_t produced =
1670
127
            next_out - (dec->box_out_buffer + dec->box_out_buffer_pos);
1671
127
        dec->box_out_buffer_pos += produced;
1672
1673
127
        if (box_result == JXL_DEC_BOX_COMPLETE &&
1674
91
            !(dec->events_wanted & JXL_DEC_BOX_COMPLETE)) {
1675
91
          box_result = JXL_DEC_SUCCESS;
1676
91
        }
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
127
        if (box_result != JXL_DEC_SUCCESS &&
1682
36
            box_result != JXL_DEC_NEED_MORE_INPUT) {
1683
0
          return box_result;
1684
0
        }
1685
127
      }
1686
74.4k
#endif
1687
74.4k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1688
74.4k
      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
74.4k
#endif
1726
74.4k
    }
1727
94.4k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1728
94.4k
    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
94.4k
    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
94.4k
#endif
1759
1760
94.4k
    if (dec->box_stage == BoxStage::kHeader) {
1761
19.9k
      if (!dec->have_container) {
1762
18.4k
        if (dec->stage == DecoderStage::kCodestreamFinished)
1763
0
          return JXL_DEC_SUCCESS;
1764
18.4k
        dec->box_stage = BoxStage::kCodestream;
1765
18.4k
        dec->box_contents_unbounded = true;
1766
18.4k
        continue;
1767
18.4k
      }
1768
1.50k
      if (dec->avail_in == 0) {
1769
46
        if (dec->stage != DecoderStage::kCodestreamFinished) {
1770
          // Not yet seen (all) codestream boxes.
1771
42
          return JXL_DEC_NEED_MORE_INPUT;
1772
42
        }
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.46k
      bool boxed_codestream_done =
1796
1.46k
          ((dec->events_wanted & JXL_DEC_BOX) &&
1797
1.46k
           dec->stage == DecoderStage::kCodestreamFinished &&
1798
4
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1799
4
           !dec->JbrdNeedMoreBoxes() &&
1800
4
#endif
1801
4
           dec->last_codestream_seen);
1802
1.46k
      if (boxed_codestream_done && dec->avail_in >= 2 &&
1803
3
          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.46k
      uint64_t box_size;
1811
1.46k
      uint64_t header_size;
1812
1.46k
      JxlDecoderStatus status =
1813
1.46k
          ParseBoxHeader(dec->next_in, dec->avail_in, 0, dec->file_pos,
1814
1.46k
                         dec->box_type, &box_size, &header_size);
1815
1.46k
      if (status != JXL_DEC_SUCCESS) {
1816
37
        if (status == JXL_DEC_NEED_MORE_INPUT) {
1817
33
          dec->basic_info_size_hint =
1818
33
              InitialBasicInfoSizeHint() + header_size - dec->file_pos;
1819
33
        }
1820
37
        return status;
1821
37
      }
1822
1.42k
      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.42k
      } else {
1829
1.42k
        memcpy(dec->box_decoded_type, dec->box_type,
1830
1.42k
               sizeof(dec->box_decoded_type));
1831
1.42k
      }
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.42k
      dec->box_count++;
1837
1.42k
      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.42k
      if (dec->box_count == 2 && memcmp(dec->box_type, "ftyp", 4) != 0) {
1843
79
        return JXL_INPUT_ERROR("the second box must be the ftyp box");
1844
79
      }
1845
1.34k
      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.34k
      dec->box_contents_unbounded = (box_size == 0);
1850
1.34k
      dec->box_contents_begin = dec->file_pos + header_size;
1851
1.34k
      dec->box_contents_end =
1852
1.34k
          dec->box_contents_unbounded ? 0 : (dec->file_pos + box_size);
1853
1.34k
      dec->box_contents_size =
1854
1.34k
          dec->box_contents_unbounded ? 0 : (box_size - header_size);
1855
1.34k
      dec->box_size = box_size;
1856
1.34k
      dec->header_size = header_size;
1857
1.34k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1858
1.34k
      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.34k
#endif
1872
1.34k
#if JPEGXL_ENABLE_BOXES
1873
1.34k
      if (dec->events_wanted & JXL_DEC_BOX) {
1874
1.34k
        bool decompress =
1875
1.34k
            dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
1876
1.34k
        dec->box_content_decoder.StartBox(
1877
1.34k
            decompress, dec->box_contents_unbounded, dec->box_contents_size);
1878
1.34k
      }
1879
1.34k
#endif
1880
1.34k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1881
1.34k
      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.34k
#endif
1887
1.34k
      if (memcmp(dec->box_type, "ftyp", 4) == 0) {
1888
324
        dec->box_stage = BoxStage::kFtyp;
1889
1.01k
      } else if (memcmp(dec->box_type, "jxlc", 4) == 0) {
1890
34
        if (dec->last_codestream_seen) {
1891
0
          return JXL_INPUT_ERROR("there can only be one jxlc box");
1892
0
        }
1893
34
        dec->last_codestream_seen = true;
1894
34
        dec->box_stage = BoxStage::kCodestream;
1895
983
      } else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
1896
213
        dec->box_stage = BoxStage::kPartialCodestream;
1897
213
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1898
770
      } 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
770
      } else {
1907
770
        dec->box_stage = BoxStage::kSkip;
1908
770
      }
1909
1910
1.34k
      if (dec->events_wanted & JXL_DEC_BOX) {
1911
1.34k
        dec->box_event = true;
1912
1.34k
        dec->box_out_buffer_set_current_box = false;
1913
1.34k
        return JXL_DEC_BOX;
1914
1.34k
      }
1915
74.4k
    } else if (dec->box_stage == BoxStage::kFtyp) {
1916
362
      if (dec->box_contents_size < 12) {
1917
2
        return JXL_INPUT_ERROR("file type box too small");
1918
2
      }
1919
360
      if (dec->avail_in < 8) return JXL_DEC_NEED_MORE_INPUT;
1920
278
      if (memcmp(dec->next_in, "jxl ", 4) != 0) {
1921
0
        return JXL_INPUT_ERROR("file type box major brand must be \"jxl \"");
1922
0
      }
1923
278
      uint32_t version = LoadBE32(dec->next_in + 4);
1924
278
      if (version > 1) {
1925
34
        return JXL_INPUT_ERROR(
1926
34
            "unknown jxl file format version %u (known versions: 0, 1)",
1927
34
            version);
1928
34
      }
1929
244
      dec->jxl_file_format_version = version;
1930
244
      dec->AdvanceInput(8);
1931
244
      dec->box_stage = BoxStage::kSkip;
1932
74.1k
    } else if (dec->box_stage == BoxStage::kPartialCodestream) {
1933
214
      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
214
      if (dec->avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
1938
212
      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
211
      uint32_t jxlp_index = LoadBE32(dec->next_in);
1942
211
      uint32_t counter = jxlp_index & 0x7FFFFFFF;
1943
211
      bool is_last = (jxlp_index & 0x80000000) != 0;
1944
1945
211
      if (counter < dec->next_jxlp_index) {
1946
10
        return JXL_INPUT_ERROR(
1947
10
            "jxlp box index %u is a duplicate (already processed)", counter);
1948
10
      }
1949
201
      dec->AdvanceInput(4);  // consume the 4-byte jxlp counter header
1950
1951
201
      if (counter == dec->next_jxlp_index) {
1952
159
        dec->next_jxlp_index++;
1953
159
        if (is_last) dec->last_codestream_seen = true;
1954
159
        dec->box_stage = BoxStage::kCodestream;
1955
159
      } else if (dec->jxl_file_format_version >= 1) {
1956
        // Out-of-order box (version 1+): buffer payload for later injection.
1957
22
        dec->buffering_jxlp_index = counter;
1958
22
        dec->buffering_jxlp_is_last = is_last;
1959
22
        dec->jxlp_ooo_buffer.emplace(
1960
22
            counter, std::make_pair(std::vector<uint8_t>(), is_last));
1961
22
        dec->box_stage = BoxStage::kBufferingJxlp;
1962
22
      } else {
1963
20
        return JXL_INPUT_ERROR(
1964
20
            "jxlp box index %u is out of order (expected %u); out-of-order "
1965
20
            "jxlp boxes require file format version 1 in the ftyp box",
1966
20
            counter, dec->next_jxlp_index);
1967
20
      }
1968
73.9k
    } else if (dec->box_stage == BoxStage::kCodestream) {
1969
72.8k
      JxlDecoderStatus status = jxl::JxlDecoderProcessCodestream(dec);
1970
72.8k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1971
72.8k
      if (status == JXL_DEC_FULL_IMAGE) {
1972
3.17k
        if (dec->recon_output_jpeg != JpegReconStage::kNone) {
1973
0
          continue;
1974
0
        }
1975
3.17k
      }
1976
72.8k
#endif
1977
72.8k
      if (status == JXL_DEC_NEED_MORE_INPUT) {
1978
12.7k
        if (dec->file_pos == dec->box_contents_end &&
1979
145
            !dec->box_contents_unbounded) {
1980
          // Physical box exhausted; inject the next buffered OOO box if ready.
1981
145
          if (dec->InjectNextBufferedJxlpBox()) continue;
1982
145
          dec->box_stage = BoxStage::kHeader;
1983
145
          continue;
1984
145
        }
1985
12.7k
      }
1986
1987
72.7k
      if (status == JXL_DEC_SUCCESS) {
1988
2.87k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
1989
2.87k
        if (dec->JbrdNeedMoreBoxes()) {
1990
0
          dec->box_stage = BoxStage::kSkip;
1991
0
          continue;
1992
0
        }
1993
2.87k
#endif
1994
2.87k
        if (dec->box_contents_unbounded) {
1995
          // Last box reached and codestream done, nothing more to do.
1996
2.87k
          break;
1997
2.87k
        }
1998
6
        if (dec->events_wanted & JXL_DEC_BOX) {
1999
          // Codestream done, but there may be more other boxes.
2000
6
          dec->box_stage = BoxStage::kSkip;
2001
6
          continue;
2002
6
        }
2003
6
      }
2004
69.8k
      return status;
2005
72.7k
    } else if (dec->box_stage == BoxStage::kBufferingJxlp) {
2006
26
      size_t remaining =
2007
26
          dec->box_contents_unbounded
2008
26
              ? dec->avail_in
2009
26
              : std::min<size_t>(dec->avail_in,
2010
26
                                 dec->box_contents_end - dec->file_pos);
2011
26
      auto& entry = dec->jxlp_ooo_buffer[dec->buffering_jxlp_index];
2012
26
      entry.first.insert(entry.first.end(), dec->next_in,
2013
26
                         dec->next_in + remaining);
2014
26
      dec->AdvanceInput(remaining);
2015
2016
26
      bool box_done = !dec->box_contents_unbounded &&
2017
26
                      dec->file_pos >= dec->box_contents_end;
2018
26
      if (!box_done) {
2019
8
        return JXL_DEC_NEED_MORE_INPUT;
2020
8
      }
2021
      // Box fully buffered; parse the next box header.
2022
18
      dec->box_stage = BoxStage::kHeader;
2023
18
#if JPEGXL_ENABLE_TRANSCODE_JPEG
2024
1.00k
    } 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.00k
    } else if (dec->box_stage == BoxStage::kSkip) {
2074
1.00k
      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.00k
      size_t remaining = dec->box_contents_end - dec->file_pos;
2092
1.00k
      if (dec->avail_in < remaining) {
2093
        // Indicate how many more bytes needed starting from next_in.
2094
112
        dec->basic_info_size_hint =
2095
112
            InitialBasicInfoSizeHint() + dec->box_contents_end - dec->file_pos;
2096
        // Don't have the full box yet, skip all we have so far
2097
112
        dec->AdvanceInput(dec->avail_in);
2098
112
        return JXL_DEC_NEED_MORE_INPUT;
2099
891
      } else {
2100
        // Full box available, skip all its remaining bytes
2101
891
        dec->AdvanceInput(remaining);
2102
891
        dec->box_stage = BoxStage::kHeader;
2103
891
      }
2104
1.00k
    } else {
2105
0
      JXL_DEBUG_ABORT("Unreachable");
2106
0
    }
2107
94.4k
  }
2108
2.87k
  return JXL_DEC_SUCCESS;
2109
74.5k
}
2110
2111
93.4k
JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
2112
93.4k
  if (dec->stage == DecoderStage::kInited) {
2113
18.9k
    dec->stage = DecoderStage::kStarted;
2114
18.9k
  }
2115
93.4k
  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
93.4k
  if (!dec->got_signature) {
2122
37.8k
    JxlSignature sig = JxlSignatureCheck(dec->next_in, dec->avail_in);
2123
37.8k
    if (sig == JXL_SIG_INVALID) return JXL_INPUT_ERROR("invalid signature");
2124
37.7k
    if (sig == JXL_SIG_NOT_ENOUGH_BYTES) {
2125
18.9k
      if (dec->input_closed) {
2126
7
        return JXL_INPUT_ERROR("file too small for signature");
2127
7
      }
2128
18.9k
      return JXL_DEC_NEED_MORE_INPUT;
2129
18.9k
    }
2130
2131
18.8k
    dec->got_signature = true;
2132
2133
18.8k
    if (sig == JXL_SIG_CONTAINER) {
2134
413
      dec->have_container = true;
2135
18.4k
    } else {
2136
18.4k
      dec->last_codestream_seen = true;
2137
18.4k
    }
2138
18.8k
  }
2139
2140
74.5k
  JxlDecoderStatus status = HandleBoxes(dec);
2141
2142
74.5k
  if (status == JXL_DEC_NEED_MORE_INPUT && dec->input_closed) {
2143
6.45k
    return JXL_INPUT_ERROR("premature end of input");
2144
6.45k
  }
2145
2146
  // Even if the box handling returns success, certain types of
2147
  // data may be missing.
2148
68.0k
  if (status == JXL_DEC_SUCCESS) {
2149
2.87k
    if (dec->CanUseMoreCodestreamInput()) {
2150
0
      return JXL_INPUT_ERROR("codestream never finished");
2151
0
    }
2152
2.87k
#if JPEGXL_ENABLE_TRANSCODE_JPEG
2153
2.87k
    if (dec->JbrdNeedMoreBoxes()) {
2154
0
      return JXL_INPUT_ERROR("missing metadata boxes for jpeg reconstruction");
2155
0
    }
2156
2.87k
#endif
2157
2.87k
  }
2158
2159
68.0k
  return status;
2160
68.0k
}
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.5k
                                        JxlBasicInfo* info) {
2168
17.5k
  if (!dec->got_basic_info) return JXL_DEC_NEED_MORE_INPUT;
2169
2170
17.5k
  if (info) {
2171
17.5k
    memset(info, 0, sizeof(*info));
2172
2173
17.5k
    const jxl::ImageMetadata& meta = dec->metadata.m;
2174
2175
17.5k
    info->have_container = TO_JXL_BOOL(dec->have_container);
2176
17.5k
    info->xsize = dec->metadata.size.xsize();
2177
17.5k
    info->ysize = dec->metadata.size.ysize();
2178
17.5k
    info->uses_original_profile = TO_JXL_BOOL(!meta.xyb_encoded);
2179
2180
17.5k
    info->bits_per_sample = meta.bit_depth.bits_per_sample;
2181
17.5k
    info->exponent_bits_per_sample = meta.bit_depth.exponent_bits_per_sample;
2182
2183
17.5k
    info->have_preview = TO_JXL_BOOL(meta.have_preview);
2184
17.5k
    info->have_animation = TO_JXL_BOOL(meta.have_animation);
2185
17.5k
    info->orientation = static_cast<JxlOrientation>(meta.orientation);
2186
2187
17.5k
    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.5k
    info->intensity_target = meta.IntensityTarget();
2195
17.5k
    if (dec->desired_intensity_target > 0) {
2196
0
      info->intensity_target = dec->desired_intensity_target;
2197
0
    }
2198
17.5k
    info->min_nits = meta.tone_mapping.min_nits;
2199
17.5k
    info->relative_to_max_display =
2200
17.5k
        TO_JXL_BOOL(meta.tone_mapping.relative_to_max_display);
2201
17.5k
    info->linear_below = meta.tone_mapping.linear_below;
2202
2203
17.5k
    const jxl::ExtraChannelInfo* alpha = meta.Find(jxl::ExtraChannel::kAlpha);
2204
17.5k
    if (alpha != nullptr) {
2205
2.28k
      info->alpha_bits = alpha->bit_depth.bits_per_sample;
2206
2.28k
      info->alpha_exponent_bits = alpha->bit_depth.exponent_bits_per_sample;
2207
2.28k
      info->alpha_premultiplied = TO_JXL_BOOL(alpha->alpha_associated);
2208
15.2k
    } else {
2209
15.2k
      info->alpha_bits = 0;
2210
15.2k
      info->alpha_exponent_bits = 0;
2211
15.2k
      info->alpha_premultiplied = 0;
2212
15.2k
    }
2213
2214
17.5k
    info->num_color_channels =
2215
17.5k
        meta.color_encoding.GetColorSpace() == jxl::ColorSpace::kGray ? 1 : 3;
2216
2217
17.5k
    info->num_extra_channels = meta.num_extra_channels;
2218
2219
17.5k
    if (info->have_preview) {
2220
187
      info->preview.xsize = dec->metadata.m.preview_size.xsize();
2221
187
      info->preview.ysize = dec->metadata.m.preview_size.ysize();
2222
187
    }
2223
2224
17.5k
    if (info->have_animation) {
2225
231
      info->animation.tps_numerator = dec->metadata.m.animation.tps_numerator;
2226
231
      info->animation.tps_denominator =
2227
231
          dec->metadata.m.animation.tps_denominator;
2228
231
      info->animation.num_loops = dec->metadata.m.animation.num_loops;
2229
231
      info->animation.have_timecodes =
2230
231
          TO_JXL_BOOL(dec->metadata.m.animation.have_timecodes);
2231
231
    }
2232
2233
17.5k
    if (meta.have_intrinsic_size) {
2234
232
      info->intrinsic_xsize = dec->metadata.m.intrinsic_size.xsize();
2235
232
      info->intrinsic_ysize = dec->metadata.m.intrinsic_size.ysize();
2236
17.2k
    } else {
2237
17.2k
      info->intrinsic_xsize = info->xsize;
2238
17.2k
      info->intrinsic_ysize = info->ysize;
2239
17.2k
    }
2240
17.5k
  }
2241
2242
17.5k
  return JXL_DEC_SUCCESS;
2243
17.5k
}
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
55.7k
    const jxl::ColorEncoding** encoding) {
2302
55.7k
  if (!dec->got_all_headers) return JXL_DEC_NEED_MORE_INPUT;
2303
55.7k
  *encoding = nullptr;
2304
55.7k
  if (target == JXL_COLOR_PROFILE_TARGET_DATA && dec->metadata.m.xyb_encoded) {
2305
10.7k
    *encoding = &dec->passes_state->output_encoding_info.color_encoding;
2306
45.0k
  } else {
2307
45.0k
    *encoding = &dec->metadata.m.color_encoding;
2308
45.0k
  }
2309
55.7k
  return JXL_DEC_SUCCESS;
2310
55.7k
}
2311
}  // namespace
2312
2313
JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile(
2314
    const JxlDecoder* dec, JxlColorProfileTarget target,
2315
13.9k
    JxlColorEncoding* color_encoding) {
2316
13.9k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2317
13.9k
  JxlDecoderStatus status =
2318
13.9k
      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2319
13.9k
  if (status) return status;
2320
2321
13.9k
  if (jxl_color_encoding->WantICC())
2322
20
    return JXL_DEC_ERROR;  // Indicate no encoded profile available.
2323
2324
13.9k
  if (color_encoding) {
2325
13.9k
    *color_encoding = jxl_color_encoding->ToExternal();
2326
13.9k
  }
2327
2328
13.9k
  return JXL_DEC_SUCCESS;
2329
13.9k
}
2330
2331
JxlDecoderStatus JxlDecoderGetICCProfileSize(const JxlDecoder* dec,
2332
                                             JxlColorProfileTarget target,
2333
27.8k
                                             size_t* size) {
2334
27.8k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2335
27.8k
  JxlDecoderStatus status =
2336
27.8k
      GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2337
27.8k
  if (status != JXL_DEC_SUCCESS) return status;
2338
2339
27.8k
  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
27.8k
  if (size) {
2353
27.8k
    *size = jxl_color_encoding->ICC().size();
2354
27.8k
  }
2355
2356
27.8k
  return JXL_DEC_SUCCESS;
2357
27.8k
}
2358
2359
JxlDecoderStatus JxlDecoderGetColorAsICCProfile(const JxlDecoder* dec,
2360
                                                JxlColorProfileTarget target,
2361
                                                uint8_t* icc_profile,
2362
13.9k
                                                size_t size) {
2363
13.9k
  size_t wanted_size;
2364
  // This also checks the NEED_MORE_INPUT and the unknown/xyb cases
2365
13.9k
  JxlDecoderStatus status =
2366
13.9k
      JxlDecoderGetICCProfileSize(dec, target, &wanted_size);
2367
13.9k
  if (status != JXL_DEC_SUCCESS) return status;
2368
13.9k
  if (size < wanted_size) return JXL_API_ERROR("ICC profile output too small");
2369
2370
13.9k
  const jxl::ColorEncoding* jxl_color_encoding = nullptr;
2371
13.9k
  status = GetColorEncodingForTarget(dec, target, &jxl_color_encoding);
2372
13.9k
  if (status != JXL_DEC_SUCCESS) return status;
2373
2374
13.9k
  memcpy(icc_profile, jxl_color_encoding->ICC().data(),
2375
13.9k
         jxl_color_encoding->ICC().size());
2376
2377
13.9k
  return JXL_DEC_SUCCESS;
2378
13.9k
}
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.1k
                                  const JxlPixelFormat* format, size_t* bits) {
2386
13.1k
  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.1k
  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.1k
  if (format->num_channels > 4) {
2395
0
    return JXL_API_ERROR("More than 4 channels not supported");
2396
0
  }
2397
2398
13.1k
  *bits = BitsPerChannel(format->data_type);
2399
2400
13.1k
  if (*bits == 0) {
2401
0
    return JXL_API_ERROR("Invalid/unsupported data type");
2402
0
  }
2403
2404
13.1k
  return JXL_DEC_SUCCESS;
2405
13.1k
}
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.1k
                                   bool preview) {
2447
13.1k
  size_t bits;
2448
13.1k
  JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits);
2449
13.1k
  if (status != JXL_DEC_SUCCESS) return status;
2450
13.1k
  size_t xsize;
2451
13.1k
  size_t ysize;
2452
13.1k
  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.1k
  } else {
2456
13.1k
    GetCurrentDimensions(dec, xsize, ysize);
2457
13.1k
  }
2458
13.1k
  if (num_channels == 0) num_channels = format->num_channels;
2459
13.1k
  size_t row_bits;
2460
13.1k
  if (!jxl::SafeMul<size_t>(xsize, num_channels, row_bits) ||
2461
13.1k
      !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.1k
  size_t row_size = jxl::DivCeil(row_bits, jxl::kBitsPerByte);
2465
13.1k
  const size_t last_row_size = row_size;
2466
13.1k
  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.1k
  size_t total = 0;
2475
13.1k
  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.1k
  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.1k
  return JXL_DEC_SUCCESS;
2482
13.1k
}
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.1k
    const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) {
2524
13.1k
  if (format->num_channels < 3 &&
2525
336
      !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.1k
  return GetMinSize(dec, format, 0, size, false);
2530
13.1k
}
2531
2532
JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec,
2533
                                             const JxlPixelFormat* format,
2534
6.72k
                                             void* buffer, size_t size) {
2535
6.72k
  if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE) ||
2536
6.72k
      dec->preview_frame) {
2537
0
    return JXL_API_ERROR("No image out buffer needed at this time");
2538
0
  }
2539
6.72k
  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.72k
  if (format->num_channels < 3 &&
2544
168
      !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.72k
  size_t min_size;
2548
  // This also checks whether the format is valid and supported and basic info
2549
  // is available.
2550
6.72k
  JxlDecoderStatus status =
2551
6.72k
      JxlDecoderImageOutBufferSize(dec, format, &min_size);
2552
6.72k
  if (status != JXL_DEC_SUCCESS) return status;
2553
2554
6.72k
  if (size < min_size) return JXL_DEC_ERROR;
2555
2556
6.72k
  dec->image_out_buffer_set = true;
2557
6.72k
  dec->image_out_buffer = buffer;
2558
6.72k
  dec->image_out_size = size;
2559
6.72k
  dec->image_out_format = *format;
2560
2561
6.72k
  return JXL_DEC_SUCCESS;
2562
6.72k
}
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
6.91k
                                          JxlFrameHeader* header) {
2663
6.91k
  if (!dec->frame_header || dec->frame_stage == FrameStage::kHeader) {
2664
0
    return JXL_API_ERROR("no frame header available");
2665
0
  }
2666
6.91k
  const auto& metadata = dec->metadata.m;
2667
6.91k
  memset(header, 0, sizeof(*header));
2668
6.91k
  if (metadata.have_animation) {
2669
572
    header->duration = dec->frame_header->animation_frame.duration;
2670
572
    if (metadata.animation.have_timecodes) {
2671
0
      header->timecode = dec->frame_header->animation_frame.timecode;
2672
0
    }
2673
572
  }
2674
6.91k
  header->name_length = dec->frame_header->name.size();
2675
6.91k
  header->is_last = TO_JXL_BOOL(dec->frame_header->is_last);
2676
6.91k
  size_t xsize;
2677
6.91k
  size_t ysize;
2678
6.91k
  GetCurrentDimensions(dec, xsize, ysize);
2679
6.91k
  header->layer_info.xsize = xsize;
2680
6.91k
  header->layer_info.ysize = ysize;
2681
6.91k
  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
6.91k
  } else {
2686
6.91k
    header->layer_info.crop_x0 = 0;
2687
6.91k
    header->layer_info.crop_y0 = 0;
2688
6.91k
    header->layer_info.have_crop = JXL_FALSE;
2689
6.91k
  }
2690
6.91k
  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
6.91k
  if (dec->coalescing) {
2706
6.91k
    header->layer_info.blend_info.blendmode = JXL_BLEND_REPLACE;
2707
6.91k
    header->layer_info.blend_info.source = 0;
2708
6.91k
    header->layer_info.blend_info.alpha = 0;
2709
6.91k
    header->layer_info.blend_info.clamp = JXL_FALSE;
2710
6.91k
    header->layer_info.save_as_reference = 0;
2711
6.91k
  } 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
6.91k
  return JXL_DEC_SUCCESS;
2723
6.91k
}
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
109
                                        size_t size) {
2843
109
  if (dec->box_out_buffer_set) {
2844
0
    return JXL_API_ERROR("must release box buffer before setting it again");
2845
0
  }
2846
109
  if (!dec->box_event) {
2847
0
    return JXL_API_ERROR("can only set box buffer after box event");
2848
0
  }
2849
2850
109
  dec->box_out_buffer_set = true;
2851
109
  dec->box_out_buffer_set_current_box = true;
2852
109
  dec->box_out_buffer = data;
2853
109
  dec->box_out_buffer_size = size;
2854
109
  dec->box_out_buffer_pos = 0;
2855
109
  return JXL_DEC_SUCCESS;
2856
109
}
2857
2858
20.2k
size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec) {
2859
20.2k
  if (!dec->box_out_buffer_set) {
2860
20.1k
    return 0;
2861
20.1k
  }
2862
109
  size_t result = dec->box_out_buffer_size - dec->box_out_buffer_pos;
2863
109
  dec->box_out_buffer_set = false;
2864
109
  dec->box_out_buffer = nullptr;
2865
109
  dec->box_out_buffer_size = 0;
2866
109
  if (!dec->box_out_buffer_set_current_box) {
2867
89
    dec->box_out_buffer_begin = 0;
2868
89
  } else {
2869
20
    dec->box_out_buffer_begin += dec->box_out_buffer_pos;
2870
20
  }
2871
109
  dec->box_out_buffer_set_current_box = false;
2872
109
  return result;
2873
20.2k
}
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.34k
                                      JXL_BOOL decompressed) {
2885
1.34k
  if (!dec->box_event) {
2886
0
    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
2887
0
  }
2888
1.34k
  if (decompressed) {
2889
0
    memcpy(type, dec->box_decoded_type, sizeof(dec->box_decoded_type));
2890
1.34k
  } else {
2891
1.34k
    memcpy(type, dec->box_type, sizeof(dec->box_type));
2892
1.34k
  }
2893
2894
1.34k
  return JXL_DEC_SUCCESS;
2895
1.34k
}
2896
2897
JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
2898
1.34k
                                         uint64_t* size) {
2899
1.34k
  if (!dec->box_event) {
2900
0
    return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event");
2901
0
  }
2902
1.34k
  if (size) {
2903
1.34k
    *size = dec->box_size;
2904
1.34k
  }
2905
1.34k
  return JXL_DEC_SUCCESS;
2906
1.34k
}
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
}