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