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