Coverage Report

Created: 2026-10-28 07:26

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