Coverage Report

Created: 2026-05-24 07:45

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