jxl.cc (23600B)
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 "lib/extras/dec/jxl.h" 7 8 #include <jxl/cms.h> 9 #include <jxl/codestream_header.h> 10 #include <jxl/decode.h> 11 #include <jxl/decode_cxx.h> 12 #include <jxl/types.h> 13 14 #include <cinttypes> // PRIu32 15 #include <cstddef> 16 #include <cstdint> 17 #include <cstdio> 18 #include <limits> 19 #include <vector> 20 21 #include "lib/extras/common.h" 22 #include "lib/extras/dec/color_description.h" 23 #include "lib/jxl/base/common.h" 24 #include "lib/jxl/base/exif.h" 25 #include "lib/jxl/base/printf_macros.h" 26 #include "lib/jxl/base/status.h" 27 28 namespace jxl { 29 namespace extras { 30 namespace { 31 32 #define QUIT(M) \ 33 fprintf(stderr, "%s\n", M); \ 34 return false; 35 36 struct BoxProcessor { 37 explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } 38 39 bool InitializeOutput(std::vector<uint8_t>* out) { 40 if (out == nullptr) { 41 fprintf(stderr, "internal: out == nullptr\n"); 42 return false; 43 } 44 box_data_ = out; 45 return AddMoreOutput(); 46 } 47 48 bool AddMoreOutput() { 49 if (box_data_ == nullptr) { 50 fprintf(stderr, "internal: box_data_ == nullptr\n"); 51 return false; 52 } 53 Flush(); 54 static const size_t kBoxOutputChunkSize = 1 << 16; 55 box_data_->resize(box_data_->size() + kBoxOutputChunkSize); 56 next_out_ = box_data_->data() + total_size_; 57 avail_out_ = box_data_->size() - total_size_; 58 if (JXL_DEC_SUCCESS != 59 JxlDecoderSetBoxBuffer(dec_, next_out_, avail_out_)) { 60 fprintf(stderr, "JxlDecoderSetBoxBuffer failed\n"); 61 return false; 62 } 63 return true; 64 } 65 66 void FinalizeOutput() { 67 if (box_data_ == nullptr) return; 68 Flush(); 69 box_data_->resize(total_size_); 70 Reset(); 71 } 72 73 private: 74 JxlDecoder* dec_; 75 std::vector<uint8_t>* box_data_; 76 uint8_t* next_out_; 77 size_t avail_out_; 78 size_t total_size_; 79 80 void Reset() { 81 box_data_ = nullptr; 82 next_out_ = nullptr; 83 avail_out_ = 0; 84 total_size_ = 0; 85 } 86 void Flush() { 87 if (box_data_ == nullptr) return; 88 size_t remaining = JxlDecoderReleaseBoxBuffer(dec_); 89 size_t bytes_written = avail_out_ - remaining; 90 next_out_ += bytes_written; 91 avail_out_ -= bytes_written; 92 total_size_ += bytes_written; 93 } 94 }; 95 96 void SetBitDepthFromDataType(JxlDataType data_type, uint32_t* bits_per_sample, 97 uint32_t* exponent_bits_per_sample) { 98 switch (data_type) { 99 case JXL_TYPE_UINT8: 100 *bits_per_sample = 8; 101 *exponent_bits_per_sample = 0; 102 break; 103 case JXL_TYPE_UINT16: 104 *bits_per_sample = 16; 105 *exponent_bits_per_sample = 0; 106 break; 107 case JXL_TYPE_FLOAT16: 108 *bits_per_sample = 16; 109 *exponent_bits_per_sample = 5; 110 break; 111 case JXL_TYPE_FLOAT: 112 *bits_per_sample = 32; 113 *exponent_bits_per_sample = 8; 114 break; 115 } 116 } 117 118 template <typename T> 119 void UpdateBitDepth(JxlBitDepth bit_depth, JxlDataType data_type, T* info) { 120 if (bit_depth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT) { 121 SetBitDepthFromDataType(data_type, &info->bits_per_sample, 122 &info->exponent_bits_per_sample); 123 } else if (bit_depth.type == JXL_BIT_DEPTH_CUSTOM) { 124 info->bits_per_sample = bit_depth.bits_per_sample; 125 info->exponent_bits_per_sample = bit_depth.exponent_bits_per_sample; 126 } 127 } 128 129 } // namespace 130 131 bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, 132 const JXLDecompressParams& dparams, size_t* decoded_bytes, 133 PackedPixelFile* ppf, std::vector<uint8_t>* jpeg_bytes) { 134 JxlSignature sig = JxlSignatureCheck(bytes, bytes_size); 135 // silently return false if this is not a JXL file 136 if (sig == JXL_SIG_INVALID) return false; 137 138 auto decoder = JxlDecoderMake(dparams.memory_manager); 139 JxlDecoder* dec = decoder.get(); 140 ppf->frames.clear(); 141 142 if (dparams.runner_opaque != nullptr && 143 JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec, dparams.runner, 144 dparams.runner_opaque)) { 145 fprintf(stderr, "JxlEncoderSetParallelRunner failed\n"); 146 return false; 147 } 148 149 JxlPixelFormat format = {}; // Initialize to calm down clang-tidy. 150 std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats; 151 152 JxlColorEncoding color_encoding; 153 size_t num_color_channels = 0; 154 if (!dparams.color_space.empty()) { 155 if (!jxl::ParseDescription(dparams.color_space, &color_encoding)) { 156 fprintf(stderr, "Failed to parse color space %s.\n", 157 dparams.color_space.c_str()); 158 return false; 159 } 160 num_color_channels = 161 color_encoding.color_space == JXL_COLOR_SPACE_GRAY ? 1 : 3; 162 } 163 164 bool can_reconstruct_jpeg = false; 165 std::vector<uint8_t> jpeg_data_chunk; 166 if (jpeg_bytes != nullptr) { 167 // This bound is very likely to be enough to hold the entire 168 // reconstructed JPEG, to avoid having to do expensive retries. 169 jpeg_data_chunk.resize(bytes_size * 3 / 2 + 1024); 170 jpeg_bytes->resize(0); 171 } 172 173 int events = (JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE); 174 175 bool max_passes_defined = 176 (dparams.max_passes < std::numeric_limits<uint32_t>::max()); 177 if (max_passes_defined || dparams.max_downsampling > 1) { 178 events |= JXL_DEC_FRAME_PROGRESSION; 179 if (max_passes_defined) { 180 JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kPasses); 181 } else { 182 JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kLastPasses); 183 } 184 } 185 if (jpeg_bytes != nullptr) { 186 events |= JXL_DEC_JPEG_RECONSTRUCTION; 187 } else { 188 events |= (JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_PREVIEW_IMAGE | 189 JXL_DEC_BOX); 190 if (accepted_formats.empty()) { 191 // decoding just the metadata, not the pixel data 192 events ^= (JXL_DEC_FULL_IMAGE | JXL_DEC_PREVIEW_IMAGE); 193 } 194 } 195 if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec, events)) { 196 fprintf(stderr, "JxlDecoderSubscribeEvents failed\n"); 197 return false; 198 } 199 if (jpeg_bytes == nullptr) { 200 if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors( 201 dec, TO_JXL_BOOL(dparams.render_spotcolors))) { 202 fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n"); 203 return false; 204 } 205 if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation( 206 dec, TO_JXL_BOOL(dparams.keep_orientation))) { 207 fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n"); 208 return false; 209 } 210 if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha( 211 dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) { 212 fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n"); 213 return false; 214 } 215 if (dparams.display_nits > 0 && 216 JXL_DEC_SUCCESS != 217 JxlDecoderSetDesiredIntensityTarget(dec, dparams.display_nits)) { 218 fprintf(stderr, "Decoder failed to set desired intensity target\n"); 219 return false; 220 } 221 if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec, JXL_TRUE)) { 222 fprintf(stderr, "JxlDecoderSetDecompressBoxes failed\n"); 223 return false; 224 } 225 } 226 if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec, bytes, bytes_size)) { 227 fprintf(stderr, "Decoder failed to set input\n"); 228 return false; 229 } 230 uint32_t progression_index = 0; 231 bool codestream_done = jpeg_bytes == nullptr && accepted_formats.empty(); 232 BoxProcessor boxes(dec); 233 for (;;) { 234 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 235 if (status == JXL_DEC_ERROR) { 236 fprintf(stderr, "Failed to decode image\n"); 237 return false; 238 } else if (status == JXL_DEC_NEED_MORE_INPUT) { 239 if (codestream_done) { 240 break; 241 } 242 if (dparams.allow_partial_input) { 243 if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) { 244 fprintf(stderr, 245 "Input file is truncated and there is no preview " 246 "available yet.\n"); 247 return false; 248 } 249 break; 250 } 251 size_t released_size = JxlDecoderReleaseInput(dec); 252 fprintf(stderr, 253 "Input file is truncated (total bytes: %" PRIuS 254 ", processed bytes: %" PRIuS 255 ") and --allow_partial_files is not present.\n", 256 bytes_size, bytes_size - released_size); 257 return false; 258 } else if (status == JXL_DEC_BOX) { 259 boxes.FinalizeOutput(); 260 JxlBoxType box_type; 261 if (JXL_DEC_SUCCESS != JxlDecoderGetBoxType(dec, box_type, JXL_TRUE)) { 262 fprintf(stderr, "JxlDecoderGetBoxType failed\n"); 263 return false; 264 } 265 std::vector<uint8_t>* box_data = nullptr; 266 if (memcmp(box_type, "Exif", 4) == 0) { 267 box_data = &ppf->metadata.exif; 268 } else if (memcmp(box_type, "iptc", 4) == 0) { 269 box_data = &ppf->metadata.iptc; 270 } else if (memcmp(box_type, "jumb", 4) == 0) { 271 box_data = &ppf->metadata.jumbf; 272 } else if (memcmp(box_type, "jhgm", 4) == 0) { 273 box_data = &ppf->metadata.jhgm; 274 } else if (memcmp(box_type, "xml ", 4) == 0) { 275 box_data = &ppf->metadata.xmp; 276 } 277 if (box_data) { 278 if (!boxes.InitializeOutput(box_data)) { 279 return false; 280 } 281 } 282 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 283 if (!boxes.AddMoreOutput()) { 284 return false; 285 } 286 } else if (status == JXL_DEC_JPEG_RECONSTRUCTION) { 287 can_reconstruct_jpeg = true; 288 // Decoding to JPEG. 289 if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec, 290 jpeg_data_chunk.data(), 291 jpeg_data_chunk.size())) { 292 fprintf(stderr, "Decoder failed to set JPEG Buffer\n"); 293 return false; 294 } 295 } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { 296 if (jpeg_bytes == nullptr) { 297 fprintf(stderr, "internal: jpeg_bytes == nullptr\n"); 298 return false; 299 } 300 // Decoded a chunk to JPEG. 301 size_t used_jpeg_output = 302 jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); 303 jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(), 304 jpeg_data_chunk.data() + used_jpeg_output); 305 if (used_jpeg_output == 0) { 306 // Chunk is too small. 307 jpeg_data_chunk.resize(jpeg_data_chunk.size() * 2); 308 } 309 if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec, 310 jpeg_data_chunk.data(), 311 jpeg_data_chunk.size())) { 312 fprintf(stderr, "Decoder failed to set JPEG Buffer\n"); 313 return false; 314 } 315 } else if (status == JXL_DEC_BASIC_INFO) { 316 if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &ppf->info)) { 317 fprintf(stderr, "JxlDecoderGetBasicInfo failed\n"); 318 return false; 319 } 320 if (accepted_formats.empty()) continue; 321 if (num_color_channels != 0) { 322 // Mark the change in number of color channels due to the requested 323 // color space. 324 ppf->info.num_color_channels = num_color_channels; 325 } 326 if (dparams.output_bitdepth.type == JXL_BIT_DEPTH_CUSTOM) { 327 // Select format based on custom bits per sample. 328 ppf->info.bits_per_sample = dparams.output_bitdepth.bits_per_sample; 329 } 330 // Select format according to accepted formats. 331 if (!jxl::extras::SelectFormat(accepted_formats, ppf->info, &format)) { 332 fprintf(stderr, "SelectFormat failed\n"); 333 return false; 334 } 335 bool have_alpha = (format.num_channels == 2 || format.num_channels == 4); 336 if (!have_alpha) { 337 // Mark in the basic info that alpha channel was dropped. 338 ppf->info.alpha_bits = 0; 339 } else { 340 if (dparams.unpremultiply_alpha) { 341 // Mark in the basic info that alpha was unpremultiplied. 342 ppf->info.alpha_premultiplied = JXL_FALSE; 343 } 344 } 345 bool alpha_found = false; 346 for (uint32_t i = 0; i < ppf->info.num_extra_channels; ++i) { 347 JxlExtraChannelInfo eci; 348 if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &eci)) { 349 fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n"); 350 return false; 351 } 352 if (eci.type == JXL_CHANNEL_ALPHA && have_alpha && !alpha_found) { 353 // Skip the first alpha channels because it is already present in the 354 // interleaved image. 355 alpha_found = true; 356 continue; 357 } 358 std::string name(eci.name_length + 1, 0); 359 if (JXL_DEC_SUCCESS != 360 JxlDecoderGetExtraChannelName( 361 dec, i, const_cast<char*>(name.data()), name.size())) { 362 fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n"); 363 return false; 364 } 365 name.resize(eci.name_length); 366 ppf->extra_channels_info.push_back({eci, i, name}); 367 } 368 } else if (status == JXL_DEC_COLOR_ENCODING) { 369 if (!dparams.color_space.empty()) { 370 if (ppf->info.uses_original_profile) { 371 fprintf(stderr, 372 "Warning: --color_space ignored because the image is " 373 "not XYB encoded.\n"); 374 } else { 375 JxlDecoderSetCms(dec, *JxlGetDefaultCms()); 376 if (JXL_DEC_SUCCESS != 377 JxlDecoderSetPreferredColorProfile(dec, &color_encoding)) { 378 fprintf(stderr, "Failed to set color space.\n"); 379 return false; 380 } 381 } 382 } 383 size_t icc_size = 0; 384 JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA; 385 if (JXL_DEC_SUCCESS != 386 JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { 387 fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); 388 } 389 if (icc_size != 0) { 390 ppf->icc.resize(icc_size); 391 if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( 392 dec, target, ppf->icc.data(), icc_size)) { 393 fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); 394 return false; 395 } 396 } 397 if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile( 398 dec, target, &ppf->color_encoding)) { 399 ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; 400 } 401 ppf->primary_color_representation = 402 PackedPixelFile::kColorEncodingIsPrimary; 403 icc_size = 0; 404 target = JXL_COLOR_PROFILE_TARGET_ORIGINAL; 405 if (JXL_DEC_SUCCESS != 406 JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { 407 fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); 408 } 409 if (icc_size != 0) { 410 ppf->orig_icc.resize(icc_size); 411 if (JXL_DEC_SUCCESS != 412 JxlDecoderGetColorAsICCProfile(dec, target, ppf->orig_icc.data(), 413 icc_size)) { 414 fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); 415 return false; 416 } 417 ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; 418 } 419 } else if (status == JXL_DEC_FRAME) { 420 auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize, 421 ppf->info.ysize, format); 422 JXL_ASSIGN_OR_QUIT(jxl::extras::PackedFrame frame, 423 jxl::extras::PackedFrame::Create( 424 ppf->info.xsize, ppf->info.ysize, format), 425 "Failed to create image frame."); 426 if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) { 427 fprintf(stderr, "JxlDecoderGetFrameHeader failed\n"); 428 return false; 429 } 430 frame.name.resize(frame.frame_info.name_length + 1, 0); 431 if (JXL_DEC_SUCCESS != 432 JxlDecoderGetFrameName(dec, const_cast<char*>(frame.name.data()), 433 frame.name.size())) { 434 fprintf(stderr, "JxlDecoderGetFrameName failed\n"); 435 return false; 436 } 437 frame.name.resize(frame.frame_info.name_length); 438 ppf->frames.emplace_back(std::move(frame)); 439 progression_index = 0; 440 } else if (status == JXL_DEC_FRAME_PROGRESSION) { 441 size_t downsampling = JxlDecoderGetIntendedDownsamplingRatio(dec); 442 if ((max_passes_defined && progression_index >= dparams.max_passes) || 443 (!max_passes_defined && downsampling <= dparams.max_downsampling)) { 444 if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) { 445 fprintf(stderr, "JxlDecoderFlushImage failed\n"); 446 return false; 447 } 448 if (ppf->frames.back().frame_info.is_last) { 449 break; 450 } 451 if (JXL_DEC_SUCCESS != JxlDecoderSkipCurrentFrame(dec)) { 452 fprintf(stderr, "JxlDecoderSkipCurrentFrame failed\n"); 453 return false; 454 } 455 } 456 ++progression_index; 457 } else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) { 458 size_t buffer_size; 459 if (JXL_DEC_SUCCESS != 460 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)) { 461 fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n"); 462 return false; 463 } 464 JXL_ASSIGN_OR_QUIT( 465 jxl::extras::PackedImage preview_image, 466 jxl::extras::PackedImage::Create(ppf->info.preview.xsize, 467 ppf->info.preview.ysize, format), 468 "Failed to create preview image."); 469 ppf->preview_frame = 470 jxl::make_unique<jxl::extras::PackedFrame>(std::move(preview_image)); 471 if (buffer_size != ppf->preview_frame->color.pixels_size) { 472 fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", 473 buffer_size, ppf->preview_frame->color.pixels_size); 474 return false; 475 } 476 if (JXL_DEC_SUCCESS != 477 JxlDecoderSetPreviewOutBuffer( 478 dec, &format, ppf->preview_frame->color.pixels(), buffer_size)) { 479 fprintf(stderr, "JxlDecoderSetPreviewOutBuffer failed\n"); 480 return false; 481 } 482 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 483 if (jpeg_bytes != nullptr) { 484 break; 485 } 486 size_t buffer_size; 487 if (JXL_DEC_SUCCESS != 488 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)) { 489 fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n"); 490 return false; 491 } 492 jxl::extras::PackedFrame& frame = ppf->frames.back(); 493 if (buffer_size != frame.color.pixels_size) { 494 fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", 495 buffer_size, frame.color.pixels_size); 496 return false; 497 } 498 499 if (dparams.use_image_callback) { 500 auto callback = [](void* opaque, size_t x, size_t y, size_t num_pixels, 501 const void* pixels) { 502 auto* ppf = reinterpret_cast<jxl::extras::PackedPixelFile*>(opaque); 503 jxl::extras::PackedImage& color = ppf->frames.back().color; 504 uint8_t* pixels_buffer = reinterpret_cast<uint8_t*>(color.pixels()); 505 size_t sample_size = color.pixel_stride(); 506 memcpy(pixels_buffer + (color.stride * y + sample_size * x), pixels, 507 num_pixels * sample_size); 508 }; 509 if (JXL_DEC_SUCCESS != 510 JxlDecoderSetImageOutCallback(dec, &format, callback, ppf)) { 511 fprintf(stderr, "JxlDecoderSetImageOutCallback failed\n"); 512 return false; 513 } 514 } else { 515 if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec, &format, 516 frame.color.pixels(), 517 buffer_size)) { 518 fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n"); 519 return false; 520 } 521 } 522 if (JXL_DEC_SUCCESS != 523 JxlDecoderSetImageOutBitDepth(dec, &dparams.output_bitdepth)) { 524 fprintf(stderr, "JxlDecoderSetImageOutBitDepth failed\n"); 525 return false; 526 } 527 UpdateBitDepth(dparams.output_bitdepth, format.data_type, &ppf->info); 528 bool have_alpha = (format.num_channels == 2 || format.num_channels == 4); 529 if (have_alpha) { 530 // Interleaved alpha channels has the same bit depth as color channels. 531 ppf->info.alpha_bits = ppf->info.bits_per_sample; 532 ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample; 533 } 534 JxlPixelFormat ec_format = format; 535 ec_format.num_channels = 1; 536 for (auto& eci : ppf->extra_channels_info) { 537 JXL_ASSIGN_OR_QUIT(jxl::extras::PackedImage image, 538 jxl::extras::PackedImage::Create( 539 ppf->info.xsize, ppf->info.ysize, ec_format), 540 "Failed to create extra channel image."); 541 frame.extra_channels.emplace_back(std::move(image)); 542 auto& ec = frame.extra_channels.back(); 543 size_t buffer_size; 544 if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize( 545 dec, &ec_format, &buffer_size, eci.index)) { 546 fprintf(stderr, "JxlDecoderExtraChannelBufferSize failed\n"); 547 return false; 548 } 549 if (buffer_size != ec.pixels_size) { 550 fprintf(stderr, 551 "Invalid extra channel buffer size" 552 " %" PRIuS " %" PRIuS "\n", 553 buffer_size, ec.pixels_size); 554 return false; 555 } 556 if (JXL_DEC_SUCCESS != 557 JxlDecoderSetExtraChannelBuffer(dec, &ec_format, ec.pixels(), 558 buffer_size, eci.index)) { 559 fprintf(stderr, "JxlDecoderSetExtraChannelBuffer failed\n"); 560 return false; 561 } 562 UpdateBitDepth(dparams.output_bitdepth, ec_format.data_type, 563 &eci.ec_info); 564 } 565 } else if (status == JXL_DEC_SUCCESS) { 566 // Decoding finished successfully. 567 break; 568 } else if (status == JXL_DEC_PREVIEW_IMAGE) { 569 // Nothing to do. 570 } else if (status == JXL_DEC_FULL_IMAGE) { 571 if (jpeg_bytes != nullptr || ppf->frames.back().frame_info.is_last) { 572 codestream_done = true; 573 } 574 } else { 575 fprintf(stderr, "Error: unexpected status: %d\n", 576 static_cast<int>(status)); 577 return false; 578 } 579 } 580 boxes.FinalizeOutput(); 581 if (!ppf->metadata.exif.empty()) { 582 // Verify that Exif box has a valid TIFF header at the specified offset. 583 // Discard bytes preceding the header. 584 if (ppf->metadata.exif.size() >= 4) { 585 uint32_t offset = LoadBE32(ppf->metadata.exif.data()); 586 if (offset <= ppf->metadata.exif.size() - 8) { 587 std::vector<uint8_t> exif(ppf->metadata.exif.begin() + 4 + offset, 588 ppf->metadata.exif.end()); 589 bool bigendian; 590 if (IsExif(exif, &bigendian)) { 591 ppf->metadata.exif = std::move(exif); 592 } else { 593 fprintf(stderr, "Warning: invalid TIFF header in Exif\n"); 594 } 595 } else { 596 fprintf(stderr, "Warning: invalid Exif offset: %" PRIu32 "\n", offset); 597 } 598 } else { 599 fprintf(stderr, "Warning: invalid Exif length: %" PRIuS "\n", 600 ppf->metadata.exif.size()); 601 } 602 } 603 if (jpeg_bytes != nullptr) { 604 if (!can_reconstruct_jpeg) return false; 605 size_t used_jpeg_output = 606 jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); 607 jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(), 608 jpeg_data_chunk.data() + used_jpeg_output); 609 } 610 if (decoded_bytes) { 611 *decoded_bytes = bytes_size - JxlDecoderReleaseInput(dec); 612 } 613 return true; 614 } 615 616 } // namespace extras 617 } // namespace jxl