decode_test.cc (232012B)
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/decode.h" 7 8 #include <jxl/cms.h> 9 #include <jxl/codestream_header.h> 10 #include <jxl/color_encoding.h> 11 #include <jxl/decode.h> 12 #include <jxl/decode_cxx.h> 13 #include <jxl/memory_manager.h> 14 #include <jxl/parallel_runner.h> 15 #include <jxl/resizable_parallel_runner.h> 16 #include <jxl/resizable_parallel_runner_cxx.h> 17 #include <jxl/thread_parallel_runner.h> 18 #include <jxl/thread_parallel_runner_cxx.h> 19 #include <jxl/types.h> 20 21 #include <algorithm> 22 #include <cstdint> 23 #include <cstdio> 24 #include <cstdlib> 25 #include <cstring> 26 #include <ostream> 27 #include <set> 28 #include <sstream> 29 #include <string> 30 #include <tuple> 31 #include <utility> 32 #include <vector> 33 34 #include "lib/extras/dec/color_description.h" 35 #include "lib/extras/enc/encode.h" 36 #include "lib/extras/enc/jpg.h" 37 #include "lib/extras/packed_image.h" 38 #include "lib/jxl/base/byte_order.h" 39 #include "lib/jxl/base/common.h" 40 #include "lib/jxl/base/compiler_specific.h" 41 #include "lib/jxl/base/override.h" 42 #include "lib/jxl/base/span.h" 43 #include "lib/jxl/butteraugli/butteraugli.h" 44 #include "lib/jxl/cms/color_encoding_cms.h" 45 #include "lib/jxl/color_encoding_internal.h" 46 #include "lib/jxl/common.h" // SpeedTier 47 #include "lib/jxl/dec_bit_reader.h" 48 #include "lib/jxl/dec_external_image.h" 49 #include "lib/jxl/enc_aux_out.h" 50 #include "lib/jxl/enc_external_image.h" 51 #include "lib/jxl/enc_fields.h" 52 #include "lib/jxl/enc_frame.h" 53 #include "lib/jxl/enc_icc_codec.h" 54 #include "lib/jxl/enc_params.h" 55 #include "lib/jxl/enc_progressive_split.h" 56 #include "lib/jxl/encode_internal.h" 57 #include "lib/jxl/fields.h" 58 #include "lib/jxl/frame_dimensions.h" 59 #include "lib/jxl/frame_header.h" 60 #include "lib/jxl/headers.h" 61 #include "lib/jxl/image.h" 62 #include "lib/jxl/image_bundle.h" 63 #include "lib/jxl/image_metadata.h" 64 #include "lib/jxl/image_ops.h" 65 #include "lib/jxl/jpeg/enc_jpeg_data.h" 66 #include "lib/jxl/jpeg/jpeg_data.h" 67 #include "lib/jxl/padded_bytes.h" 68 #include "lib/jxl/test_image.h" 69 #include "lib/jxl/test_memory_manager.h" 70 #include "lib/jxl/test_utils.h" 71 #include "lib/jxl/testing.h" 72 #include "lib/jxl/toc.h" 73 using ::jxl::test::GetIccTestProfile; 74 //////////////////////////////////////////////////////////////////////////////// 75 76 namespace { 77 void AppendU32BE(uint32_t u32, std::vector<uint8_t>* bytes) { 78 bytes->push_back(u32 >> 24); 79 bytes->push_back(u32 >> 16); 80 bytes->push_back(u32 >> 8); 81 bytes->push_back(u32 >> 0); 82 } 83 84 // What type of codestream format in the boxes to use for testing 85 enum CodeStreamBoxFormat { 86 // Do not use box format at all, only pure codestream 87 kCSBF_None, 88 // Have a single codestream box, with its actual size given in the box 89 kCSBF_Single, 90 // Have a single codestream box, with box size 0 (final box running to end) 91 kCSBF_Single_Zero_Terminated, 92 // Single codestream box, with another unknown box behind it 93 kCSBF_Single_Other, 94 // Have multiple partial codestream boxes 95 kCSBF_Multi, 96 // Have multiple partial codestream boxes, with final box size 0 (running 97 // to end) 98 kCSBF_Multi_Zero_Terminated, 99 // Have multiple partial codestream boxes, terminated by non-codestream box 100 kCSBF_Multi_Other_Terminated, 101 // Have multiple partial codestream boxes, terminated by non-codestream box 102 // that has its size set to 0 (running to end) 103 kCSBF_Multi_Other_Zero_Terminated, 104 // Have multiple partial codestream boxes, and the first one has a content 105 // of zero length 106 kCSBF_Multi_First_Empty, 107 // Have multiple partial codestream boxes, and the last one has a content 108 // of zero length and there is an unknown empty box at the end 109 kCSBF_Multi_Last_Empty_Other, 110 // Have a compressed exif box before a regular codestream box 111 kCSBF_Brob_Exif, 112 // Not a value but used for counting amount of enum entries 113 kCSBF_NUM_ENTRIES, 114 }; 115 116 // Unknown boxes for testing 117 const char* unk1_box_type = "unk1"; 118 const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz"; 119 const size_t unk1_box_size = strlen(unk1_box_contents); 120 const char* unk2_box_type = "unk2"; 121 const char* unk2_box_contents = "0123456789"; 122 const size_t unk2_box_size = strlen(unk2_box_contents); 123 const char* unk3_box_type = "unk3"; 124 const char* unk3_box_contents = "ABCDEF123456"; 125 const size_t unk3_box_size = strlen(unk3_box_contents); 126 // Box with brob-compressed exif, including header 127 const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>( 128 "\0\0\0@brobExif\241\350\2\300\177\244v\2525\304\360\27=?\267{" 129 "\33\37\314\332\214QX17PT\"\256\0\0\202s\214\313t\333\310\320k\20\276\30" 130 "\204\277l$\326c#\1\b"); 131 size_t box_brob_exif_size = 64; 132 // The uncompressed Exif data from the brob box 133 const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>( 134 "\0\0\0\0MM\0*" 135 "\0\0\0\b\0\5\1\22\0\3\0\0\0\1\0\5\0\0\1\32\0\5\0\0\0\1\0\0\0J\1\33\0\5\0\0" 136 "\0\1\0\0\0R\1(" 137 "\0\3\0\0\0\1\0\1\0\0\2\23\0\3\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0" 138 "\0\1\0\0\0\1"); 139 size_t exif_uncompressed_size = 94; 140 141 } // namespace 142 143 namespace jxl { 144 namespace { 145 146 void AppendTestBox(const char* type, const char* contents, size_t contents_size, 147 bool unbounded, std::vector<uint8_t>* bytes) { 148 AppendU32BE(contents_size + 8, bytes); 149 bytes->push_back(type[0]); 150 bytes->push_back(type[1]); 151 bytes->push_back(type[2]); 152 bytes->push_back(type[3]); 153 const uint8_t* contents_u = reinterpret_cast<const uint8_t*>(contents); 154 Bytes(contents_u, contents_size).AppendTo(*bytes); 155 } 156 157 enum PreviewMode { 158 kNoPreview, 159 kSmallPreview, 160 kBigPreview, 161 kNumPreviewModes, 162 }; 163 164 void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) { 165 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 166 if (preview_mode == kSmallPreview) { 167 ASSERT_TRUE(ib->ShrinkTo(ib->xsize() / 7, ib->ysize() / 7)); 168 } else if (preview_mode == kBigPreview) { 169 auto upsample7 = [&](const ImageF& in, ImageF* out) { 170 for (size_t y = 0; y < out->ysize(); ++y) { 171 for (size_t x = 0; x < out->xsize(); ++x) { 172 out->Row(y)[x] = in.ConstRow(y / 7)[x / 7]; 173 } 174 } 175 }; 176 JXL_TEST_ASSIGN_OR_DIE( 177 Image3F preview, 178 Image3F::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7)); 179 for (size_t c = 0; c < 3; ++c) { 180 upsample7(ib->color()->Plane(c), &preview.Plane(c)); 181 } 182 std::vector<ImageF> extra_channels; 183 for (size_t i = 0; i < ib->extra_channels().size(); ++i) { 184 JXL_TEST_ASSIGN_OR_DIE( 185 ImageF ec, 186 ImageF::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7)); 187 upsample7(ib->extra_channels()[i], &ec); 188 extra_channels.emplace_back(std::move(ec)); 189 } 190 ib->RemoveColor(); 191 ib->ClearExtraChannels(); 192 ASSERT_TRUE(ib->SetFromImage(std::move(preview), ib->c_current())); 193 ASSERT_TRUE(ib->SetExtraChannels(std::move(extra_channels))); 194 } 195 } 196 197 struct TestCodestreamParams { 198 CompressParams cparams; 199 CodeStreamBoxFormat box_format = kCSBF_None; 200 JxlOrientation orientation = JXL_ORIENT_IDENTITY; 201 PreviewMode preview_mode = kNoPreview; 202 bool add_intrinsic_size = false; 203 bool add_icc_profile = false; 204 float intensity_target = 0.0; 205 std::string color_space; 206 std::vector<uint8_t>* jpeg_codestream = nullptr; 207 }; 208 209 // Input pixels always given as 16-bit RGBA, 8 bytes per pixel. 210 // include_alpha determines if the encoded image should contain the alpha 211 // channel. 212 // add_icc_profile: if false, encodes the image as sRGB using the JXL fields, 213 // for grayscale or RGB images. If true, encodes the image using the ICC profile 214 // returned by GetIccTestProfile, without the JXL fields, this requires the 215 // image is RGB, not grayscale. 216 // Providing jpeg_codestream will populate the jpeg_codestream with compressed 217 // JPEG bytes, and make it possible to reconstruct those exact JPEG bytes using 218 // the return value _if_ add_container indicates a box format. 219 std::vector<uint8_t> CreateTestJXLCodestream( 220 Span<const uint8_t> pixels, size_t xsize, size_t ysize, size_t num_channels, 221 const TestCodestreamParams& params) { 222 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 223 // Compress the pixels with JPEG XL. 224 bool grayscale = (num_channels <= 2); 225 bool have_alpha = ((num_channels & 1) == 0); 226 bool include_alpha = have_alpha && params.jpeg_codestream == nullptr; 227 size_t bitdepth = params.jpeg_codestream == nullptr ? 16 : 8; 228 CodecInOut io{jxl::test::MemoryManager()}; 229 EXPECT_TRUE(io.SetSize(xsize, ysize)); 230 ColorEncoding color_encoding; 231 if (params.add_icc_profile) { 232 // the hardcoded ICC profile we attach requires RGB. 233 EXPECT_EQ(false, grayscale); 234 EXPECT_TRUE(params.color_space.empty()); 235 EXPECT_TRUE(color_encoding.SetICC(GetIccTestProfile(), JxlGetDefaultCms())); 236 } else if (!params.color_space.empty()) { 237 JxlColorEncoding c; 238 EXPECT_TRUE(jxl::ParseDescription(params.color_space, &c)); 239 EXPECT_TRUE(color_encoding.FromExternal(c)); 240 EXPECT_EQ(color_encoding.IsGray(), grayscale); 241 } else { 242 color_encoding = jxl::ColorEncoding::SRGB(/*is_gray=*/grayscale); 243 } 244 io.metadata.m.SetUintSamples(bitdepth); 245 if (include_alpha) { 246 io.metadata.m.SetAlphaBits(bitdepth); 247 } 248 if (params.intensity_target != 0) { 249 io.metadata.m.SetIntensityTarget(params.intensity_target); 250 } 251 JxlPixelFormat format = {static_cast<uint32_t>(num_channels), JXL_TYPE_UINT16, 252 JXL_BIG_ENDIAN, 0}; 253 // Make the grayscale-ness of the io metadata color_encoding and the packed 254 // image match. 255 io.metadata.m.color_encoding = color_encoding; 256 EXPECT_TRUE(ConvertFromExternal(pixels, xsize, ysize, color_encoding, 257 /*bits_per_sample=*/16, format, 258 /* pool */ nullptr, &io.Main())); 259 std::vector<uint8_t> jpeg_data; 260 if (params.jpeg_codestream != nullptr) { 261 if (jxl::extras::CanDecode(jxl::extras::Codec::kJPG)) { 262 std::vector<uint8_t> jpeg_bytes; 263 extras::PackedPixelFile ppf; 264 JXL_TEST_ASSIGN_OR_DIE(extras::PackedFrame frame, 265 extras::PackedFrame::Create(xsize, ysize, format)); 266 EXPECT_TRUE(frame.color.pixels_size == pixels.size()); 267 memcpy(frame.color.pixels(0, 0, 0), pixels.data(), pixels.size()); 268 ppf.frames.emplace_back(std::move(frame)); 269 ppf.info.xsize = xsize; 270 ppf.info.ysize = ysize; 271 ppf.info.num_color_channels = grayscale ? 1 : 3; 272 ppf.info.bits_per_sample = 16; 273 auto encoder = extras::GetJPEGEncoder(); 274 encoder->SetOption("quality", "70"); 275 extras::EncodedImage encoded; 276 EXPECT_TRUE(encoder->Encode(ppf, &encoded, nullptr)); 277 jpeg_bytes = encoded.bitstreams[0]; 278 Bytes(jpeg_bytes).AppendTo(*params.jpeg_codestream); 279 EXPECT_TRUE(jxl::jpeg::DecodeImageJPG( 280 jxl::Bytes(jpeg_bytes.data(), jpeg_bytes.size()), &io)); 281 EXPECT_TRUE(EncodeJPEGData(memory_manager, *io.Main().jpeg_data, 282 &jpeg_data, params.cparams)); 283 io.metadata.m.xyb_encoded = false; 284 } else { 285 ADD_FAILURE(); 286 } 287 } 288 if (params.preview_mode) { 289 JXL_TEST_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy()); 290 GeneratePreview(params.preview_mode, &io.preview_frame); 291 io.metadata.m.have_preview = true; 292 EXPECT_TRUE(io.metadata.m.preview_size.Set(io.preview_frame.xsize(), 293 io.preview_frame.ysize())); 294 } 295 if (params.add_intrinsic_size) { 296 EXPECT_TRUE(io.metadata.m.intrinsic_size.Set(xsize / 3, ysize / 3)); 297 } 298 io.metadata.m.orientation = params.orientation; 299 std::vector<uint8_t> compressed; 300 EXPECT_TRUE(test::EncodeFile(params.cparams, &io, &compressed)); 301 CodeStreamBoxFormat add_container = params.box_format; 302 if (add_container != kCSBF_None) { 303 // Header with signature box and ftyp box. 304 const uint8_t header[] = {0, 0, 0, 0xc, 0x4a, 0x58, 0x4c, 0x20, 305 0xd, 0xa, 0x87, 0xa, 0, 0, 0, 0x14, 306 0x66, 0x74, 0x79, 0x70, 0x6a, 0x78, 0x6c, 0x20, 307 0, 0, 0, 0, 0x6a, 0x78, 0x6c, 0x20}; 308 309 bool is_multi = add_container == kCSBF_Multi || 310 add_container == kCSBF_Multi_Zero_Terminated || 311 add_container == kCSBF_Multi_Other_Terminated || 312 add_container == kCSBF_Multi_Other_Zero_Terminated || 313 add_container == kCSBF_Multi_First_Empty || 314 add_container == kCSBF_Multi_Last_Empty_Other; 315 316 if (is_multi) { 317 size_t third = compressed.size() / 3; 318 std::vector<uint8_t> compressed0(compressed.data(), 319 compressed.data() + third); 320 std::vector<uint8_t> compressed1(compressed.data() + third, 321 compressed.data() + 2 * third); 322 std::vector<uint8_t> compressed2(compressed.data() + 2 * third, 323 compressed.data() + compressed.size()); 324 325 std::vector<uint8_t> c; 326 Bytes(header).AppendTo(c); 327 if (params.jpeg_codestream != nullptr) { 328 jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, 329 &c); 330 Bytes(jpeg_data).AppendTo(c); 331 } 332 uint32_t jxlp_index = 0; 333 if (add_container == kCSBF_Multi_First_Empty) { 334 // Empty placeholder codestream part 335 AppendU32BE(12, &c); 336 c.push_back('j'); 337 c.push_back('x'); 338 c.push_back('l'); 339 c.push_back('p'); 340 AppendU32BE(jxlp_index++, &c); 341 } 342 // First codestream part 343 AppendU32BE(compressed0.size() + 12, &c); 344 c.push_back('j'); 345 c.push_back('x'); 346 c.push_back('l'); 347 c.push_back('p'); 348 AppendU32BE(jxlp_index++, &c); 349 Bytes(compressed0).AppendTo(c); 350 // A few non-codestream boxes in between 351 AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, &c); 352 AppendTestBox(unk2_box_type, unk2_box_contents, unk2_box_size, false, &c); 353 // Empty placeholder codestream part 354 AppendU32BE(12, &c); 355 c.push_back('j'); 356 c.push_back('x'); 357 c.push_back('l'); 358 c.push_back('p'); 359 AppendU32BE(jxlp_index++, &c); 360 // Second codestream part 361 AppendU32BE(compressed1.size() + 12, &c); 362 c.push_back('j'); 363 c.push_back('x'); 364 c.push_back('l'); 365 c.push_back('p'); 366 AppendU32BE(jxlp_index++, &c); 367 Bytes(compressed1).AppendTo(c); 368 // Third (last) codestream part 369 AppendU32BE(add_container == kCSBF_Multi_Zero_Terminated 370 ? 0 371 : (compressed2.size() + 12), 372 &c); 373 c.push_back('j'); 374 c.push_back('x'); 375 c.push_back('l'); 376 c.push_back('p'); 377 if (add_container != kCSBF_Multi_Last_Empty_Other) { 378 AppendU32BE(jxlp_index++ | 0x80000000, &c); 379 } else { 380 AppendU32BE(jxlp_index++, &c); 381 } 382 Bytes(compressed2).AppendTo(c); 383 if (add_container == kCSBF_Multi_Last_Empty_Other) { 384 // Empty placeholder codestream part 385 AppendU32BE(12, &c); 386 c.push_back('j'); 387 c.push_back('x'); 388 c.push_back('l'); 389 c.push_back('p'); 390 AppendU32BE(jxlp_index++ | 0x80000000, &c); 391 AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, false, 392 &c); 393 } 394 if (add_container == kCSBF_Multi_Other_Terminated) { 395 AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, false, 396 &c); 397 } 398 if (add_container == kCSBF_Multi_Other_Zero_Terminated) { 399 AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, true, 400 &c); 401 } 402 compressed.swap(c); 403 } else { 404 std::vector<uint8_t> c; 405 Bytes(header).AppendTo(c); 406 if (params.jpeg_codestream != nullptr) { 407 jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, 408 &c); 409 Bytes(jpeg_data).AppendTo(c); 410 } 411 if (add_container == kCSBF_Brob_Exif) { 412 Bytes(box_brob_exif, box_brob_exif_size).AppendTo(c); 413 } 414 AppendU32BE(add_container == kCSBF_Single_Zero_Terminated 415 ? 0 416 : (compressed.size() + 8), 417 &c); 418 c.push_back('j'); 419 c.push_back('x'); 420 c.push_back('l'); 421 c.push_back('c'); 422 Bytes(compressed).AppendTo(c); 423 if (add_container == kCSBF_Single_Other) { 424 AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, 425 &c); 426 } 427 compressed.swap(c); 428 } 429 } 430 431 return compressed; 432 } 433 434 JxlDecoderStatus ProcessInputIgnoreBoxes(JxlDecoder* dec) { 435 JxlDecoderStatus status = JXL_DEC_BOX; 436 while (status == JXL_DEC_BOX) { 437 status = JxlDecoderProcessInput(dec); 438 } 439 return status; 440 } 441 442 // Decodes one-shot with the API for non-streaming decoding tests. 443 std::vector<uint8_t> DecodeWithAPI(JxlDecoder* dec, 444 Span<const uint8_t> compressed, 445 const JxlPixelFormat& format, 446 bool use_callback, bool set_buffer_early, 447 bool use_resizable_runner, 448 bool require_boxes, bool expect_success, 449 std::vector<uint8_t>* icc = nullptr) { 450 JxlThreadParallelRunnerPtr runner_fixed; 451 JxlResizableParallelRunnerPtr runner_resizable; 452 JxlParallelRunner runner_fn; 453 void* runner; 454 455 if (use_resizable_runner) { 456 runner_resizable = JxlResizableParallelRunnerMake(nullptr); 457 runner = runner_resizable.get(); 458 runner_fn = JxlResizableParallelRunner; 459 } else { 460 size_t hw_threads = JxlThreadParallelRunnerDefaultNumWorkerThreads(); 461 runner_fixed = 462 JxlThreadParallelRunnerMake(nullptr, std::min<size_t>(hw_threads, 16)); 463 runner = runner_fixed.get(); 464 runner_fn = JxlThreadParallelRunner; 465 } 466 EXPECT_EQ(JXL_DEC_SUCCESS, 467 JxlDecoderSetParallelRunner(dec, runner_fn, runner)); 468 469 auto process_input = 470 require_boxes ? ProcessInputIgnoreBoxes : JxlDecoderProcessInput; 471 472 EXPECT_EQ( 473 JXL_DEC_SUCCESS, 474 JxlDecoderSubscribeEvents( 475 dec, JXL_DEC_BASIC_INFO | (set_buffer_early ? JXL_DEC_FRAME : 0) | 476 JXL_DEC_PREVIEW_IMAGE | JXL_DEC_FULL_IMAGE | 477 (require_boxes ? JXL_DEC_BOX : 0) | 478 (icc != nullptr ? JXL_DEC_COLOR_ENCODING : 0))); 479 480 EXPECT_EQ(JXL_DEC_SUCCESS, 481 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 482 EXPECT_EQ(JXL_DEC_BASIC_INFO, process_input(dec)); 483 size_t buffer_size; 484 EXPECT_EQ(JXL_DEC_SUCCESS, 485 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 486 JxlBasicInfo info; 487 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 488 if (use_resizable_runner) { 489 JxlResizableParallelRunnerSetThreads( 490 runner, 491 JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize)); 492 } 493 494 std::vector<uint8_t> pixels(buffer_size); 495 size_t bytes_per_pixel = format.num_channels * 496 test::GetDataBits(format.data_type) / 497 jxl::kBitsPerByte; 498 size_t stride = bytes_per_pixel * info.xsize; 499 if (format.align > 1) { 500 stride = jxl::DivCeil(stride, format.align) * format.align; 501 } 502 auto callback = [&](size_t x, size_t y, size_t num_pixels, 503 const void* pixels_row) { 504 memcpy(pixels.data() + stride * y + bytes_per_pixel * x, pixels_row, 505 num_pixels * bytes_per_pixel); 506 }; 507 508 JxlDecoderStatus status = process_input(dec); 509 510 if (status == JXL_DEC_COLOR_ENCODING) { 511 size_t icc_size = 0; 512 EXPECT_EQ(JXL_DEC_SUCCESS, 513 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA, 514 &icc_size)); 515 icc->resize(icc_size); 516 EXPECT_EQ(JXL_DEC_SUCCESS, 517 JxlDecoderGetColorAsICCProfile(dec, JXL_COLOR_PROFILE_TARGET_DATA, 518 icc->data(), icc_size)); 519 520 status = process_input(dec); 521 } 522 523 std::vector<uint8_t> preview; 524 if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) { 525 size_t buffer_size; 526 EXPECT_EQ(JXL_DEC_SUCCESS, 527 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); 528 preview.resize(buffer_size); 529 EXPECT_EQ(JXL_DEC_SUCCESS, 530 JxlDecoderSetPreviewOutBuffer(dec, &format, preview.data(), 531 preview.size())); 532 EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, process_input(dec)); 533 534 status = process_input(dec); 535 } 536 537 if (set_buffer_early) { 538 EXPECT_EQ(JXL_DEC_FRAME, status); 539 } else { 540 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, status); 541 } 542 543 if (use_callback) { 544 EXPECT_EQ(JXL_DEC_SUCCESS, 545 JxlDecoderSetImageOutCallback( 546 dec, &format, 547 [](void* opaque, size_t x, size_t y, size_t xsize, 548 const void* pixels_row) { 549 auto cb = static_cast<decltype(&callback)>(opaque); 550 (*cb)(x, y, xsize, pixels_row); 551 }, 552 /*opaque=*/&callback)); 553 } else { 554 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 555 dec, &format, pixels.data(), pixels.size())); 556 } 557 558 EXPECT_EQ(JXL_DEC_FULL_IMAGE, process_input(dec)); 559 560 // After the full image was output, JxlDecoderProcessInput should return 561 // success to indicate all is done, unless we requested boxes and the last 562 // box was not a terminal unbounded box, in which case it should ask for 563 // more input. 564 JxlDecoderStatus expected_status = 565 expect_success ? JXL_DEC_SUCCESS : JXL_DEC_NEED_MORE_INPUT; 566 EXPECT_EQ(expected_status, process_input(dec)); 567 568 return pixels; 569 } 570 571 // Decodes one-shot with the API for non-streaming decoding tests. 572 std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed, 573 const JxlPixelFormat& format, 574 bool use_callback, bool set_buffer_early, 575 bool use_resizable_runner, 576 bool require_boxes, bool expect_success) { 577 JxlDecoder* dec = JxlDecoderCreate(nullptr); 578 std::vector<uint8_t> pixels = 579 DecodeWithAPI(dec, compressed, format, use_callback, set_buffer_early, 580 use_resizable_runner, require_boxes, expect_success); 581 JxlDecoderDestroy(dec); 582 return pixels; 583 } 584 585 } // namespace 586 } // namespace jxl 587 588 //////////////////////////////////////////////////////////////////////////////// 589 590 using ::jxl::Image3F; 591 using ::jxl::ImageF; 592 using ::jxl::test::BoolToCStr; 593 using ::jxl::test::ButteraugliDistance; 594 595 TEST(DecodeTest, JxlSignatureCheckTest) { 596 std::vector<std::pair<int, std::vector<uint8_t>>> tests = { 597 // No JPEGXL header starts with 'a'. 598 {JXL_SIG_INVALID, {'a'}}, 599 {JXL_SIG_INVALID, {'a', 'b', 'c', 'd', 'e', 'f'}}, 600 601 // Empty file is not enough bytes. 602 {JXL_SIG_NOT_ENOUGH_BYTES, {}}, 603 604 // JPEGXL headers. 605 {JXL_SIG_NOT_ENOUGH_BYTES, {0xff}}, // Part of a signature. 606 {JXL_SIG_INVALID, {0xff, 0xD8}}, // JPEG-1 607 {JXL_SIG_CODESTREAM, {0xff, 0x0a}}, 608 609 // JPEGXL container file. 610 {JXL_SIG_CONTAINER, 611 {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87, 0xA}}, 612 // Ending with invalid byte. 613 {JXL_SIG_INVALID, {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87, 0}}, 614 // Part of signature. 615 {JXL_SIG_NOT_ENOUGH_BYTES, 616 {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87}}, 617 {JXL_SIG_NOT_ENOUGH_BYTES, {0}}, 618 }; 619 for (const auto& test : tests) { 620 EXPECT_EQ(test.first, 621 JxlSignatureCheck(test.second.data(), test.second.size())) 622 << "Where test data is " << ::testing::PrintToString(test.second); 623 } 624 } 625 626 TEST(DecodeTest, DefaultAllocTest) { 627 JxlDecoder* dec = JxlDecoderCreate(nullptr); 628 EXPECT_NE(nullptr, dec); 629 JxlDecoderDestroy(dec); 630 } 631 632 TEST(DecodeTest, CustomAllocTest) { 633 struct CalledCounters { 634 int allocs = 0; 635 int frees = 0; 636 } counters; 637 638 JxlMemoryManager mm; 639 mm.opaque = &counters; 640 mm.alloc = [](void* opaque, size_t size) { 641 reinterpret_cast<CalledCounters*>(opaque)->allocs++; 642 return malloc(size); 643 }; 644 mm.free = [](void* opaque, void* address) { 645 reinterpret_cast<CalledCounters*>(opaque)->frees++; 646 free(address); 647 }; 648 649 JxlDecoder* dec = JxlDecoderCreate(&mm); 650 EXPECT_NE(nullptr, dec); 651 EXPECT_LE(1, counters.allocs); 652 EXPECT_EQ(0, counters.frees); 653 JxlDecoderDestroy(dec); 654 EXPECT_LE(1, counters.frees); 655 } 656 657 // TODO(lode): add multi-threaded test when multithreaded pixel decoding from 658 // API is implemented. 659 TEST(DecodeTest, DefaultParallelRunnerTest) { 660 JxlDecoder* dec = JxlDecoderCreate(nullptr); 661 EXPECT_NE(nullptr, dec); 662 EXPECT_EQ(JXL_DEC_SUCCESS, 663 JxlDecoderSetParallelRunner(dec, nullptr, nullptr)); 664 JxlDecoderDestroy(dec); 665 } 666 667 // Creates the header of a JPEG XL file with various custom parameters for 668 // testing. 669 // xsize, ysize: image dimensions to store in the SizeHeader, max 512. 670 // bits_per_sample, orientation: a selection of header parameters to test with. 671 // orientation: image orientation to set in the metadata 672 // alpha_bits: if non-0, alpha extra channel bits to set in the metadata. Also 673 // gives the alpha channel the name "alpha_test" 674 // have_container: add box container format around the codestream. 675 // metadata_default: if true, ImageMetadata is set to default and 676 // bits_per_sample, orientation and alpha_bits are ignored. 677 // insert_box: insert an extra box before the codestream box, making the header 678 // farther away from the front than is ideal. Only used if have_container. 679 std::vector<uint8_t> GetTestHeader(size_t xsize, size_t ysize, 680 size_t bits_per_sample, size_t orientation, 681 size_t alpha_bits, bool xyb_encoded, 682 bool have_container, bool metadata_default, 683 bool insert_extra_box, 684 const jxl::IccBytes& icc_profile) { 685 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 686 jxl::BitWriter writer{memory_manager}; 687 EXPECT_TRUE(writer.WithMaxBits( 688 65536, // Large enough 689 jxl::LayerType::Header, nullptr, [&] { 690 if (have_container) { 691 const std::vector<uint8_t> signature_box = { 692 0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xd, 0xa, 0x87, 0xa}; 693 const std::vector<uint8_t> filetype_box = { 694 0, 0, 0, 0x14, 'f', 't', 'y', 'p', 'j', 'x', 695 'l', ' ', 0, 0, 0, 0, 'j', 'x', 'l', ' '}; 696 const std::vector<uint8_t> extra_box_header = {0, 0, 0, 0xff, 697 't', 'e', 's', 't'}; 698 // Beginning of codestream box, with an arbitrary size certainly large 699 // enough to contain the header 700 const std::vector<uint8_t> codestream_box_header = { 701 0, 0, 0, 0xff, 'j', 'x', 'l', 'c'}; 702 703 for (uint8_t c : signature_box) { 704 writer.Write(8, c); 705 } 706 for (uint8_t c : filetype_box) { 707 writer.Write(8, c); 708 } 709 if (insert_extra_box) { 710 for (uint8_t c : extra_box_header) { 711 writer.Write(8, c); 712 } 713 for (size_t i = 0; i < 255 - 8; i++) { 714 writer.Write(8, 0); 715 } 716 } 717 for (uint8_t c : codestream_box_header) { 718 writer.Write(8, c); 719 } 720 } 721 722 // JXL signature 723 writer.Write(8, 0xff); 724 writer.Write(8, 0x0a); 725 726 // SizeHeader 727 jxl::CodecMetadata metadata; 728 EXPECT_TRUE(metadata.size.Set(xsize, ysize)); 729 EXPECT_TRUE(WriteSizeHeader(metadata.size, &writer, 730 jxl::LayerType::Header, nullptr)); 731 732 if (!metadata_default) { 733 metadata.m.SetUintSamples(bits_per_sample); 734 metadata.m.orientation = orientation; 735 metadata.m.SetAlphaBits(alpha_bits); 736 metadata.m.xyb_encoded = xyb_encoded; 737 if (alpha_bits != 0) { 738 metadata.m.extra_channel_info[0].name = "alpha_test"; 739 } 740 } 741 742 if (!icc_profile.empty()) { 743 jxl::IccBytes copy = icc_profile; 744 EXPECT_TRUE(metadata.m.color_encoding.SetICC(std::move(copy), 745 JxlGetDefaultCms())); 746 } 747 748 EXPECT_TRUE(jxl::Bundle::Write(metadata.m, &writer, 749 jxl::LayerType::Header, nullptr)); 750 metadata.transform_data.nonserialized_xyb_encoded = 751 metadata.m.xyb_encoded; 752 EXPECT_TRUE(jxl::Bundle::Write(metadata.transform_data, &writer, 753 jxl::LayerType::Header, nullptr)); 754 755 if (!icc_profile.empty()) { 756 EXPECT_TRUE(metadata.m.color_encoding.WantICC()); 757 EXPECT_TRUE(jxl::WriteICC(jxl::Span<const uint8_t>(icc_profile), 758 &writer, jxl::LayerType::Header, nullptr)); 759 } 760 761 writer.ZeroPadToByte(); 762 return true; 763 })); 764 jxl::Bytes bytes = writer.GetSpan(); 765 return std::vector<uint8_t>(bytes.data(), bytes.data() + bytes.size()); 766 } 767 768 TEST(DecodeTest, BasicInfoTest) { 769 size_t xsize[2] = {50, 33}; 770 size_t ysize[2] = {50, 77}; 771 size_t bits_per_sample[2] = {8, 23}; 772 size_t orientation[2] = {3, 5}; 773 size_t alpha_bits[2] = {0, 8}; 774 bool have_container[2] = {false, true}; 775 bool xyb_encoded = false; 776 777 std::vector<std::vector<uint8_t>> test_samples; 778 // Test with direct codestream 779 test_samples.push_back(GetTestHeader( 780 xsize[0], ysize[0], bits_per_sample[0], orientation[0], alpha_bits[0], 781 xyb_encoded, have_container[0], /*metadata_default=*/false, 782 /*insert_extra_box=*/false, {})); 783 // Test with container and different parameters 784 test_samples.push_back(GetTestHeader( 785 xsize[1], ysize[1], bits_per_sample[1], orientation[1], alpha_bits[1], 786 xyb_encoded, have_container[1], /*metadata_default=*/false, 787 /*insert_extra_box=*/false, {})); 788 789 for (size_t i = 0; i < test_samples.size(); ++i) { 790 const std::vector<uint8_t>& data = test_samples[i]; 791 // Test decoding too small header first, until we reach the final byte. 792 for (size_t size = 0; size <= data.size(); ++size) { 793 // Test with a new decoder for each tested byte size. 794 JxlDecoder* dec = JxlDecoderCreate(nullptr); 795 EXPECT_EQ(JXL_DEC_SUCCESS, 796 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 797 const uint8_t* next_in = data.data(); 798 size_t avail_in = size; 799 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 800 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 801 802 JxlBasicInfo info; 803 JxlDecoderStatus bi_status = JxlDecoderGetBasicInfo(dec, &info); 804 bool have_basic_info = (bi_status == JXL_DEC_SUCCESS); 805 806 if (size == data.size()) { 807 EXPECT_EQ(JXL_DEC_BASIC_INFO, status); 808 809 // All header bytes given so the decoder must have the basic info. 810 EXPECT_EQ(true, have_basic_info); 811 EXPECT_EQ(have_container[i], FROM_JXL_BOOL(info.have_container)); 812 EXPECT_EQ(alpha_bits[i], info.alpha_bits); 813 // Orientations 5..8 swap the dimensions 814 if (orientation[i] >= 5) { 815 EXPECT_EQ(xsize[i], info.ysize); 816 EXPECT_EQ(ysize[i], info.xsize); 817 } else { 818 EXPECT_EQ(xsize[i], info.xsize); 819 EXPECT_EQ(ysize[i], info.ysize); 820 } 821 // The API should set the orientation to identity by default since it 822 // already applies the transformation internally by default. 823 EXPECT_EQ(1u, info.orientation); 824 825 EXPECT_EQ(3u, info.num_color_channels); 826 827 if (alpha_bits[i] != 0) { 828 // Expect an extra channel 829 EXPECT_EQ(1u, info.num_extra_channels); 830 JxlExtraChannelInfo extra; 831 EXPECT_EQ(0, JxlDecoderGetExtraChannelInfo(dec, 0, &extra)); 832 EXPECT_EQ(alpha_bits[i], extra.bits_per_sample); 833 EXPECT_EQ(JXL_CHANNEL_ALPHA, extra.type); 834 EXPECT_EQ(0, extra.alpha_premultiplied); 835 // Verify the name "alpha_test" given to the alpha channel 836 EXPECT_EQ(10u, extra.name_length); 837 char name[11]; 838 EXPECT_EQ(0, 839 JxlDecoderGetExtraChannelName(dec, 0, name, sizeof(name))); 840 EXPECT_EQ(std::string("alpha_test"), std::string(name)); 841 } else { 842 EXPECT_EQ(0u, info.num_extra_channels); 843 } 844 845 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 846 } else { 847 // If we did not give the full header, the basic info should not be 848 // available. Allow a few bytes of slack due to some bits for default 849 // opsinmatrix/extension bits. 850 if (size + 2 < data.size()) { 851 EXPECT_EQ(false, have_basic_info); 852 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, status); 853 } 854 } 855 856 // Test that decoder doesn't allow setting a setting required at beginning 857 // unless it's reset 858 EXPECT_EQ(JXL_DEC_ERROR, 859 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 860 JxlDecoderReset(dec); 861 EXPECT_EQ(JXL_DEC_SUCCESS, 862 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 863 864 JxlDecoderDestroy(dec); 865 } 866 } 867 } 868 869 TEST(DecodeTest, BufferSizeTest) { 870 size_t xsize = 33; 871 size_t ysize = 77; 872 size_t bits_per_sample = 8; 873 size_t orientation = 1; 874 size_t alpha_bits = 8; 875 bool have_container = false; 876 bool xyb_encoded = false; 877 878 std::vector<uint8_t> header = 879 GetTestHeader(xsize, ysize, bits_per_sample, orientation, alpha_bits, 880 xyb_encoded, have_container, /*metadata_default=*/false, 881 /*insert_extra_box=*/false, {}); 882 883 JxlDecoder* dec = JxlDecoderCreate(nullptr); 884 EXPECT_EQ(JXL_DEC_SUCCESS, 885 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 886 const uint8_t* next_in = header.data(); 887 size_t avail_in = header.size(); 888 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 889 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 890 EXPECT_EQ(JXL_DEC_BASIC_INFO, status); 891 892 JxlBasicInfo info; 893 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 894 EXPECT_EQ(xsize, info.xsize); 895 EXPECT_EQ(ysize, info.ysize); 896 897 JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 898 size_t image_out_size; 899 EXPECT_EQ(JXL_DEC_SUCCESS, 900 JxlDecoderImageOutBufferSize(dec, &format, &image_out_size)); 901 EXPECT_EQ(xsize * ysize * 4, image_out_size); 902 903 JxlDecoderDestroy(dec); 904 } 905 906 TEST(DecodeTest, BasicInfoSizeHintTest) { 907 // Test on a file where the size hint is too small initially due to inserting 908 // a box before the codestream (something that is normally not recommended) 909 size_t xsize = 50; 910 size_t ysize = 50; 911 size_t bits_per_sample = 16; 912 size_t orientation = 1; 913 size_t alpha_bits = 0; 914 bool xyb_encoded = false; 915 std::vector<uint8_t> data = GetTestHeader( 916 xsize, ysize, bits_per_sample, orientation, alpha_bits, xyb_encoded, 917 /*have_container=*/true, /*metadata_default=*/false, 918 /*insert_extra_box=*/true, {}); 919 920 JxlDecoderStatus status; 921 JxlDecoder* dec = JxlDecoderCreate(nullptr); 922 EXPECT_EQ(JXL_DEC_SUCCESS, 923 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 924 925 size_t hint0 = JxlDecoderSizeHintBasicInfo(dec); 926 // Test that the test works as intended: we construct a file on purpose to 927 // be larger than the first hint by having that extra box. 928 EXPECT_LT(hint0, data.size()); 929 const uint8_t* next_in = data.data(); 930 // Do as if we have only as many bytes as indicated by the hint available 931 size_t avail_in = std::min(hint0, data.size()); 932 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 933 status = JxlDecoderProcessInput(dec); 934 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, status); 935 // Basic info cannot be available yet due to the extra inserted box. 936 EXPECT_EQ(false, !JxlDecoderGetBasicInfo(dec, nullptr)); 937 938 size_t num_read = avail_in - JxlDecoderReleaseInput(dec); 939 EXPECT_LT(num_read, data.size()); 940 941 size_t hint1 = JxlDecoderSizeHintBasicInfo(dec); 942 // The hint must be larger than the previous hint (taking already processed 943 // bytes into account, the hint is a hint for the next avail_in) since the 944 // decoder now knows there is a box in between. 945 EXPECT_GT(hint1 + num_read, hint0); 946 avail_in = std::min<size_t>(hint1, data.size() - num_read); 947 next_in += num_read; 948 949 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 950 status = JxlDecoderProcessInput(dec); 951 EXPECT_EQ(JXL_DEC_BASIC_INFO, status); 952 JxlBasicInfo info; 953 // We should have the basic info now, since we only added one box in-between, 954 // and the decoder should have known its size, its implementation can return 955 // a correct hint. 956 EXPECT_EQ(true, !JxlDecoderGetBasicInfo(dec, &info)); 957 958 // Also test if the basic info is correct. 959 EXPECT_EQ(1, info.have_container); 960 EXPECT_EQ(xsize, info.xsize); 961 EXPECT_EQ(ysize, info.ysize); 962 EXPECT_EQ(orientation, info.orientation); 963 EXPECT_EQ(bits_per_sample, info.bits_per_sample); 964 965 JxlDecoderDestroy(dec); 966 } 967 968 std::vector<uint8_t> GetIccTestHeader(const jxl::IccBytes& icc_profile, 969 bool xyb_encoded) { 970 size_t xsize = 50; 971 size_t ysize = 50; 972 size_t bits_per_sample = 16; 973 size_t orientation = 1; 974 size_t alpha_bits = 0; 975 return GetTestHeader(xsize, ysize, bits_per_sample, orientation, alpha_bits, 976 xyb_encoded, 977 /*have_container=*/false, /*metadata_default=*/false, 978 /*insert_extra_box=*/false, icc_profile); 979 } 980 981 // Tests the case where pixels and metadata ICC profile are the same 982 TEST(DecodeTest, IccProfileTestOriginal) { 983 jxl::IccBytes icc_profile = GetIccTestProfile(); 984 bool xyb_encoded = false; 985 std::vector<uint8_t> data = GetIccTestHeader(icc_profile, xyb_encoded); 986 987 JxlDecoder* dec = JxlDecoderCreate(nullptr); 988 EXPECT_EQ(JXL_DEC_SUCCESS, 989 JxlDecoderSubscribeEvents( 990 dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING)); 991 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size())); 992 993 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 994 995 // Expect the opposite of xyb_encoded for uses_original_profile 996 JxlBasicInfo info; 997 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 998 EXPECT_EQ(JXL_TRUE, info.uses_original_profile); 999 1000 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 1001 1002 // the encoded color profile expected to be not available, since the image 1003 // has an ICC profile instead 1004 EXPECT_EQ(JXL_DEC_ERROR, 1005 JxlDecoderGetColorAsEncodedProfile( 1006 dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, nullptr)); 1007 1008 size_t dec_profile_size; 1009 EXPECT_EQ(JXL_DEC_SUCCESS, 1010 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1011 &dec_profile_size)); 1012 1013 // Check that can get return status with NULL size 1014 EXPECT_EQ(JXL_DEC_SUCCESS, 1015 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1016 nullptr)); 1017 1018 // The profiles must be equal. This requires they have equal size, and if 1019 // they do, we can get the profile and compare the contents. 1020 EXPECT_EQ(icc_profile.size(), dec_profile_size); 1021 if (icc_profile.size() == dec_profile_size) { 1022 jxl::IccBytes icc_profile2(icc_profile.size()); 1023 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile( 1024 dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1025 icc_profile2.data(), icc_profile2.size())); 1026 EXPECT_EQ(icc_profile, icc_profile2); 1027 } 1028 1029 // the data is not xyb_encoded, so same result expected for the pixel data 1030 // color profile 1031 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetColorAsEncodedProfile( 1032 dec, JXL_COLOR_PROFILE_TARGET_DATA, nullptr)); 1033 1034 EXPECT_EQ(JXL_DEC_SUCCESS, 1035 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA, 1036 &dec_profile_size)); 1037 EXPECT_EQ(icc_profile.size(), dec_profile_size); 1038 1039 JxlDecoderDestroy(dec); 1040 } 1041 1042 // Tests the case where pixels and metadata ICC profile are different 1043 TEST(DecodeTest, IccProfileTestXybEncoded) { 1044 jxl::IccBytes icc_profile = GetIccTestProfile(); 1045 bool xyb_encoded = true; 1046 std::vector<uint8_t> data = GetIccTestHeader(icc_profile, xyb_encoded); 1047 1048 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1049 EXPECT_EQ(JXL_DEC_SUCCESS, 1050 JxlDecoderSubscribeEvents( 1051 dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING)); 1052 1053 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size())); 1054 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 1055 1056 // Expect the opposite of xyb_encoded for uses_original_profile 1057 JxlBasicInfo info; 1058 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 1059 EXPECT_EQ(JXL_FALSE, info.uses_original_profile); 1060 1061 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 1062 1063 // the encoded color profile expected to be not available, since the image 1064 // has an ICC profile instead 1065 EXPECT_EQ(JXL_DEC_ERROR, 1066 JxlDecoderGetColorAsEncodedProfile( 1067 dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, nullptr)); 1068 1069 // Check that can get return status with NULL size 1070 EXPECT_EQ(JXL_DEC_SUCCESS, 1071 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1072 nullptr)); 1073 1074 size_t dec_profile_size; 1075 EXPECT_EQ(JXL_DEC_SUCCESS, 1076 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1077 &dec_profile_size)); 1078 1079 // The profiles must be equal. This requires they have equal size, and if 1080 // they do, we can get the profile and compare the contents. 1081 EXPECT_EQ(icc_profile.size(), dec_profile_size); 1082 if (icc_profile.size() == dec_profile_size) { 1083 jxl::IccBytes icc_profile2(icc_profile.size()); 1084 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile( 1085 dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, 1086 icc_profile2.data(), icc_profile2.size())); 1087 EXPECT_EQ(icc_profile, icc_profile2); 1088 } 1089 1090 // Data is xyb_encoded, so the data profile is a different profile, encoded 1091 // as structured profile. 1092 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsEncodedProfile( 1093 dec, JXL_COLOR_PROFILE_TARGET_DATA, nullptr)); 1094 JxlColorEncoding pixel_encoding; 1095 EXPECT_EQ(JXL_DEC_SUCCESS, 1096 JxlDecoderGetColorAsEncodedProfile( 1097 dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding)); 1098 EXPECT_EQ(JXL_PRIMARIES_SRGB, pixel_encoding.primaries); 1099 // The API returns LINEAR by default when the colorspace cannot be represented 1100 // by enum values. 1101 EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function); 1102 1103 // Test the same but with integer format. 1104 EXPECT_EQ(JXL_DEC_SUCCESS, 1105 JxlDecoderGetColorAsEncodedProfile( 1106 dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding)); 1107 EXPECT_EQ(JXL_PRIMARIES_SRGB, pixel_encoding.primaries); 1108 EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function); 1109 1110 // Test after setting the preferred color profile to non-linear sRGB: 1111 // for XYB images with ICC profile, this setting is expected to take effect. 1112 jxl::ColorEncoding temp_jxl_srgb = jxl::ColorEncoding::SRGB(false); 1113 JxlColorEncoding pixel_encoding_srgb = temp_jxl_srgb.ToExternal(); 1114 EXPECT_EQ(JXL_DEC_SUCCESS, 1115 JxlDecoderSetPreferredColorProfile(dec, &pixel_encoding_srgb)); 1116 EXPECT_EQ(JXL_DEC_SUCCESS, 1117 JxlDecoderGetColorAsEncodedProfile( 1118 dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding)); 1119 EXPECT_EQ(JXL_TRANSFER_FUNCTION_SRGB, pixel_encoding.transfer_function); 1120 1121 // The decoder can also output this as a generated ICC profile anyway, and 1122 // we're certain that it will differ from the above defined profile since 1123 // the sRGB data should not have swapped R/G/B primaries. 1124 1125 EXPECT_EQ(JXL_DEC_SUCCESS, 1126 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA, 1127 &dec_profile_size)); 1128 // We don't need to dictate exactly what size the generated ICC profile 1129 // must be (since there are many ways to represent the same color space), 1130 // but it should not be zero. 1131 EXPECT_NE(0u, dec_profile_size); 1132 jxl::IccBytes icc_profile2(dec_profile_size); 1133 if (0 != dec_profile_size) { 1134 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile( 1135 dec, JXL_COLOR_PROFILE_TARGET_DATA, 1136 icc_profile2.data(), icc_profile2.size())); 1137 // expected not equal 1138 EXPECT_NE(icc_profile, icc_profile2); 1139 } 1140 1141 // Test setting another different preferred profile, to verify that the 1142 // returned JXL_COLOR_PROFILE_TARGET_DATA ICC profile is correctly 1143 // updated. 1144 1145 jxl::ColorEncoding temp_jxl_linear = jxl::ColorEncoding::LinearSRGB(false); 1146 JxlColorEncoding pixel_encoding_linear = temp_jxl_linear.ToExternal(); 1147 1148 EXPECT_EQ(JXL_DEC_SUCCESS, 1149 JxlDecoderSetPreferredColorProfile(dec, &pixel_encoding_linear)); 1150 EXPECT_EQ(JXL_DEC_SUCCESS, 1151 JxlDecoderGetColorAsEncodedProfile( 1152 dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding)); 1153 EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function); 1154 EXPECT_EQ(JXL_DEC_SUCCESS, 1155 JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA, 1156 &dec_profile_size)); 1157 EXPECT_NE(0u, dec_profile_size); 1158 jxl::IccBytes icc_profile3(dec_profile_size); 1159 if (0 != dec_profile_size) { 1160 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile( 1161 dec, JXL_COLOR_PROFILE_TARGET_DATA, 1162 icc_profile3.data(), icc_profile3.size())); 1163 // expected not equal to the previously set preferred profile. 1164 EXPECT_NE(icc_profile2, icc_profile3); 1165 } 1166 1167 JxlDecoderDestroy(dec); 1168 } 1169 1170 // Test decoding ICC from partial files byte for byte. 1171 // This test must pass also if JXL_CRASH_ON_ERROR is enabled, that is, the 1172 // decoding of the ANS histogram and stream of the encoded ICC profile must also 1173 // handle the case of not enough input bytes with StatusCode::kNotEnoughBytes 1174 // rather than fatal error status codes. 1175 TEST(DecodeTest, ICCPartialTest) { 1176 jxl::IccBytes icc_profile = GetIccTestProfile(); 1177 std::vector<uint8_t> data = GetIccTestHeader(icc_profile, false); 1178 1179 const uint8_t* next_in = data.data(); 1180 size_t avail_in = 0; 1181 1182 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1183 1184 EXPECT_EQ(JXL_DEC_SUCCESS, 1185 JxlDecoderSubscribeEvents( 1186 dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING)); 1187 1188 bool seen_basic_info = false; 1189 bool seen_color_encoding = false; 1190 size_t total_size = 0; 1191 1192 for (;;) { 1193 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 1194 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 1195 size_t remaining = JxlDecoderReleaseInput(dec); 1196 EXPECT_LE(remaining, avail_in); 1197 next_in += avail_in - remaining; 1198 avail_in = remaining; 1199 if (status == JXL_DEC_NEED_MORE_INPUT) { 1200 if (total_size >= data.size()) { 1201 // End of partial codestream with codestrema headers and ICC profile 1202 // reached, it should not require more input since full image is not 1203 // requested 1204 FAIL(); 1205 break; 1206 } 1207 size_t increment = 1; 1208 if (total_size + increment > data.size()) { 1209 increment = data.size() - total_size; 1210 } 1211 total_size += increment; 1212 avail_in += increment; 1213 } else if (status == JXL_DEC_BASIC_INFO) { 1214 EXPECT_FALSE(seen_basic_info); 1215 seen_basic_info = true; 1216 } else if (status == JXL_DEC_COLOR_ENCODING) { 1217 EXPECT_TRUE(seen_basic_info); 1218 EXPECT_FALSE(seen_color_encoding); 1219 seen_color_encoding = true; 1220 1221 // Sanity check that the ICC profile was decoded correctly 1222 size_t dec_profile_size; 1223 EXPECT_EQ(JXL_DEC_SUCCESS, 1224 JxlDecoderGetICCProfileSize( 1225 dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &dec_profile_size)); 1226 EXPECT_EQ(icc_profile.size(), dec_profile_size); 1227 1228 } else if (status == JXL_DEC_SUCCESS) { 1229 EXPECT_TRUE(seen_color_encoding); 1230 break; 1231 } else { 1232 // We do not expect any other events or errors 1233 FAIL(); 1234 break; 1235 } 1236 } 1237 1238 EXPECT_TRUE(seen_basic_info); 1239 EXPECT_TRUE(seen_color_encoding); 1240 1241 JxlDecoderDestroy(dec); 1242 } 1243 1244 struct PixelTestConfig { 1245 // Input image definition. 1246 bool grayscale; 1247 bool include_alpha; 1248 size_t xsize; 1249 size_t ysize; 1250 jxl::PreviewMode preview_mode; 1251 bool add_intrinsic_size; 1252 // Output format. 1253 JxlEndianness endianness; 1254 JxlDataType data_type; 1255 uint32_t output_channels; 1256 // Container options. 1257 CodeStreamBoxFormat add_container; 1258 // Decoding mode. 1259 bool use_callback; 1260 bool set_buffer_early; 1261 bool use_resizable_runner; 1262 // Exif orientation, 1-8 1263 JxlOrientation orientation; 1264 bool keep_orientation; 1265 size_t upsampling; 1266 }; 1267 1268 class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {}; 1269 1270 TEST_P(DecodeTestParam, PixelTest) { 1271 PixelTestConfig config = GetParam(); 1272 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1273 1274 if (config.keep_orientation) { 1275 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetKeepOrientation(dec, JXL_TRUE)); 1276 } 1277 1278 size_t num_pixels = config.xsize * config.ysize; 1279 uint32_t orig_channels = 1280 (config.grayscale ? 1 : 3) + (config.include_alpha ? 1 : 0); 1281 std::vector<uint8_t> pixels = 1282 jxl::test::GetSomeTestImage(config.xsize, config.ysize, orig_channels, 0); 1283 JxlPixelFormat format_orig = {orig_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 1284 0}; 1285 jxl::TestCodestreamParams params; 1286 // Lossless to verify pixels exactly after roundtrip. 1287 params.cparams.SetLossless(); 1288 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 1289 params.cparams.resampling = config.upsampling; 1290 params.cparams.ec_resampling = config.upsampling; 1291 params.box_format = config.add_container; 1292 params.orientation = config.orientation; 1293 params.preview_mode = config.preview_mode; 1294 params.add_intrinsic_size = config.add_intrinsic_size; 1295 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 1296 jxl::Bytes(pixels.data(), pixels.size()), config.xsize, config.ysize, 1297 orig_channels, params); 1298 1299 JxlPixelFormat format = {config.output_channels, config.data_type, 1300 config.endianness, 0}; 1301 1302 bool swap_xy = !config.keep_orientation && (config.orientation > 4); 1303 size_t xsize = swap_xy ? config.ysize : config.xsize; 1304 size_t ysize = swap_xy ? config.xsize : config.ysize; 1305 1306 std::vector<uint8_t> pixels2 = 1307 jxl::DecodeWithAPI(dec, jxl::Bytes(compressed.data(), compressed.size()), 1308 format, config.use_callback, config.set_buffer_early, 1309 config.use_resizable_runner, /*require_boxes=*/false, 1310 /*expect_success=*/true); 1311 JxlDecoderReset(dec); 1312 EXPECT_EQ(num_pixels * config.output_channels * 1313 jxl::test::GetDataBits(config.data_type) / jxl::kBitsPerByte, 1314 pixels2.size()); 1315 1316 // If an orientation transformation is expected, to compare the pixels, also 1317 // apply this transformation to the original pixels. ConvertToExternal is 1318 // used to achieve this, with a temporary conversion to CodecInOut and back. 1319 if (config.orientation > 1 && !config.keep_orientation) { 1320 jxl::Span<const uint8_t> bytes(pixels.data(), pixels.size()); 1321 jxl::ColorEncoding color_encoding = 1322 jxl::ColorEncoding::SRGB(config.grayscale); 1323 1324 jxl::CodecInOut io{jxl::test::MemoryManager()}; 1325 if (config.include_alpha) io.metadata.m.SetAlphaBits(16); 1326 io.metadata.m.color_encoding = color_encoding; 1327 ASSERT_TRUE(io.SetSize(config.xsize, config.ysize)); 1328 1329 EXPECT_TRUE(ConvertFromExternal(bytes, config.xsize, config.ysize, 1330 color_encoding, 16, format_orig, nullptr, 1331 &io.Main())); 1332 1333 for (uint8_t& pixel : pixels) pixel = 0; 1334 EXPECT_TRUE(ConvertToExternal( 1335 io.Main(), 16, 1336 /*float_out=*/false, orig_channels, JXL_BIG_ENDIAN, 1337 xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(), 1338 /*out_callback=*/{}, 1339 static_cast<jxl::Orientation>(config.orientation))); 1340 } 1341 if (config.upsampling == 1) { 1342 EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 1343 ysize, format_orig, format)); 1344 } else { 1345 // resampling is of course not lossless, so as a rough check: 1346 // count pixels that are more than off-by-25 in the 8-bit value of one of 1347 // the channels 1348 EXPECT_LE( 1349 jxl::test::ComparePixels( 1350 pixels.data(), pixels2.data(), xsize, ysize, format_orig, format, 1351 50.0 * (config.data_type == JXL_TYPE_UINT8 ? 1.0 : 256.0)), 1352 300u); 1353 } 1354 1355 JxlDecoderDestroy(dec); 1356 } 1357 1358 std::vector<PixelTestConfig> GeneratePixelTests() { 1359 std::vector<PixelTestConfig> all_tests; 1360 struct ChannelInfo { 1361 bool grayscale; 1362 bool include_alpha; 1363 size_t output_channels; 1364 }; 1365 ChannelInfo ch_info[] = { 1366 {false, true, 4}, // RGBA -> RGBA 1367 {true, false, 1}, // G -> G 1368 {true, true, 1}, // GA -> G 1369 {true, true, 2}, // GA -> GA 1370 {false, false, 3}, // RGB -> RGB 1371 {false, true, 3}, // RGBA -> RGB 1372 {false, false, 4}, // RGB -> RGBA 1373 }; 1374 1375 struct OutputFormat { 1376 JxlEndianness endianness; 1377 JxlDataType data_type; 1378 }; 1379 OutputFormat out_formats[] = { 1380 {JXL_NATIVE_ENDIAN, JXL_TYPE_UINT8}, 1381 {JXL_LITTLE_ENDIAN, JXL_TYPE_UINT16}, 1382 {JXL_BIG_ENDIAN, JXL_TYPE_UINT16}, 1383 {JXL_NATIVE_ENDIAN, JXL_TYPE_FLOAT16}, 1384 {JXL_LITTLE_ENDIAN, JXL_TYPE_FLOAT}, 1385 {JXL_BIG_ENDIAN, JXL_TYPE_FLOAT}, 1386 }; 1387 1388 auto make_test = [&](ChannelInfo ch, size_t xsize, size_t ysize, 1389 jxl::PreviewMode preview_mode, bool intrinsic_size, 1390 CodeStreamBoxFormat box, JxlOrientation orientation, 1391 bool keep_orientation, OutputFormat format, 1392 bool use_callback, bool set_buffer_early, 1393 bool resizable_runner, size_t upsampling) { 1394 PixelTestConfig c; 1395 c.grayscale = ch.grayscale; 1396 c.include_alpha = ch.include_alpha; 1397 c.preview_mode = preview_mode; 1398 c.add_intrinsic_size = intrinsic_size; 1399 c.xsize = xsize; 1400 c.ysize = ysize; 1401 c.add_container = box; 1402 c.output_channels = ch.output_channels; 1403 c.data_type = format.data_type; 1404 c.endianness = format.endianness; 1405 c.use_callback = use_callback; 1406 c.set_buffer_early = set_buffer_early; 1407 c.use_resizable_runner = resizable_runner; 1408 c.orientation = orientation; 1409 c.keep_orientation = keep_orientation; 1410 c.upsampling = upsampling; 1411 all_tests.push_back(c); 1412 }; 1413 1414 // Test output formats and methods. 1415 for (ChannelInfo ch : ch_info) { 1416 for (bool use_callback : {false, true}) { 1417 for (size_t upsampling : {1, 2, 4, 8}) { 1418 for (OutputFormat fmt : out_formats) { 1419 make_test(ch, 301, 33, jxl::kNoPreview, 1420 /*add_intrinsic_size=*/false, 1421 CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY, 1422 /*keep_orientation=*/false, fmt, use_callback, 1423 /*set_buffer_early=*/false, /*resizable_runner=*/false, 1424 upsampling); 1425 } 1426 } 1427 } 1428 } 1429 // Test codestream formats. 1430 for (size_t box = 1; box < kCSBF_NUM_ENTRIES; ++box) { 1431 make_test(ch_info[0], 77, 33, jxl::kNoPreview, 1432 /*add_intrinsic_size=*/false, 1433 static_cast<CodeStreamBoxFormat>(box), JXL_ORIENT_IDENTITY, 1434 /*keep_orientation=*/false, out_formats[0], 1435 /*use_callback=*/false, 1436 /*set_buffer_early=*/false, /*resizable_runner=*/false, 1); 1437 } 1438 // Test previews. 1439 for (int preview_mode = 0; preview_mode < jxl::kNumPreviewModes; 1440 preview_mode++) { 1441 make_test(ch_info[0], 77, 33, static_cast<jxl::PreviewMode>(preview_mode), 1442 /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None, 1443 JXL_ORIENT_IDENTITY, 1444 /*keep_orientation=*/false, out_formats[0], 1445 /*use_callback=*/false, /*set_buffer_early=*/false, 1446 /*resizable_runner=*/false, 1); 1447 } 1448 // Test intrinsic sizes. 1449 for (bool add_intrinsic_size : {false, true}) { 1450 make_test(ch_info[0], 55, 34, jxl::kNoPreview, add_intrinsic_size, 1451 CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY, 1452 /*keep_orientation=*/false, out_formats[0], 1453 /*use_callback=*/false, /*set_buffer_early=*/false, 1454 /*resizable_runner=*/false, 1); 1455 } 1456 // Test setting buffers early. 1457 make_test(ch_info[0], 300, 33, jxl::kNoPreview, 1458 /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None, 1459 JXL_ORIENT_IDENTITY, 1460 /*keep_orientation=*/false, out_formats[0], 1461 /*use_callback=*/false, /*set_buffer_early=*/true, 1462 /*resizable_runner=*/false, 1); 1463 1464 // Test using the resizable runner 1465 for (size_t i = 0; i < 4; i++) { 1466 make_test(ch_info[0], 300 << i, 33 << i, jxl::kNoPreview, 1467 /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None, 1468 JXL_ORIENT_IDENTITY, 1469 /*keep_orientation=*/false, out_formats[0], 1470 /*use_callback=*/false, /*set_buffer_early=*/false, 1471 /*resizable_runner=*/true, 1); 1472 } 1473 1474 // Test orientations. 1475 for (int orientation = 2; orientation <= 8; ++orientation) { 1476 for (bool keep_orientation : {false, true}) { 1477 for (bool use_callback : {false, true}) { 1478 for (ChannelInfo ch : ch_info) { 1479 for (OutputFormat fmt : out_formats) { 1480 make_test(ch, 280, 12, jxl::kNoPreview, 1481 /*add_intrinsic_size=*/false, 1482 CodeStreamBoxFormat::kCSBF_None, 1483 static_cast<JxlOrientation>(orientation), 1484 /*keep_orientation=*/keep_orientation, fmt, 1485 /*use_callback=*/use_callback, /*set_buffer_early=*/true, 1486 /*resizable_runner=*/false, 1); 1487 } 1488 } 1489 } 1490 } 1491 } 1492 1493 return all_tests; 1494 } 1495 1496 std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) { 1497 os << c.xsize << "x" << c.ysize; 1498 const char* colors[] = {"", "G", "GA", "RGB", "RGBA"}; 1499 os << colors[(c.grayscale ? 1 : 3) + (c.include_alpha ? 1 : 0)]; 1500 os << "to"; 1501 os << colors[c.output_channels]; 1502 switch (c.data_type) { 1503 case JXL_TYPE_UINT8: 1504 os << "u8"; 1505 break; 1506 case JXL_TYPE_UINT16: 1507 os << "u16"; 1508 break; 1509 case JXL_TYPE_FLOAT: 1510 os << "f32"; 1511 break; 1512 case JXL_TYPE_FLOAT16: 1513 os << "f16"; 1514 break; 1515 default: 1516 ADD_FAILURE(); 1517 }; 1518 if (jxl::test::GetDataBits(c.data_type) > jxl::kBitsPerByte) { 1519 if (c.endianness == JXL_NATIVE_ENDIAN) { 1520 // add nothing 1521 } else if (c.endianness == JXL_BIG_ENDIAN) { 1522 os << "BE"; 1523 } else if (c.endianness == JXL_LITTLE_ENDIAN) { 1524 os << "LE"; 1525 } 1526 } 1527 if (c.add_container != CodeStreamBoxFormat::kCSBF_None) { 1528 os << "Box"; 1529 os << static_cast<size_t>(c.add_container); 1530 } 1531 if (c.preview_mode == jxl::kSmallPreview) os << "Preview"; 1532 if (c.preview_mode == jxl::kBigPreview) os << "BigPreview"; 1533 if (c.add_intrinsic_size) os << "IntrinicSize"; 1534 if (c.use_callback) os << "Callback"; 1535 if (c.set_buffer_early) os << "EarlyBuffer"; 1536 if (c.use_resizable_runner) os << "ResizableRunner"; 1537 if (c.orientation != 1) os << "O" << c.orientation; 1538 if (c.keep_orientation) os << "Keep"; 1539 if (c.upsampling > 1) os << "x" << c.upsampling; 1540 return os; 1541 } 1542 1543 std::string PixelTestDescription( 1544 const testing::TestParamInfo<DecodeTestParam::ParamType>& info) { 1545 std::stringstream name; 1546 name << info.param; 1547 return name.str(); 1548 } 1549 1550 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeTest, DecodeTestParam, 1551 testing::ValuesIn(GeneratePixelTests()), 1552 PixelTestDescription); 1553 1554 TEST(DecodeTest, PixelTestWithICCProfileLossless) { 1555 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1556 1557 size_t xsize = 123; 1558 size_t ysize = 77; 1559 size_t num_pixels = xsize * ysize; 1560 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 1561 JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1562 jxl::TestCodestreamParams params; 1563 // Lossless to verify pixels exactly after roundtrip. 1564 params.cparams.SetLossless(); 1565 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 1566 params.add_icc_profile = true; 1567 // For variation: some have container and no preview, others have preview 1568 // and no container. 1569 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 1570 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 1571 1572 for (uint32_t channels = 3; channels <= 4; ++channels) { 1573 { 1574 JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 1575 1576 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 1577 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 1578 /*use_callback=*/false, /*set_buffer_early=*/false, 1579 /*use_resizable_runner=*/false, /*require_boxes=*/false, 1580 /*expect_success=*/true); 1581 JxlDecoderReset(dec); 1582 EXPECT_EQ(num_pixels * channels, pixels2.size()); 1583 EXPECT_EQ(0u, 1584 jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 1585 ysize, format_orig, format)); 1586 } 1587 { 1588 JxlPixelFormat format = {channels, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0}; 1589 1590 // Test with the container for one of the pixel formats. 1591 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 1592 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 1593 /*use_callback=*/true, /*set_buffer_early=*/true, 1594 /*use_resizable_runner=*/false, /*require_boxes=*/false, 1595 /*expect_success=*/true); 1596 JxlDecoderReset(dec); 1597 EXPECT_EQ(num_pixels * channels * 2, pixels2.size()); 1598 EXPECT_EQ(0u, 1599 jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 1600 ysize, format_orig, format)); 1601 } 1602 1603 { 1604 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 1605 1606 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 1607 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 1608 /*use_callback=*/false, /*set_buffer_early=*/false, 1609 /*use_resizable_runner=*/false, /*require_boxes=*/false, 1610 /*expect_success=*/true); 1611 JxlDecoderReset(dec); 1612 EXPECT_EQ(num_pixels * channels * 4, pixels2.size()); 1613 EXPECT_EQ(0u, 1614 jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 1615 ysize, format_orig, format)); 1616 } 1617 } 1618 1619 JxlDecoderDestroy(dec); 1620 } 1621 1622 TEST(DecodeTest, PixelTestWithICCProfileLossy) { 1623 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 1624 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1625 1626 size_t xsize = 123; 1627 size_t ysize = 77; 1628 size_t num_pixels = xsize * ysize; 1629 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 1630 JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1631 jxl::TestCodestreamParams params; 1632 params.add_icc_profile = true; 1633 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 1634 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 1635 uint32_t channels = 3; 1636 1637 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 1638 1639 std::vector<uint8_t> icc_data; 1640 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 1641 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 1642 /*use_callback=*/false, /*set_buffer_early=*/true, 1643 /*use_resizable_runner=*/false, /*require_boxes=*/false, 1644 /*expect_success=*/true, /*icc=*/&icc_data); 1645 JxlDecoderReset(dec); 1646 EXPECT_EQ(num_pixels * channels * 4, pixels2.size()); 1647 1648 // The input pixels use the profile matching GetIccTestProfile, since we set 1649 // add_icc_profile for CreateTestJXLCodestream to true. 1650 jxl::ColorEncoding color_encoding0; 1651 EXPECT_TRUE(color_encoding0.SetICC(GetIccTestProfile(), JxlGetDefaultCms())); 1652 jxl::Span<const uint8_t> span0(pixels.data(), pixels.size()); 1653 jxl::CodecInOut io0{memory_manager}; 1654 ASSERT_TRUE(io0.SetSize(xsize, ysize)); 1655 EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, 1656 /*bits_per_sample=*/16, format_orig, 1657 /*pool=*/nullptr, &io0.Main())); 1658 1659 jxl::ColorEncoding color_encoding1; 1660 jxl::IccBytes icc; 1661 jxl::Bytes(icc_data).AppendTo(icc); 1662 EXPECT_TRUE(color_encoding1.SetICC(std::move(icc), JxlGetDefaultCms())); 1663 jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size()); 1664 jxl::CodecInOut io1{memory_manager}; 1665 ASSERT_TRUE(io1.SetSize(xsize, ysize)); 1666 EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, 1667 /*bits_per_sample=*/32, format, 1668 /*pool=*/nullptr, &io1.Main())); 1669 1670 jxl::ButteraugliParams butteraugli_params; 1671 EXPECT_SLIGHTLY_BELOW( 1672 ButteraugliDistance(io0.frames, io1.frames, butteraugli_params, 1673 *JxlGetDefaultCms(), 1674 /*distmap=*/nullptr, nullptr), 1675 0.58f); 1676 1677 JxlDecoderDestroy(dec); 1678 } 1679 1680 std::string ColorDescription(JxlColorEncoding c) { 1681 jxl::ColorEncoding color_encoding; 1682 EXPECT_TRUE(color_encoding.FromExternal(c)); 1683 return Description(color_encoding); 1684 } 1685 1686 std::string GetOrigProfile(JxlDecoder* dec) { 1687 JxlColorEncoding c; 1688 JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_ORIGINAL; 1689 EXPECT_EQ(JXL_DEC_SUCCESS, 1690 JxlDecoderGetColorAsEncodedProfile(dec, target, &c)); 1691 return ColorDescription(c); 1692 } 1693 1694 std::string GetDataProfile(JxlDecoder* dec) { 1695 JxlColorEncoding c; 1696 JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA; 1697 EXPECT_EQ(JXL_DEC_SUCCESS, 1698 JxlDecoderGetColorAsEncodedProfile(dec, target, &c)); 1699 return ColorDescription(c); 1700 } 1701 1702 double ButteraugliDistance(size_t xsize, size_t ysize, 1703 const std::vector<uint8_t>& pixels_in, 1704 const jxl::ColorEncoding& color_in, 1705 float intensity_in, 1706 const std::vector<uint8_t>& pixels_out, 1707 const jxl::ColorEncoding& color_out, 1708 float intensity_out) { 1709 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 1710 jxl::CodecInOut in{memory_manager}; 1711 in.metadata.m.color_encoding = color_in; 1712 in.metadata.m.SetIntensityTarget(intensity_in); 1713 JxlPixelFormat format_in = {static_cast<uint32_t>(color_in.Channels()), 1714 JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1715 EXPECT_TRUE(jxl::ConvertFromExternal( 1716 jxl::Bytes(pixels_in.data(), pixels_in.size()), xsize, ysize, color_in, 1717 /*bits_per_sample=*/16, format_in, 1718 /*pool=*/nullptr, &in.Main())); 1719 jxl::CodecInOut out{memory_manager}; 1720 out.metadata.m.color_encoding = color_out; 1721 out.metadata.m.SetIntensityTarget(intensity_out); 1722 JxlPixelFormat format_out = {static_cast<uint32_t>(color_out.Channels()), 1723 JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1724 EXPECT_TRUE(jxl::ConvertFromExternal( 1725 jxl::Bytes(pixels_out.data(), pixels_out.size()), xsize, ysize, color_out, 1726 /*bits_per_sample=*/16, format_out, 1727 /*pool=*/nullptr, &out.Main())); 1728 return ButteraugliDistance(in.frames, out.frames, jxl::ButteraugliParams(), 1729 *JxlGetDefaultCms(), nullptr, nullptr); 1730 } 1731 1732 class DecodeAllEncodingsTest 1733 : public ::testing::TestWithParam<jxl::test::ColorEncodingDescriptor> {}; 1734 JXL_GTEST_INSTANTIATE_TEST_SUITE_P( 1735 DecodeAllEncodingsTestInstantiation, DecodeAllEncodingsTest, 1736 ::testing::ValuesIn(jxl::test::AllEncodings())); 1737 TEST_P(DecodeAllEncodingsTest, PreserveOriginalProfileTest) { 1738 size_t xsize = 123; 1739 size_t ysize = 77; 1740 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 1741 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1742 int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE; 1743 const auto& cdesc = GetParam(); 1744 jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(cdesc); 1745 if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return; 1746 std::string color_space_in = Description(c_in); 1747 float intensity_in = c_in.Tf().IsPQ() ? 10000 : 255; 1748 printf("Testing input color space %s\n", color_space_in.c_str()); 1749 jxl::TestCodestreamParams params; 1750 params.color_space = color_space_in; 1751 params.intensity_target = intensity_in; 1752 std::vector<uint8_t> data = jxl::CreateTestJXLCodestream( 1753 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 1754 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1755 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events)); 1756 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size())); 1757 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 1758 JxlBasicInfo info; 1759 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 1760 EXPECT_EQ(xsize, info.xsize); 1761 EXPECT_EQ(ysize, info.ysize); 1762 EXPECT_FALSE(info.uses_original_profile); 1763 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 1764 EXPECT_EQ(GetOrigProfile(dec), color_space_in); 1765 EXPECT_EQ(GetDataProfile(dec), color_space_in); 1766 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 1767 std::vector<uint8_t> out(pixels.size()); 1768 EXPECT_EQ(JXL_DEC_SUCCESS, 1769 JxlDecoderSetImageOutBuffer(dec, &format, out.data(), out.size())); 1770 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 1771 double dist = ButteraugliDistance(xsize, ysize, pixels, c_in, intensity_in, 1772 out, c_in, intensity_in); 1773 EXPECT_LT(dist, 1.29); 1774 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 1775 JxlDecoderDestroy(dec); 1776 } 1777 1778 namespace { 1779 void SetPreferredColorProfileTest( 1780 const jxl::test::ColorEncodingDescriptor& from, bool icc_dst, 1781 bool use_cms) { 1782 size_t xsize = 123; 1783 size_t ysize = 77; 1784 int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE; 1785 jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(from); 1786 if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return; 1787 if (c_in.GetWhitePointType() != jxl::WhitePoint::kD65) return; 1788 uint32_t num_channels = c_in.Channels(); 1789 std::vector<uint8_t> pixels = 1790 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 1791 1792 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1793 std::string color_space_in = Description(c_in); 1794 float intensity_in = c_in.Tf().IsPQ() ? 10000 : 255; 1795 jxl::TestCodestreamParams params; 1796 params.color_space = color_space_in; 1797 params.intensity_target = intensity_in; 1798 std::vector<uint8_t> data = 1799 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 1800 xsize, ysize, num_channels, params); 1801 auto all_encodings = jxl::test::AllEncodings(); 1802 // TODO(firsching): understand why XYB does not work together with icc_dst. 1803 if (!icc_dst) { 1804 all_encodings.push_back( 1805 {jxl::ColorSpace::kXYB, jxl::WhitePoint::kD65, jxl::Primaries::kCustom, 1806 jxl::TransferFunction::kUnknown, jxl::RenderingIntent::kPerceptual}); 1807 } 1808 for (const auto& c1 : all_encodings) { 1809 jxl::ColorEncoding c_out = jxl::test::ColorEncodingFromDescriptor(c1); 1810 float intensity_out = intensity_in; 1811 if (c_out.GetColorSpace() != jxl::ColorSpace::kXYB) { 1812 if (c_out.GetRenderingIntent() != jxl::RenderingIntent::kRelative) { 1813 continue; 1814 } 1815 if ((c_in.GetPrimariesType() == jxl::Primaries::k2100 && 1816 c_out.GetPrimariesType() != jxl::Primaries::k2100) || 1817 (c_in.GetPrimariesType() == jxl::Primaries::kP3 && 1818 c_out.GetPrimariesType() == jxl::Primaries::kSRGB)) { 1819 // Converting to a narrower gamut does not work without gamut mapping. 1820 continue; 1821 } 1822 } 1823 if (c_out.Tf().IsHLG() && intensity_out > 300) { 1824 // The Linear->HLG OOTF function at this intensity level can push 1825 // saturated colors out of gamut, so we would need gamut mapping in 1826 // this case too. 1827 continue; 1828 } 1829 std::string color_space_out = Description(c_out); 1830 if (color_space_in == color_space_out) continue; 1831 printf("Testing input color space %s with output color space %s\n", 1832 color_space_in.c_str(), color_space_out.c_str()); 1833 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1834 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events)); 1835 EXPECT_EQ(JXL_DEC_SUCCESS, 1836 JxlDecoderSetInput(dec, data.data(), data.size())); 1837 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 1838 JxlBasicInfo info; 1839 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 1840 EXPECT_EQ(xsize, info.xsize); 1841 EXPECT_EQ(ysize, info.ysize); 1842 EXPECT_FALSE(info.uses_original_profile); 1843 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 1844 EXPECT_EQ(GetOrigProfile(dec), color_space_in); 1845 JxlColorEncoding encoding_out; 1846 EXPECT_TRUE(jxl::ParseDescription(color_space_out, &encoding_out)); 1847 if (c_out.GetColorSpace() == jxl::ColorSpace::kXYB && 1848 (c_in.GetPrimariesType() != jxl::Primaries::kSRGB || 1849 c_in.Tf().IsPQ())) { 1850 EXPECT_EQ(JXL_DEC_ERROR, 1851 JxlDecoderSetPreferredColorProfile(dec, &encoding_out)); 1852 JxlDecoderDestroy(dec); 1853 continue; 1854 } 1855 if (use_cms) { 1856 JxlDecoderSetCms(dec, *JxlGetDefaultCms()); 1857 } 1858 if (icc_dst) { 1859 jxl::ColorEncoding internal_encoding_out; 1860 EXPECT_TRUE(internal_encoding_out.FromExternal(encoding_out)); 1861 EXPECT_TRUE(internal_encoding_out.CreateICC()); 1862 std::vector<uint8_t> rewritten_icc = internal_encoding_out.ICC(); 1863 1864 EXPECT_EQ(use_cms ? JXL_DEC_SUCCESS : JXL_DEC_ERROR, 1865 JxlDecoderSetOutputColorProfile( 1866 dec, nullptr, rewritten_icc.data(), rewritten_icc.size())); 1867 if (!use_cms) { 1868 // continue if we don't have a cms here 1869 JxlDecoderDestroy(dec); 1870 continue; 1871 } 1872 } else { 1873 EXPECT_EQ(JXL_DEC_SUCCESS, 1874 JxlDecoderSetPreferredColorProfile(dec, &encoding_out)); 1875 } 1876 EXPECT_EQ(GetOrigProfile(dec), color_space_in); 1877 if (icc_dst) { 1878 } else { 1879 EXPECT_EQ(GetDataProfile(dec), color_space_out); 1880 } 1881 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 1882 size_t buffer_size; 1883 JxlPixelFormat out_format = format; 1884 out_format.num_channels = c_out.Channels(); 1885 EXPECT_EQ(JXL_DEC_SUCCESS, 1886 JxlDecoderImageOutBufferSize(dec, &out_format, &buffer_size)); 1887 std::vector<uint8_t> out(buffer_size); 1888 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 1889 dec, &out_format, out.data(), out.size())); 1890 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 1891 double dist = ButteraugliDistance(xsize, ysize, pixels, c_in, intensity_in, 1892 out, c_out, intensity_out); 1893 1894 if (c_in.GetWhitePointType() == c_out.GetWhitePointType()) { 1895 EXPECT_LT(dist, 1.29); 1896 } else { 1897 EXPECT_LT(dist, 4.0); 1898 } 1899 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 1900 JxlDecoderDestroy(dec); 1901 } 1902 } 1903 } // namespace 1904 1905 TEST(DecodeTest, SetPreferredColorProfileTestFromGray) { 1906 jxl::test::ColorEncodingDescriptor gray = { 1907 jxl::ColorSpace::kGray, jxl::WhitePoint::kD65, jxl::Primaries::kSRGB, 1908 jxl::TransferFunction::kSRGB, jxl::RenderingIntent::kRelative}; 1909 SetPreferredColorProfileTest(gray, true, true); 1910 SetPreferredColorProfileTest(gray, false, true); 1911 SetPreferredColorProfileTest(gray, true, false); 1912 SetPreferredColorProfileTest(gray, false, false); 1913 } 1914 1915 static std::string DecodeAllEncodingsVariantsTestName( 1916 const ::testing::TestParamInfo< 1917 std::tuple<jxl::test::ColorEncodingDescriptor, bool, bool>>& info) { 1918 const auto& encoding = std::get<0>(info.param); 1919 bool icc_dst = std::get<1>(info.param); 1920 bool use_cms = std::get<2>(info.param); 1921 1922 std::string encoding_name = 1923 Description(ColorEncodingFromDescriptor(encoding)); 1924 1925 return "From_" + encoding_name + 1926 (icc_dst ? "_with_icc_dst" : "_without_icc_dst") + 1927 (use_cms ? "_with_cms" : "_without_cms"); 1928 } 1929 1930 class DecodeAllEncodingsVariantsTest 1931 : public ::testing::TestWithParam< 1932 std::tuple<jxl::test::ColorEncodingDescriptor, bool, bool>> {}; 1933 JXL_GTEST_INSTANTIATE_TEST_SUITE_P( 1934 DecodeAllEncodingsVariantsTestInstantiation, DecodeAllEncodingsVariantsTest, 1935 ::testing::Combine(::testing::ValuesIn(jxl::test::AllEncodings()), 1936 ::testing::Bool(), ::testing::Bool()), 1937 DecodeAllEncodingsVariantsTestName); 1938 TEST_P(DecodeAllEncodingsVariantsTest, SetPreferredColorProfileTest) { 1939 const auto& from = std::get<0>(GetParam()); 1940 bool icc_dst = std::get<1>(GetParam()); 1941 bool use_cms = std::get<2>(GetParam()); 1942 SetPreferredColorProfileTest(from, icc_dst, use_cms); 1943 } 1944 1945 void DecodeImageWithColorEncoding(const std::vector<uint8_t>& compressed, 1946 jxl::ColorEncoding& color_encoding, 1947 bool with_cms, std::vector<uint8_t>& out, 1948 JxlBasicInfo& info) { 1949 JxlDecoder* dec = JxlDecoderCreate(nullptr); 1950 int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE; 1951 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events)); 1952 EXPECT_EQ(JXL_DEC_SUCCESS, 1953 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 1954 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 1955 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 1956 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 1957 // TODO(eustas): why unused? 1958 std::string color_space_in = GetOrigProfile(dec); 1959 if (with_cms) { 1960 JxlDecoderSetCms(dec, *JxlGetDefaultCms()); 1961 EXPECT_TRUE(color_encoding.CreateICC()); 1962 std::vector<uint8_t> rewritten_icc = color_encoding.ICC(); 1963 EXPECT_EQ(JXL_DEC_SUCCESS, 1964 JxlDecoderSetOutputColorProfile( 1965 dec, nullptr, rewritten_icc.data(), rewritten_icc.size())); 1966 } else { 1967 JxlColorEncoding external_color_encoding = color_encoding.ToExternal(); 1968 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetOutputColorProfile( 1969 dec, &external_color_encoding, nullptr, 0)); 1970 } 1971 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 1972 1973 size_t buffer_size; 1974 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 1975 1976 JxlPixelFormat out_format = format; 1977 out_format.num_channels = color_encoding.Channels(); 1978 EXPECT_EQ(JXL_DEC_SUCCESS, 1979 JxlDecoderImageOutBufferSize(dec, &out_format, &buffer_size)); 1980 out.resize(buffer_size); 1981 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 1982 dec, &out_format, out.data(), out.size())); 1983 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 1984 JxlDecoderDestroy(dec); 1985 } 1986 1987 class DecodeAllEncodingsWithCMSTest 1988 : public ::testing::TestWithParam<jxl::test::ColorEncodingDescriptor> {}; 1989 1990 JXL_GTEST_INSTANTIATE_TEST_SUITE_P( 1991 AllEncodings, DecodeAllEncodingsWithCMSTest, 1992 testing::ValuesIn(jxl::test::AllEncodings())); 1993 1994 TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) { 1995 auto all_encodings = jxl::test::AllEncodings(); 1996 uint32_t num_channels = 3; 1997 size_t xsize = 177; 1998 size_t ysize = 123; 1999 std::vector<uint8_t> pixels = 2000 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 2001 jxl::TestCodestreamParams params; 2002 std::vector<uint8_t> data = 2003 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 2004 xsize, ysize, num_channels, params); 2005 2006 jxl::ColorEncoding color_encoding = 2007 jxl::test::ColorEncodingFromDescriptor(GetParam()); 2008 fprintf(stderr, "color_description: %s\n", 2009 Description(color_encoding).c_str()); 2010 2011 std::vector<uint8_t> out_with_cms; 2012 JxlBasicInfo info_with_cms; 2013 DecodeImageWithColorEncoding(data, color_encoding, true, out_with_cms, 2014 info_with_cms); 2015 2016 std::vector<uint8_t> out_without_cms; 2017 JxlBasicInfo info_without_cms; 2018 DecodeImageWithColorEncoding(data, color_encoding, false, out_without_cms, 2019 info_without_cms); 2020 2021 EXPECT_EQ(info_with_cms.xsize, info_without_cms.xsize); 2022 EXPECT_EQ(info_with_cms.ysize, info_without_cms.ysize); 2023 EXPECT_EQ(out_with_cms.size(), out_without_cms.size()); 2024 double dist = ButteraugliDistance(xsize, ysize, out_with_cms, color_encoding, 2025 255, out_without_cms, color_encoding, 255); 2026 2027 EXPECT_LT(dist, .1); 2028 } 2029 2030 // Tests the case of lossy sRGB image without alpha channel, decoded to RGB8 2031 // and to RGBA8 2032 TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { 2033 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 2034 for (unsigned channels = 3; channels <= 4; channels++) { 2035 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2036 2037 size_t xsize = 123; 2038 size_t ysize = 77; 2039 size_t num_pixels = xsize * ysize; 2040 std::vector<uint8_t> pixels = 2041 jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2042 JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2043 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2044 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, 2045 jxl::TestCodestreamParams()); 2046 2047 JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 2048 2049 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2050 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 2051 /*use_callback=*/true, /*set_buffer_early=*/false, 2052 /*use_resizable_runner=*/false, /*require_boxes=*/false, 2053 /*expect_success*/ true); 2054 JxlDecoderReset(dec); 2055 EXPECT_EQ(num_pixels * channels, pixels2.size()); 2056 2057 jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false); 2058 jxl::Span<const uint8_t> span0(pixels.data(), pixels.size()); 2059 jxl::CodecInOut io0{memory_manager}; 2060 ASSERT_TRUE(io0.SetSize(xsize, ysize)); 2061 EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, 2062 /*bits_per_sample=*/16, format_orig, 2063 /*pool=*/nullptr, &io0.Main())); 2064 2065 jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false); 2066 jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size()); 2067 jxl::CodecInOut io1{memory_manager}; 2068 EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, 2069 /*bits_per_sample=*/8, format, 2070 /*pool=*/nullptr, &io1.Main())); 2071 2072 jxl::ButteraugliParams butteraugli_params; 2073 EXPECT_SLIGHTLY_BELOW( 2074 ButteraugliDistance(io0.frames, io1.frames, butteraugli_params, 2075 *JxlGetDefaultCms(), 2076 /*distmap=*/nullptr, nullptr), 2077 0.6f); 2078 2079 JxlDecoderDestroy(dec); 2080 } 2081 } 2082 2083 // Opaque image with noise enabled, decoded to RGB8 and RGBA8. 2084 TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { 2085 for (unsigned channels = 3; channels <= 4; channels++) { 2086 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2087 2088 size_t xsize = 512; 2089 size_t ysize = 300; 2090 size_t num_pixels = xsize * ysize; 2091 std::vector<uint8_t> pixels = 2092 jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2093 JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2094 jxl::TestCodestreamParams params; 2095 params.cparams.noise = jxl::Override::kOn; 2096 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2097 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 2098 2099 JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 2100 2101 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2102 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 2103 /*use_callback=*/false, /*set_buffer_early=*/true, 2104 /*use_resizable_runner=*/false, /*require_boxes=*/false, 2105 /*expect_success=*/true); 2106 JxlDecoderReset(dec); 2107 EXPECT_EQ(num_pixels * channels, pixels2.size()); 2108 2109 jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false); 2110 jxl::Span<const uint8_t> span0(pixels.data(), pixels.size()); 2111 jxl::CodecInOut io0{jxl::test::MemoryManager()}; 2112 ASSERT_TRUE(io0.SetSize(xsize, ysize)); 2113 EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0, 2114 /*bits_per_sample=*/16, format_orig, 2115 /*pool=*/nullptr, &io0.Main())); 2116 2117 jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false); 2118 jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size()); 2119 jxl::CodecInOut io1{jxl::test::MemoryManager()}; 2120 EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1, 2121 /*bits_per_sample=*/8, format, 2122 /*pool=*/nullptr, &io1.Main())); 2123 2124 jxl::ButteraugliParams butteraugli_params; 2125 EXPECT_SLIGHTLY_BELOW( 2126 ButteraugliDistance(io0.frames, io1.frames, butteraugli_params, 2127 *JxlGetDefaultCms(), 2128 /*distmap=*/nullptr, nullptr), 2129 1.4f); 2130 2131 JxlDecoderDestroy(dec); 2132 } 2133 } 2134 2135 TEST(DecodeTest, ProcessEmptyInputWithBoxes) { 2136 size_t xsize = 123; 2137 size_t ysize = 77; 2138 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2139 jxl::CompressParams cparams; 2140 uint32_t channels = 3; 2141 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 2142 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2143 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2144 jxl::TestCodestreamParams params; 2145 params.box_format = static_cast<CodeStreamBoxFormat>(i); 2146 printf("Testing empty input with box format %d\n", 2147 static_cast<int>(params.box_format)); 2148 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2149 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 2150 const int events = 2151 JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING; 2152 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events)); 2153 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 2154 EXPECT_EQ(JXL_DEC_SUCCESS, 2155 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 2156 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2157 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 2158 size_t buffer_size; 2159 EXPECT_EQ(JXL_DEC_SUCCESS, 2160 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 2161 JxlBasicInfo info; 2162 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2163 const size_t remaining = JxlDecoderReleaseInput(dec); 2164 EXPECT_LE(remaining, compressed.size()); 2165 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 2166 JxlDecoderDestroy(dec); 2167 } 2168 } 2169 2170 TEST(DecodeTest, ExtraBytesAfterCompressedStream) { 2171 size_t xsize = 123; 2172 size_t ysize = 77; 2173 size_t num_pixels = xsize * ysize; 2174 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2175 jxl::CompressParams cparams; 2176 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2177 CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i); 2178 if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue; 2179 printf("Testing with box format %d\n", static_cast<int>(box_format)); 2180 size_t last_unknown_box_size = 0; 2181 if (box_format == kCSBF_Single_Other) { 2182 last_unknown_box_size = unk1_box_size + 8; 2183 } else if (box_format == kCSBF_Multi_Other_Terminated) { 2184 last_unknown_box_size = unk3_box_size + 8; 2185 } else if (box_format == kCSBF_Multi_Last_Empty_Other) { 2186 // If boxes are not required, the decoder won't consume the last empty 2187 // jxlp box. 2188 last_unknown_box_size = 12 + unk3_box_size + 8; 2189 } 2190 jxl::TestCodestreamParams params; 2191 params.box_format = box_format; 2192 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2193 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 2194 // Add some more bytes after compressed data. 2195 compressed.push_back(0); 2196 compressed.push_back(1); 2197 compressed.push_back(2); 2198 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2199 uint32_t channels = 3; 2200 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 2201 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2202 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 2203 /*use_callback=*/false, /*set_buffer_early=*/true, 2204 /*use_resizable_runner=*/false, /*require_boxes=*/false, 2205 /*expect_success=*/true); 2206 size_t unconsumed_bytes = JxlDecoderReleaseInput(dec); 2207 EXPECT_EQ(last_unknown_box_size + 3, unconsumed_bytes); 2208 EXPECT_EQ(num_pixels * channels * 4, pixels2.size()); 2209 JxlDecoderDestroy(dec); 2210 } 2211 } 2212 2213 TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) { 2214 size_t xsize = 123; 2215 size_t ysize = 77; 2216 size_t num_pixels = xsize * ysize; 2217 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2218 jxl::CompressParams cparams; 2219 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2220 CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i); 2221 if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue; 2222 printf("Testing with box format %d\n", static_cast<int>(box_format)); 2223 bool expect_success = (box_format == kCSBF_None || 2224 box_format == kCSBF_Single_Zero_Terminated || 2225 box_format == kCSBF_Multi_Zero_Terminated); 2226 jxl::TestCodestreamParams params; 2227 params.box_format = box_format; 2228 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2229 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 2230 // Add some more bytes after compressed data. 2231 compressed.push_back(0); 2232 compressed.push_back(1); 2233 compressed.push_back(2); 2234 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2235 uint32_t channels = 3; 2236 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 2237 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2238 dec, jxl::Bytes(compressed.data(), compressed.size()), format, 2239 /*use_callback=*/false, /*set_buffer_early=*/true, 2240 /*use_resizable_runner=*/false, /*require_boxes=*/true, expect_success); 2241 size_t unconsumed_bytes = JxlDecoderReleaseInput(dec); 2242 EXPECT_EQ(3, unconsumed_bytes); 2243 EXPECT_EQ(num_pixels * channels * 4, pixels2.size()); 2244 JxlDecoderDestroy(dec); 2245 } 2246 } 2247 2248 TEST(DecodeTest, ConcatenatedCompressedStreams) { 2249 size_t xsize = 123; 2250 size_t ysize = 77; 2251 size_t num_pixels = xsize * ysize; 2252 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2253 jxl::CompressParams cparams; 2254 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2255 CodeStreamBoxFormat first_box_format = static_cast<CodeStreamBoxFormat>(i); 2256 if (first_box_format == kCSBF_Multi_Other_Zero_Terminated) continue; 2257 jxl::TestCodestreamParams params1; 2258 params1.box_format = first_box_format; 2259 std::vector<uint8_t> compressed1 = jxl::CreateTestJXLCodestream( 2260 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params1); 2261 for (int j = 0; j < kCSBF_NUM_ENTRIES; ++j) { 2262 CodeStreamBoxFormat second_box_format = 2263 static_cast<CodeStreamBoxFormat>(j); 2264 if (second_box_format == kCSBF_Multi_Other_Zero_Terminated) continue; 2265 printf("Testing with box format pair %d, %d\n", 2266 static_cast<int>(first_box_format), 2267 static_cast<int>(second_box_format)); 2268 jxl::TestCodestreamParams params2; 2269 params2.box_format = second_box_format; 2270 std::vector<uint8_t> compressed2 = jxl::CreateTestJXLCodestream( 2271 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params2); 2272 std::vector<uint8_t> concat; 2273 jxl::Bytes(compressed1).AppendTo(concat); 2274 jxl::Bytes(compressed2).AppendTo(concat); 2275 uint32_t channels = 3; 2276 JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; 2277 size_t remaining = concat.size(); 2278 for (int part = 0; part < 2; ++part) { 2279 printf(" Decoding part %d\n", part + 1); 2280 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2281 size_t pos = concat.size() - remaining; 2282 bool expect_success = 2283 (part == 0 || second_box_format == kCSBF_None || 2284 second_box_format == kCSBF_Single_Zero_Terminated || 2285 second_box_format == kCSBF_Multi_Zero_Terminated); 2286 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2287 dec, jxl::Bytes(concat.data() + pos, remaining), format, 2288 /*use_callback=*/false, /*set_buffer_early=*/true, 2289 /*use_resizable_runner=*/false, /*require_boxes=*/true, 2290 expect_success); 2291 EXPECT_EQ(num_pixels * channels * 4, pixels2.size()); 2292 remaining = JxlDecoderReleaseInput(dec); 2293 JxlDecoderDestroy(dec); 2294 } 2295 EXPECT_EQ(0, remaining); 2296 } 2297 } 2298 } 2299 2300 void TestPartialStream(bool reconstructible_jpeg) { 2301 size_t xsize = 123; 2302 size_t ysize = 77; 2303 uint32_t channels = 4; 2304 if (reconstructible_jpeg) { 2305 channels = 3; 2306 } 2307 std::vector<uint8_t> pixels = 2308 jxl::test::GetSomeTestImage(xsize, ysize, channels, 0); 2309 JxlPixelFormat format_orig = {channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2310 jxl::TestCodestreamParams params; 2311 if (reconstructible_jpeg) { 2312 params.cparams.color_transform = jxl::ColorTransform::kNone; 2313 } else { 2314 // Lossless to verify pixels exactly after roundtrip. 2315 params.cparams.SetLossless(); 2316 } 2317 2318 std::vector<uint8_t> pixels2; 2319 pixels2.resize(pixels.size()); 2320 2321 std::vector<uint8_t> jpeg_output(64); 2322 size_t used_jpeg_output = 0; 2323 2324 std::vector<std::vector<uint8_t>> codestreams(kCSBF_NUM_ENTRIES); 2325 std::vector<std::vector<uint8_t>> jpeg_codestreams(kCSBF_NUM_ENTRIES); 2326 for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2327 params.box_format = static_cast<CodeStreamBoxFormat>(i); 2328 if (reconstructible_jpeg) { 2329 params.jpeg_codestream = &jpeg_codestreams[i]; 2330 } 2331 codestreams[i] = 2332 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 2333 xsize, ysize, channels, params); 2334 } 2335 2336 // Test multiple step sizes, to test different combinations of the streaming 2337 // box parsing. 2338 std::vector<size_t> increments = {1, 3, 17, 23, 120, 700, 1050}; 2339 2340 for (size_t increment : increments) { 2341 for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 2342 if (reconstructible_jpeg && static_cast<CodeStreamBoxFormat>(i) == 2343 CodeStreamBoxFormat::kCSBF_None) { 2344 continue; 2345 } 2346 const std::vector<uint8_t>& data = codestreams[i]; 2347 const uint8_t* next_in = data.data(); 2348 size_t avail_in = 0; 2349 2350 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2351 2352 EXPECT_EQ(JXL_DEC_SUCCESS, 2353 JxlDecoderSubscribeEvents( 2354 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | 2355 JXL_DEC_JPEG_RECONSTRUCTION)); 2356 2357 bool seen_basic_info = false; 2358 bool seen_full_image = false; 2359 bool seen_jpeg_recon = false; 2360 2361 size_t total_size = 0; 2362 2363 for (;;) { 2364 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 2365 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 2366 size_t remaining = JxlDecoderReleaseInput(dec); 2367 EXPECT_LE(remaining, avail_in); 2368 next_in += avail_in - remaining; 2369 avail_in = remaining; 2370 if (status == JXL_DEC_NEED_MORE_INPUT) { 2371 if (total_size >= data.size()) { 2372 // End of test data reached, it should have successfully decoded the 2373 // image now. 2374 FAIL(); 2375 break; 2376 } 2377 2378 // End of the file reached, should be the final test. 2379 if (total_size + increment > data.size()) { 2380 increment = data.size() - total_size; 2381 } 2382 total_size += increment; 2383 avail_in += increment; 2384 } else if (status == JXL_DEC_BASIC_INFO) { 2385 // This event should happen exactly once 2386 EXPECT_FALSE(seen_basic_info); 2387 if (seen_basic_info) break; 2388 seen_basic_info = true; 2389 JxlBasicInfo info; 2390 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2391 EXPECT_EQ(info.xsize, xsize); 2392 EXPECT_EQ(info.ysize, ysize); 2393 } else if (status == JXL_DEC_JPEG_RECONSTRUCTION) { 2394 EXPECT_FALSE(seen_basic_info); 2395 EXPECT_FALSE(seen_full_image); 2396 EXPECT_EQ(JXL_DEC_SUCCESS, 2397 JxlDecoderSetJPEGBuffer(dec, jpeg_output.data(), 2398 jpeg_output.size())); 2399 seen_jpeg_recon = true; 2400 } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { 2401 EXPECT_TRUE(seen_jpeg_recon); 2402 used_jpeg_output = 2403 jpeg_output.size() - JxlDecoderReleaseJPEGBuffer(dec); 2404 jpeg_output.resize(jpeg_output.size() * 2); 2405 EXPECT_EQ(JXL_DEC_SUCCESS, 2406 JxlDecoderSetJPEGBuffer( 2407 dec, jpeg_output.data() + used_jpeg_output, 2408 jpeg_output.size() - used_jpeg_output)); 2409 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 2410 EXPECT_EQ(JXL_DEC_SUCCESS, 2411 JxlDecoderSetImageOutBuffer( 2412 dec, &format_orig, pixels2.data(), pixels2.size())); 2413 } else if (status == JXL_DEC_FULL_IMAGE) { 2414 // This event should happen exactly once 2415 EXPECT_FALSE(seen_full_image); 2416 if (seen_full_image) break; 2417 // This event should happen after basic info 2418 EXPECT_TRUE(seen_basic_info); 2419 seen_full_image = true; 2420 if (reconstructible_jpeg) { 2421 used_jpeg_output = 2422 jpeg_output.size() - JxlDecoderReleaseJPEGBuffer(dec); 2423 EXPECT_EQ(used_jpeg_output, jpeg_codestreams[i].size()); 2424 EXPECT_EQ(0, memcmp(jpeg_output.data(), jpeg_codestreams[i].data(), 2425 used_jpeg_output)); 2426 } else { 2427 EXPECT_EQ(pixels, pixels2); 2428 } 2429 } else if (status == JXL_DEC_SUCCESS) { 2430 EXPECT_TRUE(seen_full_image); 2431 break; 2432 } else { 2433 // We do not expect any other events or errors 2434 FAIL(); 2435 break; 2436 } 2437 } 2438 2439 // Ensure the decoder emitted the basic info and full image events 2440 EXPECT_TRUE(seen_basic_info); 2441 EXPECT_TRUE(seen_full_image); 2442 2443 JxlDecoderDestroy(dec); 2444 } 2445 } 2446 } 2447 2448 // Tests the return status when trying to decode pixels on incomplete file: it 2449 // should return JXL_DEC_NEED_MORE_INPUT, not error. 2450 TEST(DecodeTest, PixelPartialTest) { TestPartialStream(false); } 2451 2452 // Tests the return status when trying to decode JPEG bytes on incomplete file. 2453 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGPartialTest) { 2454 TEST_LIBJPEG_SUPPORT(); 2455 TestPartialStream(true); 2456 } 2457 2458 // The DC event still exists, but is no longer implemented, it is deprecated. 2459 TEST(DecodeTest, DCNotGettableTest) { 2460 // 1x1 pixel JXL image 2461 std::string compressed( 2462 "\377\n\0\20\260\23\0H\200(" 2463 "\0\334\0U\17\0\0\250P\31e\334\340\345\\\317\227\37:," 2464 "\246m\\gh\253m\vK\22E\306\261I\252C&pH\22\353 " 2465 "\363\6\22\bp\0\200\237\34\231W2d\255$\1", 2466 68); 2467 2468 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2469 2470 EXPECT_EQ(JXL_DEC_SUCCESS, 2471 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); 2472 EXPECT_EQ(JXL_DEC_SUCCESS, 2473 JxlDecoderSetInput( 2474 dec, reinterpret_cast<const uint8_t*>(compressed.data()), 2475 compressed.size())); 2476 2477 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2478 2479 // Since the image is only 1x1 pixel, there is only 1 group, the decoder is 2480 // unable to get DC size from this, and will not return the DC at all. Since 2481 // no full image is requested either, it is expected to return success. 2482 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 2483 2484 JxlDecoderDestroy(dec); 2485 } 2486 2487 TEST(DecodeTest, PreviewTest) { 2488 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 2489 size_t xsize = 77; 2490 size_t ysize = 120; 2491 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2492 JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2493 for (jxl::PreviewMode mode : {jxl::kSmallPreview, jxl::kBigPreview}) { 2494 jxl::TestCodestreamParams params; 2495 params.preview_mode = mode; 2496 2497 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2498 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); 2499 2500 JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 2501 2502 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2503 const uint8_t* next_in = compressed.data(); 2504 size_t avail_in = compressed.size(); 2505 2506 EXPECT_EQ(JXL_DEC_SUCCESS, 2507 JxlDecoderSubscribeEvents( 2508 dec, JXL_DEC_BASIC_INFO | JXL_DEC_PREVIEW_IMAGE)); 2509 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 2510 2511 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2512 JxlBasicInfo info; 2513 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2514 size_t buffer_size; 2515 EXPECT_EQ(JXL_DEC_SUCCESS, 2516 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); 2517 2518 jxl::ColorEncoding c_srgb = jxl::ColorEncoding::SRGB(false); 2519 jxl::CodecInOut io0{memory_manager}; 2520 EXPECT_TRUE(jxl::ConvertFromExternal( 2521 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, c_srgb, 2522 /*bits_per_sample=*/16, format_orig, /*pool=*/nullptr, &io0.Main())); 2523 GeneratePreview(params.preview_mode, &io0.Main()); 2524 2525 size_t xsize_preview = io0.Main().xsize(); 2526 size_t ysize_preview = io0.Main().ysize(); 2527 EXPECT_EQ(xsize_preview, info.preview.xsize); 2528 EXPECT_EQ(ysize_preview, info.preview.ysize); 2529 EXPECT_EQ(xsize_preview * ysize_preview * 3, buffer_size); 2530 2531 EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec)); 2532 2533 std::vector<uint8_t> preview(buffer_size); 2534 EXPECT_EQ(JXL_DEC_SUCCESS, 2535 JxlDecoderSetPreviewOutBuffer(dec, &format, preview.data(), 2536 preview.size())); 2537 2538 EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec)); 2539 2540 jxl::CodecInOut io1{memory_manager}; 2541 EXPECT_TRUE( 2542 jxl::ConvertFromExternal(jxl::Bytes(preview.data(), preview.size()), 2543 xsize_preview, ysize_preview, c_srgb, 2544 /*bits_per_sample=*/8, format, 2545 /*pool=*/nullptr, &io1.Main())); 2546 2547 jxl::ButteraugliParams butteraugli_params; 2548 // TODO(lode): this ButteraugliDistance silently returns 0 (dangerous for 2549 // tests) if xsize or ysize is < 8, no matter how different the images, a 2550 // tiny size that could happen for a preview. ButteraugliDiffmap does 2551 // support smaller than 8x8, but jxl's ButteraugliDistance does not. Perhaps 2552 // move butteraugli's <8x8 handling from ButteraugliDiffmap to 2553 // ButteraugliComparator::Diffmap in butteraugli.cc. 2554 EXPECT_LE(ButteraugliDistance(io0.frames, io1.frames, butteraugli_params, 2555 *JxlGetDefaultCms(), 2556 /*distmap=*/nullptr, nullptr), 2557 mode == jxl::kSmallPreview ? 0.7f : 1.2f); 2558 2559 JxlDecoderDestroy(dec); 2560 } 2561 } 2562 2563 TEST(DecodeTest, AlignTest) { 2564 size_t xsize = 123; 2565 size_t ysize = 77; 2566 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 2567 JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2568 2569 jxl::TestCodestreamParams params; 2570 // Lossless to verify pixels exactly after roundtrip. 2571 params.cparams.SetLossless(); 2572 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 2573 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2574 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 2575 2576 size_t align = 17; 2577 JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align}; 2578 // On purpose not using jxl::RoundUpTo to test it independently. 2579 size_t expected_line_size_last = 1 * 3 * xsize; 2580 size_t expected_line_size = 2581 ((expected_line_size_last + align - 1) / align) * align; 2582 size_t expected_pixels_size = 2583 expected_line_size * (ysize - 1) + expected_line_size_last; 2584 2585 for (bool use_callback : {false, true}) { 2586 std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( 2587 jxl::Bytes(compressed.data(), compressed.size()), format, use_callback, 2588 /*set_buffer_early=*/false, 2589 /*use_resizable_runner=*/false, /*require_boxes=*/false, 2590 /*expect_success=*/true); 2591 EXPECT_EQ(expected_pixels_size, pixels2.size()); 2592 EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 2593 ysize, format_orig, format)); 2594 } 2595 } 2596 2597 TEST(DecodeTest, AnimationTest) { 2598 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 2599 size_t xsize = 123; 2600 size_t ysize = 77; 2601 static const size_t num_frames = 2; 2602 std::vector<uint8_t> frames[2]; 2603 frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2604 frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1); 2605 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2606 2607 jxl::CodecInOut io{memory_manager}; 2608 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2609 io.metadata.m.SetUintSamples(16); 2610 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 2611 io.metadata.m.have_animation = true; 2612 io.frames.clear(); 2613 io.frames.reserve(num_frames); 2614 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2615 2616 std::vector<uint32_t> frame_durations(num_frames); 2617 for (size_t i = 0; i < num_frames; ++i) { 2618 frame_durations[i] = 5 + i; 2619 } 2620 2621 for (size_t i = 0; i < num_frames; ++i) { 2622 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 2623 2624 EXPECT_TRUE(ConvertFromExternal( 2625 jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, 2626 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 2627 /*bits_per_sample=*/16, format, 2628 /*pool=*/nullptr, &bundle)); 2629 bundle.duration = frame_durations[i]; 2630 io.frames.push_back(std::move(bundle)); 2631 } 2632 2633 jxl::CompressParams cparams; 2634 cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip. 2635 cparams.speed_tier = jxl::SpeedTier::kThunder; 2636 std::vector<uint8_t> compressed; 2637 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 2638 2639 // Decode and test the animation frames 2640 2641 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2642 const uint8_t* next_in = compressed.data(); 2643 size_t avail_in = compressed.size(); 2644 2645 void* runner = JxlThreadParallelRunnerCreate( 2646 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 2647 EXPECT_EQ(JXL_DEC_SUCCESS, 2648 JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); 2649 2650 EXPECT_EQ(JXL_DEC_SUCCESS, 2651 JxlDecoderSubscribeEvents( 2652 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 2653 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 2654 2655 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2656 size_t buffer_size; 2657 EXPECT_EQ(JXL_DEC_SUCCESS, 2658 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 2659 JxlBasicInfo info; 2660 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2661 2662 for (size_t i = 0; i < num_frames; ++i) { 2663 std::vector<uint8_t> pixels(buffer_size); 2664 2665 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 2666 2667 JxlFrameHeader frame_header; 2668 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 2669 EXPECT_EQ(frame_durations[i], frame_header.duration); 2670 EXPECT_EQ(0u, frame_header.name_length); 2671 // For now, test with empty name, there's currently no easy way to encode 2672 // a jxl file with a frame name because ImageBundle doesn't have a 2673 // jxl::FrameHeader to set the name in. We can test the null termination 2674 // character though. 2675 char name; 2676 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameName(dec, &name, 1)); 2677 EXPECT_EQ(0, name); 2678 2679 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 2680 2681 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 2682 2683 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 2684 dec, &format, pixels.data(), pixels.size())); 2685 2686 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 2687 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 2688 xsize, ysize, format, format)); 2689 } 2690 2691 // After all frames were decoded, JxlDecoderProcessInput should return 2692 // success to indicate all is done. 2693 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 2694 2695 JxlThreadParallelRunnerDestroy(runner); 2696 JxlDecoderDestroy(dec); 2697 } 2698 2699 TEST(DecodeTest, AnimationTestStreaming) { 2700 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 2701 size_t xsize = 123; 2702 size_t ysize = 77; 2703 static const size_t num_frames = 2; 2704 std::vector<uint8_t> frames[2]; 2705 frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); 2706 frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1); 2707 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2708 2709 jxl::CodecInOut io{memory_manager}; 2710 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2711 io.metadata.m.SetUintSamples(16); 2712 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 2713 io.metadata.m.have_animation = true; 2714 io.frames.clear(); 2715 io.frames.reserve(num_frames); 2716 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2717 2718 std::vector<uint32_t> frame_durations(num_frames); 2719 for (size_t i = 0; i < num_frames; ++i) { 2720 frame_durations[i] = 5 + i; 2721 } 2722 2723 for (size_t i = 0; i < num_frames; ++i) { 2724 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 2725 2726 EXPECT_TRUE(ConvertFromExternal( 2727 jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, 2728 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 2729 /*bits_per_sample=*/16, format, 2730 /*pool=*/nullptr, &bundle)); 2731 bundle.duration = frame_durations[i]; 2732 io.frames.push_back(std::move(bundle)); 2733 } 2734 2735 jxl::CompressParams cparams; 2736 cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip. 2737 cparams.speed_tier = jxl::SpeedTier::kThunder; 2738 std::vector<uint8_t> compressed; 2739 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 2740 2741 // Decode and test the animation frames 2742 2743 const size_t step_size = 16; 2744 2745 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2746 const uint8_t* next_in = compressed.data(); 2747 size_t avail_in = 0; 2748 size_t frame_headers_seen = 0; 2749 size_t frames_seen = 0; 2750 bool seen_basic_info = false; 2751 2752 void* runner = JxlThreadParallelRunnerCreate( 2753 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 2754 EXPECT_EQ(JXL_DEC_SUCCESS, 2755 JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); 2756 2757 EXPECT_EQ(JXL_DEC_SUCCESS, 2758 JxlDecoderSubscribeEvents( 2759 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 2760 2761 std::vector<uint8_t> frames2[2]; 2762 for (size_t i = 0; i < num_frames; ++i) { 2763 frames2[i].resize(frames[i].size()); 2764 } 2765 2766 size_t total_in = 0; 2767 size_t loop_count = 0; 2768 2769 for (;;) { 2770 if (loop_count++ > compressed.size()) { 2771 fprintf(stderr, "Too many loops\n"); 2772 FAIL(); 2773 break; 2774 } 2775 2776 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 2777 auto status = JxlDecoderProcessInput(dec); 2778 size_t remaining = JxlDecoderReleaseInput(dec); 2779 EXPECT_LE(remaining, avail_in); 2780 next_in += avail_in - remaining; 2781 avail_in = remaining; 2782 2783 if (status == JXL_DEC_SUCCESS) { 2784 break; 2785 } else if (status == JXL_DEC_ERROR) { 2786 FAIL(); 2787 } else if (status == JXL_DEC_NEED_MORE_INPUT) { 2788 if (total_in >= compressed.size()) { 2789 fprintf(stderr, "Already gave all input data\n"); 2790 FAIL(); 2791 break; 2792 } 2793 size_t amount = step_size; 2794 if (total_in + amount > compressed.size()) { 2795 amount = compressed.size() - total_in; 2796 } 2797 avail_in += amount; 2798 total_in += amount; 2799 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 2800 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 2801 dec, &format, frames2[frames_seen].data(), 2802 frames2[frames_seen].size())); 2803 } else if (status == JXL_DEC_BASIC_INFO) { 2804 EXPECT_EQ(false, seen_basic_info); 2805 seen_basic_info = true; 2806 JxlBasicInfo info; 2807 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2808 EXPECT_EQ(xsize, info.xsize); 2809 EXPECT_EQ(ysize, info.ysize); 2810 } else if (status == JXL_DEC_FRAME) { 2811 EXPECT_EQ(true, seen_basic_info); 2812 frame_headers_seen++; 2813 } else if (status == JXL_DEC_FULL_IMAGE) { 2814 frames_seen++; 2815 EXPECT_EQ(frame_headers_seen, frames_seen); 2816 } else { 2817 fprintf(stderr, "Unexpected status: %d\n", static_cast<int>(status)); 2818 FAIL(); 2819 } 2820 } 2821 2822 EXPECT_EQ(true, seen_basic_info); 2823 EXPECT_EQ(num_frames, frames_seen); 2824 EXPECT_EQ(num_frames, frame_headers_seen); 2825 for (size_t i = 0; i < num_frames; ++i) { 2826 EXPECT_EQ(frames[i], frames2[i]); 2827 } 2828 2829 JxlThreadParallelRunnerDestroy(runner); 2830 JxlDecoderDestroy(dec); 2831 } 2832 2833 TEST(DecodeTest, ExtraChannelTest) { 2834 size_t xsize = 55; 2835 size_t ysize = 257; 2836 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 2837 JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2838 2839 jxl::TestCodestreamParams params; 2840 // Lossless to verify pixels exactly after roundtrip. 2841 params.cparams.SetLossless(); 2842 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 2843 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 2844 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 2845 2846 size_t align = 17; 2847 JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align}; 2848 2849 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2850 2851 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( 2852 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE)); 2853 2854 EXPECT_EQ(JXL_DEC_SUCCESS, 2855 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 2856 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2857 JxlBasicInfo info; 2858 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2859 EXPECT_EQ(1u, info.num_extra_channels); 2860 EXPECT_EQ(JXL_FALSE, info.alpha_premultiplied); 2861 2862 JxlExtraChannelInfo extra_info; 2863 EXPECT_EQ(JXL_DEC_SUCCESS, 2864 JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info)); 2865 EXPECT_EQ(0, extra_info.type); 2866 2867 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 2868 size_t buffer_size; 2869 EXPECT_EQ(JXL_DEC_SUCCESS, 2870 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 2871 size_t extra_size; 2872 EXPECT_EQ(JXL_DEC_SUCCESS, 2873 JxlDecoderExtraChannelBufferSize(dec, &format, &extra_size, 0)); 2874 2875 std::vector<uint8_t> image(buffer_size); 2876 std::vector<uint8_t> extra(extra_size); 2877 2878 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 2879 dec, &format, image.data(), image.size())); 2880 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetExtraChannelBuffer( 2881 dec, &format, extra.data(), extra.size(), 0)); 2882 2883 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 2884 2885 // After the full image was output, JxlDecoderProcessInput should return 2886 // success to indicate all is done. 2887 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 2888 JxlDecoderDestroy(dec); 2889 2890 EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), image.data(), xsize, 2891 ysize, format_orig, format)); 2892 2893 // Compare the extracted extra channel with the original alpha channel 2894 2895 std::vector<uint8_t> alpha(pixels.size() / 4); 2896 for (size_t i = 0; i < pixels.size(); i += 8) { 2897 size_t index_alpha = i / 4; 2898 alpha[index_alpha + 0] = pixels[i + 6]; 2899 alpha[index_alpha + 1] = pixels[i + 7]; 2900 } 2901 JxlPixelFormat format_alpha = format; 2902 format_alpha.num_channels = 1; 2903 JxlPixelFormat format_orig_alpha = format_orig; 2904 format_orig_alpha.num_channels = 1; 2905 2906 EXPECT_EQ(0u, 2907 jxl::test::ComparePixels(alpha.data(), extra.data(), xsize, ysize, 2908 format_orig_alpha, format_alpha)); 2909 } 2910 2911 TEST(DecodeTest, SkipCurrentFrameTest) { 2912 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 2913 size_t xsize = 90; 2914 size_t ysize = 120; 2915 constexpr size_t num_frames = 7; 2916 std::vector<uint8_t> frames[num_frames]; 2917 for (size_t i = 0; i < num_frames; i++) { 2918 frames[i] = jxl::test::GetSomeTestImage(xsize, ysize, 3, i); 2919 } 2920 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 2921 2922 jxl::CodecInOut io{memory_manager}; 2923 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2924 io.metadata.m.SetUintSamples(16); 2925 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 2926 io.metadata.m.have_animation = true; 2927 io.frames.clear(); 2928 io.frames.reserve(num_frames); 2929 ASSERT_TRUE(io.SetSize(xsize, ysize)); 2930 2931 std::vector<uint32_t> frame_durations(num_frames); 2932 for (size_t i = 0; i < num_frames; ++i) { 2933 frame_durations[i] = 5 + i; 2934 } 2935 2936 for (size_t i = 0; i < num_frames; ++i) { 2937 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 2938 if (i & 1) { 2939 // Mark some frames as referenceable, others not. 2940 bundle.use_for_next_frame = true; 2941 } 2942 2943 EXPECT_TRUE(ConvertFromExternal( 2944 jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, 2945 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 2946 /*bits_per_sample=*/16, format, 2947 /*pool=*/nullptr, &bundle)); 2948 bundle.duration = frame_durations[i]; 2949 io.frames.push_back(std::move(bundle)); 2950 } 2951 2952 jxl::CompressParams cparams; 2953 cparams.speed_tier = jxl::SpeedTier::kThunder; 2954 std::vector<uint8_t> compressed; 2955 jxl::PassDefinition passes[] = {{2, 0, 4}, {4, 0, 4}, {8, 2, 2}, {8, 0, 1}}; 2956 jxl::ProgressiveMode progressive_mode{passes}; 2957 cparams.custom_progressive_mode = &progressive_mode; 2958 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 2959 2960 JxlDecoder* dec = JxlDecoderCreate(nullptr); 2961 const uint8_t* next_in = compressed.data(); 2962 size_t avail_in = compressed.size(); 2963 2964 EXPECT_EQ(JXL_DEC_SUCCESS, 2965 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | 2966 JXL_DEC_FRAME_PROGRESSION | 2967 JXL_DEC_FULL_IMAGE)); 2968 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kLastPasses)); 2969 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 2970 2971 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 2972 size_t buffer_size; 2973 EXPECT_EQ(JXL_DEC_SUCCESS, 2974 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 2975 JxlBasicInfo info; 2976 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 2977 2978 for (size_t i = 0; i < num_frames; ++i) { 2979 printf("Decoding frame %d\n", static_cast<int>(i)); 2980 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec)); 2981 std::vector<uint8_t> pixels(buffer_size); 2982 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 2983 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec)); 2984 JxlFrameHeader frame_header; 2985 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 2986 EXPECT_EQ(frame_durations[i], frame_header.duration); 2987 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 2988 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 2989 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 2990 dec, &format, pixels.data(), pixels.size())); 2991 if (i == 2) { 2992 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec)); 2993 continue; 2994 } 2995 EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec)); 2996 EXPECT_EQ(8, JxlDecoderGetIntendedDownsamplingRatio(dec)); 2997 if (i == 3) { 2998 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec)); 2999 continue; 3000 } 3001 EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec)); 3002 EXPECT_EQ(4, JxlDecoderGetIntendedDownsamplingRatio(dec)); 3003 if (i == 4) { 3004 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec)); 3005 continue; 3006 } 3007 EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec)); 3008 EXPECT_EQ(2, JxlDecoderGetIntendedDownsamplingRatio(dec)); 3009 if (i == 5) { 3010 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec)); 3011 continue; 3012 } 3013 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3014 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec)); 3015 } 3016 3017 // After all frames were decoded, JxlDecoderProcessInput should return 3018 // success to indicate all is done. 3019 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3020 3021 JxlDecoderDestroy(dec); 3022 } 3023 3024 TEST(DecodeTest, SkipFrameTest) { 3025 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 3026 size_t xsize = 90; 3027 size_t ysize = 120; 3028 constexpr size_t num_frames = 16; 3029 std::vector<uint8_t> frames[num_frames]; 3030 for (size_t i = 0; i < num_frames; i++) { 3031 frames[i] = jxl::test::GetSomeTestImage(xsize, ysize, 3, i); 3032 } 3033 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 3034 3035 jxl::CodecInOut io{memory_manager}; 3036 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3037 io.metadata.m.SetUintSamples(16); 3038 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 3039 io.metadata.m.have_animation = true; 3040 io.frames.clear(); 3041 io.frames.reserve(num_frames); 3042 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3043 3044 std::vector<uint32_t> frame_durations(num_frames); 3045 for (size_t i = 0; i < num_frames; ++i) { 3046 frame_durations[i] = 5 + i; 3047 } 3048 3049 for (size_t i = 0; i < num_frames; ++i) { 3050 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 3051 if (i & 1) { 3052 // Mark some frames as referenceable, others not. 3053 bundle.use_for_next_frame = true; 3054 } 3055 3056 EXPECT_TRUE(ConvertFromExternal( 3057 jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize, 3058 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3059 /*bits_per_sample=*/16, format, 3060 /*pool=*/nullptr, &bundle)); 3061 bundle.duration = frame_durations[i]; 3062 io.frames.push_back(std::move(bundle)); 3063 } 3064 3065 jxl::CompressParams cparams; 3066 cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip. 3067 cparams.speed_tier = jxl::SpeedTier::kThunder; 3068 std::vector<uint8_t> compressed; 3069 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 3070 3071 // Decode and test the animation frames 3072 3073 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3074 const uint8_t* next_in = compressed.data(); 3075 size_t avail_in = compressed.size(); 3076 3077 void* runner = JxlThreadParallelRunnerCreate( 3078 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3079 EXPECT_EQ(JXL_DEC_SUCCESS, 3080 JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); 3081 3082 EXPECT_EQ(JXL_DEC_SUCCESS, 3083 JxlDecoderSubscribeEvents( 3084 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3085 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3086 3087 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 3088 size_t buffer_size; 3089 EXPECT_EQ(JXL_DEC_SUCCESS, 3090 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3091 JxlBasicInfo info; 3092 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 3093 3094 for (size_t i = 0; i < num_frames; ++i) { 3095 if (i == 3) { 3096 JxlDecoderSkipFrames(dec, 5); 3097 i += 5; 3098 } 3099 std::vector<uint8_t> pixels(buffer_size); 3100 3101 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3102 3103 JxlFrameHeader frame_header; 3104 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3105 EXPECT_EQ(frame_durations[i], frame_header.duration); 3106 3107 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3108 3109 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3110 3111 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3112 dec, &format, pixels.data(), pixels.size())); 3113 3114 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3115 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3116 xsize, ysize, format, format)); 3117 } 3118 3119 // After all frames were decoded, JxlDecoderProcessInput should return 3120 // success to indicate all is done. 3121 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3122 3123 // Test rewinding the decoder and skipping different frames 3124 3125 JxlDecoderRewind(dec); 3126 EXPECT_EQ(JXL_DEC_SUCCESS, 3127 JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3128 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3129 3130 for (size_t i = 0; i < num_frames; ++i) { 3131 int test_skipping = (i == 9) ? 3 : 0; 3132 std::vector<uint8_t> pixels(buffer_size); 3133 3134 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3135 3136 // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this 3137 // should only skip the next frame, not the currently processed one. 3138 if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping); 3139 3140 JxlFrameHeader frame_header; 3141 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3142 EXPECT_EQ(frame_durations[i], frame_header.duration); 3143 3144 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3145 3146 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3147 3148 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3149 dec, &format, pixels.data(), pixels.size())); 3150 3151 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3152 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3153 xsize, ysize, format, format)); 3154 3155 if (test_skipping) i += test_skipping; 3156 } 3157 3158 JxlThreadParallelRunnerDestroy(runner); 3159 JxlDecoderDestroy(dec); 3160 } 3161 3162 TEST(DecodeTest, SkipFrameWithBlendingTest) { 3163 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 3164 size_t xsize = 90; 3165 size_t ysize = 120; 3166 constexpr size_t num_frames = 16; 3167 std::vector<uint8_t> frames[num_frames]; 3168 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 3169 3170 jxl::CodecInOut io{memory_manager}; 3171 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3172 io.metadata.m.SetUintSamples(16); 3173 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 3174 io.metadata.m.have_animation = true; 3175 io.frames.clear(); 3176 io.frames.reserve(num_frames); 3177 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3178 3179 std::vector<uint32_t> frame_durations(num_frames); 3180 3181 for (size_t i = 0; i < num_frames; ++i) { 3182 if (i < 5) { 3183 std::vector<uint8_t> frame_internal = 3184 jxl::test::GetSomeTestImage(xsize, ysize, 3, i * 2 + 1); 3185 // An internal frame with 0 duration, and use_for_next_frame, this is a 3186 // frame that is not rendered and not output by the API, but on which the 3187 // rendered frames depend 3188 jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m); 3189 EXPECT_TRUE(ConvertFromExternal( 3190 jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize, 3191 ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3192 /*bits_per_sample=*/16, format, 3193 /*pool=*/nullptr, &bundle_internal)); 3194 bundle_internal.duration = 0; 3195 bundle_internal.use_for_next_frame = true; 3196 io.frames.push_back(std::move(bundle_internal)); 3197 } 3198 3199 std::vector<uint8_t> frame = 3200 jxl::test::GetSomeTestImage(xsize, ysize, 3, i * 2); 3201 // Actual rendered frame 3202 frame_durations[i] = 5 + i; 3203 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 3204 EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()), 3205 xsize, ysize, 3206 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3207 /*bits_per_sample=*/16, format, 3208 /*pool=*/nullptr, &bundle)); 3209 bundle.duration = frame_durations[i]; 3210 // Create some variation in which frames depend on which. 3211 if (i != 3 && i != 9 && i != 10) { 3212 bundle.use_for_next_frame = true; 3213 } 3214 if (i != 12) { 3215 bundle.blend = true; 3216 // Choose a blend mode that depends on the pixels of the saved frame and 3217 // doesn't use alpha 3218 bundle.blendmode = jxl::BlendMode::kMul; 3219 } 3220 io.frames.push_back(std::move(bundle)); 3221 } 3222 3223 jxl::CompressParams cparams; 3224 cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip. 3225 cparams.speed_tier = jxl::SpeedTier::kThunder; 3226 std::vector<uint8_t> compressed; 3227 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 3228 3229 // Independently decode all frames without any skipping, to create the 3230 // expected blended frames, for the actual tests below to compare with. 3231 { 3232 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3233 const uint8_t* next_in = compressed.data(); 3234 size_t avail_in = compressed.size(); 3235 3236 void* runner = JxlThreadParallelRunnerCreate( 3237 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3238 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( 3239 dec, JxlThreadParallelRunner, runner)); 3240 EXPECT_EQ(JXL_DEC_SUCCESS, 3241 JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE)); 3242 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3243 for (auto& frame : frames) { 3244 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3245 frame.resize(xsize * ysize * 6); 3246 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3247 dec, &format, frame.data(), frame.size())); 3248 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3249 } 3250 3251 // After all frames were decoded, JxlDecoderProcessInput should return 3252 // success to indicate all is done. 3253 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3254 JxlThreadParallelRunnerDestroy(runner); 3255 JxlDecoderDestroy(dec); 3256 } 3257 3258 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3259 const uint8_t* next_in = compressed.data(); 3260 size_t avail_in = compressed.size(); 3261 3262 void* runner = JxlThreadParallelRunnerCreate( 3263 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3264 EXPECT_EQ(JXL_DEC_SUCCESS, 3265 JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); 3266 3267 EXPECT_EQ(JXL_DEC_SUCCESS, 3268 JxlDecoderSubscribeEvents( 3269 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3270 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3271 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 3272 size_t buffer_size; 3273 EXPECT_EQ(JXL_DEC_SUCCESS, 3274 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3275 JxlBasicInfo info; 3276 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 3277 3278 for (size_t i = 0; i < num_frames; ++i) { 3279 std::vector<uint8_t> pixels(buffer_size); 3280 3281 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3282 3283 JxlFrameHeader frame_header; 3284 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3285 EXPECT_EQ(frame_durations[i], frame_header.duration); 3286 3287 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3288 3289 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3290 3291 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3292 dec, &format, pixels.data(), pixels.size())); 3293 3294 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3295 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3296 xsize, ysize, format, format)); 3297 3298 // Test rewinding mid-way, not decoding all frames. 3299 if (i == 8) { 3300 break; 3301 } 3302 } 3303 3304 JxlDecoderRewind(dec); 3305 EXPECT_EQ(JXL_DEC_SUCCESS, 3306 JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3307 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3308 3309 for (size_t i = 0; i < num_frames; ++i) { 3310 if (i == 3) { 3311 JxlDecoderSkipFrames(dec, 5); 3312 i += 5; 3313 } 3314 std::vector<uint8_t> pixels(buffer_size); 3315 3316 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3317 3318 JxlFrameHeader frame_header; 3319 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3320 EXPECT_EQ(frame_durations[i], frame_header.duration); 3321 3322 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3323 3324 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3325 3326 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3327 dec, &format, pixels.data(), pixels.size())); 3328 3329 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3330 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3331 xsize, ysize, format, format)); 3332 } 3333 3334 // After all frames were decoded, JxlDecoderProcessInput should return 3335 // success to indicate all is done. 3336 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3337 3338 // Test rewinding the decoder and skipping different frames 3339 3340 JxlDecoderRewind(dec); 3341 EXPECT_EQ(JXL_DEC_SUCCESS, 3342 JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3343 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3344 3345 for (size_t i = 0; i < num_frames; ++i) { 3346 int test_skipping = (i == 9) ? 3 : 0; 3347 std::vector<uint8_t> pixels(buffer_size); 3348 3349 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3350 3351 // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this 3352 // should only skip the next frame, not the currently processed one. 3353 if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping); 3354 3355 JxlFrameHeader frame_header; 3356 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3357 EXPECT_EQ(frame_durations[i], frame_header.duration); 3358 3359 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3360 3361 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3362 3363 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 3364 dec, &format, pixels.data(), pixels.size())); 3365 3366 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3367 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3368 xsize, ysize, format, format)); 3369 3370 if (test_skipping) i += test_skipping; 3371 } 3372 3373 JxlThreadParallelRunnerDestroy(runner); 3374 JxlDecoderDestroy(dec); 3375 } 3376 3377 TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { 3378 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 3379 size_t xsize = 90; 3380 size_t ysize = 120; 3381 constexpr size_t num_frames = 16; 3382 std::vector<uint8_t> frames[num_frames + 5]; 3383 JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 3384 3385 jxl::CodecInOut io{memory_manager}; 3386 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3387 io.metadata.m.SetUintSamples(16); 3388 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 3389 io.metadata.m.have_animation = true; 3390 io.frames.clear(); 3391 io.frames.reserve(num_frames + 5); 3392 ASSERT_TRUE(io.SetSize(xsize, ysize)); 3393 3394 std::vector<uint32_t> frame_durations_c; 3395 std::vector<uint32_t> frame_durations_nc; 3396 std::vector<uint32_t> frame_xsize; 3397 std::vector<uint32_t> frame_ysize; 3398 std::vector<uint32_t> frame_x0; 3399 std::vector<uint32_t> frame_y0; 3400 3401 for (size_t i = 0; i < num_frames; ++i) { 3402 size_t cropxsize = 1 + xsize * 2 / (i + 1); 3403 size_t cropysize = 1 + ysize * 3 / (i + 2); 3404 int cropx0 = i * 3 - 8; 3405 int cropy0 = i * 4 - 7; 3406 if (i < 5) { 3407 std::vector<uint8_t> frame_internal = 3408 jxl::test::GetSomeTestImage(xsize / 2, ysize / 2, 4, i * 2 + 1); 3409 // An internal frame with 0 duration, and use_for_next_frame, this is a 3410 // frame that is not rendered and not output by default by the API, but on 3411 // which the rendered frames depend 3412 jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m); 3413 EXPECT_TRUE(ConvertFromExternal( 3414 jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize / 2, 3415 ysize / 2, jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3416 /*bits_per_sample=*/16, format, 3417 /*pool=*/nullptr, &bundle_internal)); 3418 bundle_internal.duration = 0; 3419 bundle_internal.use_for_next_frame = true; 3420 bundle_internal.origin = {13, 17}; 3421 io.frames.push_back(std::move(bundle_internal)); 3422 frame_durations_nc.push_back(0); 3423 frame_xsize.push_back(xsize / 2); 3424 frame_ysize.push_back(ysize / 2); 3425 frame_x0.push_back(13); 3426 frame_y0.push_back(17); 3427 } 3428 3429 std::vector<uint8_t> frame = 3430 jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2); 3431 // Actual rendered frame 3432 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 3433 EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()), 3434 cropxsize, cropysize, 3435 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3436 /*bits_per_sample=*/16, format, 3437 /*pool=*/nullptr, &bundle)); 3438 bundle.duration = 5 + i; 3439 frame_durations_nc.push_back(5 + i); 3440 frame_durations_c.push_back(5 + i); 3441 frame_xsize.push_back(cropxsize); 3442 frame_ysize.push_back(cropysize); 3443 frame_x0.push_back(cropx0); 3444 frame_y0.push_back(cropy0); 3445 bundle.origin = {cropx0, cropy0}; 3446 // Create some variation in which frames depend on which. 3447 if (i != 3 && i != 9 && i != 10) { 3448 bundle.use_for_next_frame = true; 3449 } 3450 if (i != 12) { 3451 bundle.blend = true; 3452 bundle.blendmode = jxl::BlendMode::kBlend; 3453 } 3454 io.frames.push_back(std::move(bundle)); 3455 } 3456 3457 jxl::CompressParams cparams; 3458 cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip. 3459 cparams.speed_tier = jxl::SpeedTier::kThunder; 3460 std::vector<uint8_t> compressed; 3461 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 3462 // try both with and without coalescing 3463 for (auto coalescing : {JXL_TRUE, JXL_FALSE}) { 3464 // Independently decode all frames without any skipping, to create the 3465 // expected blended frames, for the actual tests below to compare with. 3466 { 3467 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3468 const uint8_t* next_in = compressed.data(); 3469 size_t avail_in = compressed.size(); 3470 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); 3471 void* runner = JxlThreadParallelRunnerCreate( 3472 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3473 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( 3474 dec, JxlThreadParallelRunner, runner)); 3475 EXPECT_EQ(JXL_DEC_SUCCESS, 3476 JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE)); 3477 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3478 for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) { 3479 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3480 size_t buffer_size; 3481 EXPECT_EQ(JXL_DEC_SUCCESS, 3482 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3483 if (coalescing) { 3484 EXPECT_EQ(xsize * ysize * 8, buffer_size); 3485 } else { 3486 EXPECT_EQ(frame_xsize[i] * frame_ysize[i] * 8, buffer_size); 3487 } 3488 frames[i].resize(buffer_size); 3489 EXPECT_EQ(JXL_DEC_SUCCESS, 3490 JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(), 3491 frames[i].size())); 3492 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3493 } 3494 3495 // After all frames were decoded, JxlDecoderProcessInput should return 3496 // success to indicate all is done. 3497 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3498 JxlThreadParallelRunnerDestroy(runner); 3499 JxlDecoderDestroy(dec); 3500 } 3501 3502 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3503 const uint8_t* next_in = compressed.data(); 3504 size_t avail_in = compressed.size(); 3505 3506 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); 3507 void* runner = JxlThreadParallelRunnerCreate( 3508 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3509 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( 3510 dec, JxlThreadParallelRunner, runner)); 3511 3512 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( 3513 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | 3514 JXL_DEC_FULL_IMAGE)); 3515 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3516 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 3517 JxlBasicInfo info; 3518 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 3519 3520 for (size_t i = 0; i < num_frames; ++i) { 3521 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3522 3523 size_t buffer_size; 3524 EXPECT_EQ(JXL_DEC_SUCCESS, 3525 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3526 std::vector<uint8_t> pixels(buffer_size); 3527 3528 JxlFrameHeader frame_header; 3529 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3530 EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]), 3531 frame_header.duration); 3532 3533 EXPECT_EQ(i + 1 == num_frames, frame_header.is_last); 3534 3535 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3536 3537 EXPECT_EQ(JXL_DEC_SUCCESS, 3538 JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(), 3539 pixels.size())); 3540 3541 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3542 if (coalescing) { 3543 EXPECT_EQ(frame_header.layer_info.xsize, xsize); 3544 } else { 3545 EXPECT_EQ(frame_header.layer_info.xsize, frame_xsize[i]); 3546 } 3547 if (coalescing) { 3548 EXPECT_EQ(frame_header.layer_info.ysize, ysize); 3549 } else { 3550 EXPECT_EQ(frame_header.layer_info.ysize, frame_ysize[i]); 3551 } 3552 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3553 frame_header.layer_info.xsize, 3554 frame_header.layer_info.ysize, 3555 format, format)); 3556 3557 // Test rewinding mid-way, not decoding all frames. 3558 if (i == 8) { 3559 break; 3560 } 3561 } 3562 3563 JxlDecoderRewind(dec); 3564 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( 3565 dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3566 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3567 3568 for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) { 3569 if (i == 3) { 3570 JxlDecoderSkipFrames(dec, 5); 3571 i += 5; 3572 } 3573 3574 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3575 size_t buffer_size; 3576 EXPECT_EQ(JXL_DEC_SUCCESS, 3577 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3578 std::vector<uint8_t> pixels(buffer_size); 3579 3580 JxlFrameHeader frame_header; 3581 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3582 EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]), 3583 frame_header.duration); 3584 3585 EXPECT_EQ(i + 1 == num_frames + (coalescing ? 0 : 5), 3586 frame_header.is_last); 3587 3588 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3589 3590 EXPECT_EQ(JXL_DEC_SUCCESS, 3591 JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(), 3592 pixels.size())); 3593 3594 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3595 if (coalescing) { 3596 EXPECT_EQ(frame_header.layer_info.xsize, xsize); 3597 EXPECT_EQ(frame_header.layer_info.ysize, ysize); 3598 EXPECT_EQ(frame_header.layer_info.crop_x0, 0); 3599 EXPECT_EQ(frame_header.layer_info.crop_y0, 0); 3600 } else { 3601 EXPECT_EQ(frame_header.layer_info.xsize, frame_xsize[i]); 3602 EXPECT_EQ(frame_header.layer_info.ysize, frame_ysize[i]); 3603 EXPECT_EQ(frame_header.layer_info.crop_x0, frame_x0[i]); 3604 EXPECT_EQ(frame_header.layer_info.crop_y0, frame_y0[i]); 3605 EXPECT_EQ(frame_header.layer_info.blend_info.blendmode, 3606 i != 12 + 5 && frame_header.duration != 0 3607 ? 2 3608 : 0); // kBlend or the default kReplace 3609 } 3610 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3611 frame_header.layer_info.xsize, 3612 frame_header.layer_info.ysize, 3613 format, format)); 3614 } 3615 3616 // After all frames were decoded, JxlDecoderProcessInput should return 3617 // success to indicate all is done. 3618 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3619 3620 // Test rewinding the decoder and skipping different frames 3621 3622 JxlDecoderRewind(dec); 3623 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( 3624 dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3625 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3626 3627 for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) { 3628 int test_skipping = (i == 9) ? 3 : 0; 3629 3630 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 3631 size_t buffer_size; 3632 EXPECT_EQ(JXL_DEC_SUCCESS, 3633 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3634 std::vector<uint8_t> pixels(buffer_size); 3635 3636 // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this 3637 // should only skip the next frame, not the currently processed one. 3638 if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping); 3639 3640 JxlFrameHeader frame_header; 3641 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header)); 3642 EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]), 3643 frame_header.duration); 3644 3645 EXPECT_EQ(i + 1 == num_frames + (coalescing ? 0 : 5), 3646 frame_header.is_last); 3647 3648 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3649 3650 EXPECT_EQ(JXL_DEC_SUCCESS, 3651 JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(), 3652 pixels.size())); 3653 3654 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3655 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(), 3656 frame_header.layer_info.xsize, 3657 frame_header.layer_info.ysize, 3658 format, format)); 3659 3660 if (test_skipping) i += test_skipping; 3661 } 3662 3663 JxlThreadParallelRunnerDestroy(runner); 3664 JxlDecoderDestroy(dec); 3665 } 3666 } 3667 3668 TEST(DecodeTest, OrientedCroppedFrameTest) { 3669 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 3670 const auto test = [&](bool keep_orientation, uint32_t orientation, 3671 uint32_t resampling) { 3672 size_t xsize = 90; 3673 size_t ysize = 120; 3674 JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 3675 size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize); 3676 size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize); 3677 jxl::CodecInOut io{memory_manager}; 3678 EXPECT_TRUE(io.SetSize(xsize, ysize)); 3679 io.metadata.m.SetUintSamples(16); 3680 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 3681 io.metadata.m.orientation = orientation; 3682 io.frames.clear(); 3683 EXPECT_TRUE(io.SetSize(xsize, ysize)); 3684 3685 for (size_t i = 0; i < 3; ++i) { 3686 size_t cropxsize = 1 + xsize * 2 / (i + 1); 3687 size_t cropysize = 1 + ysize * 3 / (i + 2); 3688 int cropx0 = i * 3 - 8; 3689 int cropy0 = i * 4 - 7; 3690 3691 std::vector<uint8_t> frame = 3692 jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2); 3693 jxl::ImageBundle bundle(memory_manager, &io.metadata.m); 3694 EXPECT_TRUE(ConvertFromExternal( 3695 jxl::Bytes(frame.data(), frame.size()), cropxsize, cropysize, 3696 jxl::ColorEncoding::SRGB(/*is_gray=*/false), 3697 /*bits_per_sample=*/16, format, 3698 /*pool=*/nullptr, &bundle)); 3699 bundle.origin = {cropx0, cropy0}; 3700 bundle.use_for_next_frame = true; 3701 io.frames.push_back(std::move(bundle)); 3702 } 3703 3704 jxl::CompressParams cparams; 3705 cparams 3706 .SetLossless(); // Lossless to verify pixels exactly after roundtrip. 3707 cparams.speed_tier = jxl::SpeedTier::kThunder; 3708 cparams.resampling = resampling; 3709 std::vector<uint8_t> compressed; 3710 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 3711 3712 // 0 is merged frame as decoded with coalescing enabled (default) 3713 // 1-3 are non-coalesced frames as decoded with coalescing disabled 3714 // 4 is the manually merged frame 3715 std::vector<uint8_t> frames[5]; 3716 frames[4].resize(xsize * ysize * 8, 0); 3717 3718 // try both with and without coalescing 3719 for (auto coalescing : {JXL_TRUE, JXL_FALSE}) { 3720 // Independently decode all frames without any skipping, to create the 3721 // expected blended frames, for the actual tests below to compare with. 3722 { 3723 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3724 const uint8_t* next_in = compressed.data(); 3725 size_t avail_in = compressed.size(); 3726 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); 3727 EXPECT_EQ(JXL_DEC_SUCCESS, 3728 JxlDecoderSetKeepOrientation(dec, keep_orientation)); 3729 void* runner = JxlThreadParallelRunnerCreate( 3730 nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); 3731 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( 3732 dec, JxlThreadParallelRunner, runner)); 3733 EXPECT_EQ(JXL_DEC_SUCCESS, 3734 JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE)); 3735 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3736 for (size_t i = (coalescing ? 0 : 1); i < (coalescing ? 1 : 4); ++i) { 3737 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 3738 JxlFrameHeader frame_header; 3739 EXPECT_EQ(JXL_DEC_SUCCESS, 3740 JxlDecoderGetFrameHeader(dec, &frame_header)); 3741 size_t buffer_size; 3742 EXPECT_EQ(JXL_DEC_SUCCESS, 3743 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3744 if (coalescing) { 3745 EXPECT_EQ(xsize * ysize * 8, buffer_size); 3746 } else { 3747 EXPECT_EQ(frame_header.layer_info.xsize * 3748 frame_header.layer_info.ysize * 8, 3749 buffer_size); 3750 } 3751 frames[i].resize(buffer_size); 3752 EXPECT_EQ(JXL_DEC_SUCCESS, 3753 JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(), 3754 frames[i].size())); 3755 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 3756 EXPECT_EQ(frame_header.layer_info.blend_info.blendmode, 3757 JXL_BLEND_REPLACE); 3758 if (coalescing) { 3759 EXPECT_EQ(frame_header.layer_info.xsize, oxsize); 3760 EXPECT_EQ(frame_header.layer_info.ysize, oysize); 3761 EXPECT_EQ(frame_header.layer_info.crop_x0, 0); 3762 EXPECT_EQ(frame_header.layer_info.crop_y0, 0); 3763 } else { 3764 // manually merge this layer 3765 int x0 = frame_header.layer_info.crop_x0; 3766 int y0 = frame_header.layer_info.crop_y0; 3767 int w = frame_header.layer_info.xsize; 3768 int h = frame_header.layer_info.ysize; 3769 for (int y = 0; y < static_cast<int>(oysize); y++) { 3770 if (y < y0 || y >= y0 + h) continue; 3771 // pointers do whole 16-bit RGBA pixels at a time 3772 uint64_t* row_merged = reinterpret_cast<uint64_t*>( 3773 frames[4].data() + y * oxsize * 8); 3774 uint64_t* row_layer = reinterpret_cast<uint64_t*>( 3775 frames[i].data() + (y - y0) * w * 8); 3776 for (int x = 0; x < static_cast<int>(oxsize); x++) { 3777 if (x < x0 || x >= x0 + w) continue; 3778 row_merged[x] = row_layer[x - x0]; 3779 } 3780 } 3781 } 3782 } 3783 3784 // After all frames were decoded, JxlDecoderProcessInput should return 3785 // success to indicate all is done. 3786 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 3787 JxlThreadParallelRunnerDestroy(runner); 3788 JxlDecoderDestroy(dec); 3789 } 3790 } 3791 3792 EXPECT_EQ(0u, jxl::test::ComparePixels(frames[0].data(), frames[4].data(), 3793 oxsize, oysize, format, format)); 3794 }; 3795 3796 for (bool keep_orientation : {true, false}) { 3797 for (uint32_t orientation = 1; orientation <= 8; orientation++) { 3798 for (uint32_t resampling : {1, 2, 4, 8}) { 3799 SCOPED_TRACE(testing::Message() 3800 << "keep_orientation: " << keep_orientation << ", " 3801 << "orientation: " << orientation << ", " 3802 << "resampling: " << resampling); 3803 test(keep_orientation, orientation, resampling); 3804 } 3805 } 3806 } 3807 } 3808 3809 struct FramePositions { 3810 size_t frame_start; 3811 size_t header_end; 3812 size_t toc_end; 3813 std::vector<size_t> section_end; 3814 }; 3815 3816 struct StreamPositions { 3817 size_t codestream_start; 3818 size_t codestream_end; 3819 size_t basic_info; 3820 size_t jbrd_end = 0; 3821 std::vector<size_t> box_start; 3822 std::vector<FramePositions> frames; 3823 }; 3824 3825 void AnalyzeCodestream(const std::vector<uint8_t>& data, 3826 StreamPositions* streampos) { 3827 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 3828 // Unbox data to codestream and mark where it is broken up by boxes. 3829 std::vector<uint8_t> codestream; 3830 std::vector<std::pair<size_t, size_t>> breakpoints; 3831 bool codestream_end = false; 3832 ASSERT_LE(2, data.size()); 3833 if (data[0] == 0xff && data[1] == 0x0a) { 3834 codestream = std::vector<uint8_t>(data.begin(), data.end()); 3835 streampos->codestream_start = 0; 3836 } else { 3837 const uint8_t* in = data.data(); 3838 size_t pos = 0; 3839 while (pos < data.size()) { 3840 ASSERT_LE(pos + 8, data.size()); 3841 streampos->box_start.push_back(pos); 3842 size_t box_size = LoadBE32(in + pos); 3843 if (box_size == 0) box_size = data.size() - pos; 3844 ASSERT_LE(pos + box_size, data.size()); 3845 if (memcmp(in + pos + 4, "jxlc", 4) == 0) { 3846 EXPECT_TRUE(codestream.empty()); 3847 streampos->codestream_start = pos + 8; 3848 codestream.insert(codestream.end(), in + pos + 8, in + pos + box_size); 3849 codestream_end = true; 3850 } else if (memcmp(in + pos + 4, "jxlp", 4) == 0) { 3851 codestream_end = ((LoadBE32(in + pos + 8) & 0x80000000) != 0); 3852 if (codestream.empty()) { 3853 streampos->codestream_start = pos + 12; 3854 } else if (box_size > 12 || !codestream_end) { 3855 breakpoints.emplace_back(codestream.size(), 12); 3856 } 3857 codestream.insert(codestream.end(), in + pos + 12, in + pos + box_size); 3858 } else if (memcmp(in + pos + 4, "jbrd", 4) == 0) { 3859 EXPECT_TRUE(codestream.empty()); 3860 streampos->jbrd_end = pos + box_size; 3861 } else if (!codestream.empty() && !codestream_end) { 3862 breakpoints.emplace_back(codestream.size(), box_size); 3863 } 3864 pos += box_size; 3865 } 3866 ASSERT_EQ(pos, data.size()); 3867 } 3868 // Translate codestream positions to boxed stream positions. 3869 size_t offset = streampos->codestream_start; 3870 size_t bp = 0; 3871 auto add_offset = [&](size_t pos) { 3872 while (bp < breakpoints.size() && pos >= breakpoints[bp].first) { 3873 offset += breakpoints[bp++].second; 3874 } 3875 return pos + offset; 3876 }; 3877 // Analyze the unboxed codestream. 3878 jxl::BitReader br(jxl::Bytes(codestream.data(), codestream.size())); 3879 ASSERT_EQ(br.ReadFixedBits<16>(), 0x0AFF); 3880 jxl::CodecMetadata metadata; 3881 ASSERT_TRUE(ReadSizeHeader(&br, &metadata.size)); 3882 ASSERT_TRUE(ReadImageMetadata(&br, &metadata.m)); 3883 streampos->basic_info = 3884 add_offset(br.TotalBitsConsumed() / jxl::kBitsPerByte); 3885 metadata.transform_data.nonserialized_xyb_encoded = metadata.m.xyb_encoded; 3886 ASSERT_TRUE(jxl::Bundle::Read(&br, &metadata.transform_data)); 3887 if (metadata.m.color_encoding.WantICC()) { 3888 std::vector<uint8_t> icc; 3889 ASSERT_TRUE(jxl::test::ReadICC(&br, &icc)); 3890 ASSERT_TRUE(!icc.empty()); 3891 metadata.m.color_encoding.SetICCRaw(std::move(icc)); 3892 } 3893 ASSERT_TRUE(br.JumpToByteBoundary()); 3894 bool has_preview = metadata.m.have_preview; 3895 while (br.TotalBitsConsumed() < br.TotalBytes() * jxl::kBitsPerByte) { 3896 FramePositions p; 3897 p.frame_start = add_offset(br.TotalBitsConsumed() / jxl::kBitsPerByte); 3898 jxl::FrameHeader frame_header(&metadata); 3899 if (has_preview) { 3900 frame_header.nonserialized_is_preview = true; 3901 has_preview = false; 3902 } 3903 ASSERT_TRUE(ReadFrameHeader(&br, &frame_header)); 3904 p.header_end = 3905 add_offset(jxl::DivCeil(br.TotalBitsConsumed(), jxl::kBitsPerByte)); 3906 jxl::FrameDimensions frame_dim = frame_header.ToFrameDimensions(); 3907 uint64_t groups_total_size; 3908 const size_t toc_entries = 3909 jxl::NumTocEntries(frame_dim.num_groups, frame_dim.num_dc_groups, 3910 frame_header.passes.num_passes); 3911 std::vector<uint64_t> section_offsets; 3912 std::vector<uint32_t> section_sizes; 3913 ASSERT_TRUE(ReadGroupOffsets(memory_manager, toc_entries, &br, 3914 §ion_offsets, §ion_sizes, 3915 &groups_total_size)); 3916 EXPECT_EQ(br.TotalBitsConsumed() % jxl::kBitsPerByte, 0); 3917 size_t sections_start = br.TotalBitsConsumed() / jxl::kBitsPerByte; 3918 p.toc_end = add_offset(sections_start); 3919 for (size_t i = 0; i < toc_entries; ++i) { 3920 size_t end = sections_start + section_offsets[i] + section_sizes[i]; 3921 p.section_end.push_back(add_offset(end)); 3922 } 3923 br.SkipBits(groups_total_size * jxl::kBitsPerByte); 3924 streampos->frames.push_back(p); 3925 } 3926 streampos->codestream_end = add_offset(codestream.size()); 3927 EXPECT_EQ(br.TotalBitsConsumed(), br.TotalBytes() * jxl::kBitsPerByte); 3928 EXPECT_TRUE(br.Close()); 3929 } 3930 3931 enum ExpectedFlushState { NO_FLUSH, SAME_FLUSH, NEW_FLUSH }; 3932 struct Breakpoint { 3933 size_t file_pos; 3934 ExpectedFlushState expect_flush; 3935 }; 3936 3937 void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels, 3938 const std::vector<uint8_t>& pixels, 3939 const std::vector<uint8_t>& data, 3940 std::vector<Breakpoint> breakpoints) { 3941 // Size large enough for multiple groups, required to have progressive stages. 3942 ASSERT_LT(256, xsize); 3943 ASSERT_LT(256, ysize); 3944 std::vector<uint8_t> pixels2; 3945 pixels2.resize(pixels.size()); 3946 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 3947 JxlDecoder* dec = JxlDecoderCreate(nullptr); 3948 EXPECT_EQ(JXL_DEC_SUCCESS, 3949 JxlDecoderSubscribeEvents( 3950 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 3951 int bp = 0; 3952 const uint8_t* next_in = data.data(); 3953 size_t avail_in = breakpoints[bp].file_pos; 3954 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 3955 double prev_dist = 1.0; 3956 for (;;) { 3957 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 3958 printf("bp: %d status: 0x%x\n", bp, static_cast<int>(status)); 3959 if (status == JXL_DEC_BASIC_INFO) { 3960 JxlBasicInfo info; 3961 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 3962 EXPECT_EQ(info.xsize, xsize); 3963 EXPECT_EQ(info.ysize, ysize); 3964 // Output buffer/callback not yet set 3965 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 3966 size_t buffer_size; 3967 EXPECT_EQ(JXL_DEC_SUCCESS, 3968 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 3969 EXPECT_EQ(pixels2.size(), buffer_size); 3970 EXPECT_EQ(JXL_DEC_SUCCESS, 3971 JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(), 3972 pixels2.size())); 3973 } else if (status == JXL_DEC_FRAME) { 3974 // Nothing to do. 3975 } else if (status == JXL_DEC_SUCCESS) { 3976 EXPECT_EQ(bp + 1, breakpoints.size()); 3977 break; 3978 } else if (status == JXL_DEC_NEED_MORE_INPUT || 3979 status == JXL_DEC_FULL_IMAGE) { 3980 if (breakpoints[bp].expect_flush == NO_FLUSH) { 3981 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 3982 } else { 3983 if (status != JXL_DEC_FULL_IMAGE) { 3984 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 3985 } 3986 double dist = jxl::test::DistanceRMS(pixels2.data(), pixels.data(), 3987 xsize, ysize, format); 3988 if (breakpoints[bp].expect_flush == NEW_FLUSH) { 3989 EXPECT_LT(dist, prev_dist); 3990 prev_dist = dist; 3991 } else { 3992 EXPECT_EQ(dist, prev_dist); 3993 } 3994 } 3995 if (status == JXL_DEC_FULL_IMAGE) { 3996 EXPECT_EQ(bp + 1, breakpoints.size()); 3997 continue; 3998 } 3999 ASSERT_LT(++bp, breakpoints.size()); 4000 next_in += avail_in - JxlDecoderReleaseInput(dec); 4001 avail_in = breakpoints[bp].file_pos - (next_in - data.data()); 4002 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 4003 } else { 4004 printf("Unexpected status: 0x%x\n", static_cast<int>(status)); 4005 FAIL(); // unexpected returned status 4006 } 4007 } 4008 JxlDecoderDestroy(dec); 4009 } 4010 4011 TEST(DecodeTest, ProgressionTest) { 4012 size_t xsize = 508; 4013 size_t ysize = 470; 4014 uint32_t num_channels = 3; 4015 std::vector<uint8_t> pixels = 4016 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4017 jxl::TestCodestreamParams params; 4018 params.cparams.progressive_dc = 1; 4019 params.preview_mode = jxl::kSmallPreview; 4020 std::vector<uint8_t> data = 4021 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4022 xsize, ysize, num_channels, params); 4023 StreamPositions streampos; 4024 AnalyzeCodestream(data, &streampos); 4025 const std::vector<FramePositions>& fp = streampos.frames; 4026 // We have preview, dc frame and regular frame. 4027 EXPECT_EQ(3, fp.size()); 4028 EXPECT_EQ(7, fp[2].section_end.size()); 4029 EXPECT_EQ(data.size(), fp[2].section_end[6]); 4030 std::vector<Breakpoint> breakpoints{ 4031 {fp[0].frame_start, NO_FLUSH}, // headers 4032 {fp[1].frame_start, NO_FLUSH}, // preview 4033 {fp[2].frame_start, NO_FLUSH}, // dc frame 4034 {fp[2].section_end[0], NO_FLUSH}, // DC global 4035 {fp[2].section_end[1] - 1, NO_FLUSH}, // partial DC group 4036 {fp[2].section_end[1], NEW_FLUSH}, // DC group 4037 {fp[2].section_end[2], SAME_FLUSH}, // AC global 4038 {fp[2].section_end[3], NEW_FLUSH}, // AC group 0 4039 {fp[2].section_end[4] - 1, SAME_FLUSH}, // partial AC group 1 4040 {fp[2].section_end[4], NEW_FLUSH}, // AC group 1 4041 {fp[2].section_end[5], NEW_FLUSH}, // AC group 2 4042 {data.size() - 1, SAME_FLUSH}, // partial AC group 3 4043 {data.size(), NEW_FLUSH}}; // full image 4044 VerifyProgression(xsize, ysize, num_channels, pixels, data, breakpoints); 4045 } 4046 4047 TEST(DecodeTest, ProgressionTestLosslessAlpha) { 4048 size_t xsize = 508; 4049 size_t ysize = 470; 4050 uint32_t num_channels = 4; 4051 std::vector<uint8_t> pixels = 4052 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4053 jxl::TestCodestreamParams params; 4054 params.cparams.SetLossless(); 4055 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 4056 params.cparams.responsive = 1; 4057 std::vector<uint8_t> data = 4058 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4059 xsize, ysize, num_channels, params); 4060 StreamPositions streampos; 4061 AnalyzeCodestream(data, &streampos); 4062 const std::vector<FramePositions>& fp = streampos.frames; 4063 // We have preview, dc frame and regular frame. 4064 EXPECT_EQ(1, fp.size()); 4065 EXPECT_EQ(7, fp[0].section_end.size()); 4066 EXPECT_EQ(data.size(), fp[0].section_end[6]); 4067 std::vector<Breakpoint> breakpoints{ 4068 {fp[0].frame_start, NO_FLUSH}, // headers 4069 {fp[0].section_end[0] - 1, NO_FLUSH}, // partial DC global 4070 {fp[0].section_end[0], NEW_FLUSH}, // DC global 4071 {fp[0].section_end[1], SAME_FLUSH}, // DC group 4072 {fp[0].section_end[2], SAME_FLUSH}, // AC global 4073 {fp[0].section_end[3], NEW_FLUSH}, // AC group 0 4074 {fp[0].section_end[4] - 1, SAME_FLUSH}, // partial AC group 1 4075 {fp[0].section_end[4], NEW_FLUSH}, // AC group 1 4076 {fp[0].section_end[5], NEW_FLUSH}, // AC group 2 4077 {data.size() - 1, SAME_FLUSH}, // partial AC group 3 4078 {data.size(), NEW_FLUSH}}; // full image 4079 VerifyProgression(xsize, ysize, num_channels, pixels, data, breakpoints); 4080 } 4081 4082 void VerifyFilePosition(size_t expected_pos, const std::vector<uint8_t>& data, 4083 JxlDecoder* dec) { 4084 size_t remaining = JxlDecoderReleaseInput(dec); 4085 size_t pos = data.size() - remaining; 4086 EXPECT_EQ(expected_pos, pos); 4087 EXPECT_EQ(JXL_DEC_SUCCESS, 4088 JxlDecoderSetInput(dec, data.data() + pos, remaining)); 4089 } 4090 4091 TEST(DecodeTest, InputHandlingTestOneShot) { 4092 size_t xsize = 508; 4093 size_t ysize = 470; 4094 uint32_t num_channels = 3; 4095 std::vector<uint8_t> pixels = 4096 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4097 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 4098 printf("Testing with box format %d\n", i); 4099 jxl::TestCodestreamParams params; 4100 params.cparams.progressive_dc = 1; 4101 params.preview_mode = jxl::kSmallPreview; 4102 params.box_format = static_cast<CodeStreamBoxFormat>(i); 4103 std::vector<uint8_t> data = 4104 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4105 xsize, ysize, num_channels, params); 4106 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4107 StreamPositions streampos; 4108 AnalyzeCodestream(data, &streampos); 4109 const std::vector<FramePositions>& fp = streampos.frames; 4110 // We have preview, dc frame and regular frame. 4111 EXPECT_EQ(3, fp.size()); 4112 4113 std::vector<uint8_t> pixels2; 4114 pixels2.resize(pixels.size()); 4115 4116 int kNumEvents = 6; 4117 int events[] = { 4118 JXL_DEC_BASIC_INFO, JXL_DEC_COLOR_ENCODING, JXL_DEC_PREVIEW_IMAGE, 4119 JXL_DEC_FRAME, JXL_DEC_FULL_IMAGE, JXL_DEC_FRAME_PROGRESSION, 4120 }; 4121 size_t end_positions[] = { 4122 streampos.basic_info, fp[0].frame_start, 4123 fp[1].frame_start, fp[2].toc_end, 4124 streampos.codestream_end, streampos.codestream_end}; 4125 int events_wanted = 0; 4126 for (int j = 0; j < kNumEvents; ++j) { 4127 events_wanted |= events[j]; 4128 size_t end_pos = end_positions[j]; 4129 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4130 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted)); 4131 EXPECT_EQ(JXL_DEC_SUCCESS, 4132 JxlDecoderSetInput(dec, data.data(), data.size())); 4133 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4134 VerifyFilePosition(streampos.basic_info, data, dec); 4135 if (j >= 1) { 4136 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 4137 VerifyFilePosition(fp[0].frame_start, data, dec); 4138 } 4139 if (j >= 2) { 4140 EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec)); 4141 VerifyFilePosition(fp[0].toc_end, data, dec); 4142 size_t buffer_size; 4143 EXPECT_EQ(JXL_DEC_SUCCESS, 4144 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); 4145 EXPECT_GE(pixels2.size(), buffer_size); 4146 EXPECT_EQ(JXL_DEC_SUCCESS, 4147 JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(), 4148 buffer_size)); 4149 EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec)); 4150 VerifyFilePosition(fp[1].frame_start, data, dec); 4151 } 4152 if (j >= 3) { 4153 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4154 VerifyFilePosition(fp[2].toc_end, data, dec); 4155 if (j >= 5) { 4156 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kDC)); 4157 } 4158 } 4159 if (j >= 4) { 4160 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 4161 VerifyFilePosition(fp[2].toc_end, data, dec); 4162 size_t buffer_size; 4163 EXPECT_EQ(JXL_DEC_SUCCESS, 4164 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4165 EXPECT_EQ(pixels2.size(), buffer_size); 4166 EXPECT_EQ(JXL_DEC_SUCCESS, 4167 JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(), 4168 pixels2.size())); 4169 if (j >= 5) { 4170 EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec)); 4171 VerifyFilePosition(fp[2].section_end[1], data, dec); 4172 } 4173 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4174 VerifyFilePosition(streampos.codestream_end, data, dec); 4175 } 4176 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 4177 VerifyFilePosition(end_pos, data, dec); 4178 JxlDecoderDestroy(dec); 4179 } 4180 } 4181 } 4182 4183 JXL_TRANSCODE_JPEG_TEST(DecodeTest, InputHandlingTestJPEGOneshot) { 4184 TEST_LIBJPEG_SUPPORT(); 4185 size_t xsize = 123; 4186 size_t ysize = 77; 4187 size_t channels = 3; 4188 std::vector<uint8_t> pixels = 4189 jxl::test::GetSomeTestImage(xsize, ysize, channels, /*seed=*/0); 4190 for (int i = 1; i < kCSBF_NUM_ENTRIES; ++i) { 4191 printf("Testing with box format %d\n", i); 4192 std::vector<uint8_t> jpeg_codestream; 4193 jxl::TestCodestreamParams params; 4194 params.cparams.color_transform = jxl::ColorTransform::kNone; 4195 params.jpeg_codestream = &jpeg_codestream; 4196 params.preview_mode = jxl::kSmallPreview; 4197 params.box_format = static_cast<CodeStreamBoxFormat>(i); 4198 std::vector<uint8_t> data = 4199 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4200 xsize, ysize, channels, params); 4201 JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4202 StreamPositions streampos; 4203 AnalyzeCodestream(data, &streampos); 4204 const std::vector<FramePositions>& fp = streampos.frames; 4205 // We have preview and regular frame. 4206 EXPECT_EQ(2, fp.size()); 4207 EXPECT_LT(0, streampos.jbrd_end); 4208 4209 std::vector<uint8_t> pixels2; 4210 pixels2.resize(pixels.size()); 4211 4212 int kNumEvents = 6; 4213 int events[] = {JXL_DEC_BASIC_INFO, JXL_DEC_JPEG_RECONSTRUCTION, 4214 JXL_DEC_COLOR_ENCODING, JXL_DEC_PREVIEW_IMAGE, 4215 JXL_DEC_FRAME, JXL_DEC_FULL_IMAGE}; 4216 size_t end_positions[] = {streampos.basic_info, streampos.basic_info, 4217 fp[0].frame_start, fp[1].frame_start, 4218 fp[1].toc_end, streampos.codestream_end}; 4219 int events_wanted = 0; 4220 for (int j = 0; j < kNumEvents; ++j) { 4221 printf("j = %d\n", j); 4222 events_wanted |= events[j]; 4223 size_t end_pos = end_positions[j]; 4224 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4225 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted)); 4226 EXPECT_EQ(JXL_DEC_SUCCESS, 4227 JxlDecoderSetInput(dec, data.data(), data.size())); 4228 if (j >= 1) { 4229 EXPECT_EQ(JXL_DEC_JPEG_RECONSTRUCTION, JxlDecoderProcessInput(dec)); 4230 VerifyFilePosition(streampos.jbrd_end, data, dec); 4231 } 4232 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4233 VerifyFilePosition(streampos.basic_info, data, dec); 4234 if (j >= 2) { 4235 EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); 4236 VerifyFilePosition(fp[0].frame_start, data, dec); 4237 } 4238 if (j >= 3) { 4239 EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec)); 4240 VerifyFilePosition(fp[0].toc_end, data, dec); 4241 size_t buffer_size; 4242 EXPECT_EQ(JXL_DEC_SUCCESS, 4243 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); 4244 EXPECT_GE(pixels2.size(), buffer_size); 4245 EXPECT_EQ(JXL_DEC_SUCCESS, 4246 JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(), 4247 buffer_size)); 4248 EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec)); 4249 VerifyFilePosition(fp[1].frame_start, data, dec); 4250 } 4251 if (j >= 4) { 4252 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4253 VerifyFilePosition(fp[1].toc_end, data, dec); 4254 } 4255 if (j >= 5) { 4256 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 4257 VerifyFilePosition(fp[1].toc_end, data, dec); 4258 size_t buffer_size; 4259 EXPECT_EQ(JXL_DEC_SUCCESS, 4260 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4261 EXPECT_EQ(pixels2.size(), buffer_size); 4262 EXPECT_EQ(JXL_DEC_SUCCESS, 4263 JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(), 4264 pixels2.size())); 4265 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4266 VerifyFilePosition(streampos.codestream_end, data, dec); 4267 } 4268 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 4269 VerifyFilePosition(end_pos, data, dec); 4270 JxlDecoderDestroy(dec); 4271 } 4272 } 4273 } 4274 4275 TEST(DecodeTest, InputHandlingTestStreaming) { 4276 size_t xsize = 508; 4277 size_t ysize = 470; 4278 uint32_t num_channels = 3; 4279 std::vector<uint8_t> pixels = 4280 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4281 for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { 4282 printf("Testing with box format %d\n", i); 4283 fflush(stdout); 4284 jxl::TestCodestreamParams params; 4285 params.cparams.progressive_dc = 1; 4286 params.box_format = static_cast<CodeStreamBoxFormat>(i); 4287 params.preview_mode = jxl::kSmallPreview; 4288 std::vector<uint8_t> data = 4289 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4290 xsize, ysize, num_channels, params); 4291 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4292 StreamPositions streampos; 4293 AnalyzeCodestream(data, &streampos); 4294 const std::vector<FramePositions>& fp = streampos.frames; 4295 // We have preview, dc frame and regular frame. 4296 EXPECT_EQ(3, fp.size()); 4297 std::vector<uint8_t> pixels2; 4298 pixels2.resize(pixels.size()); 4299 int events_wanted = 4300 (JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_PREVIEW_IMAGE | 4301 JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION | 4302 JXL_DEC_BOX); 4303 for (size_t increment : {1, 7, 27, 1024}) { 4304 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4305 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted)); 4306 size_t file_pos = 0; 4307 size_t box_index = 0; 4308 size_t avail_in = 0; 4309 for (;;) { 4310 const uint8_t* next_in = data.data() + file_pos; 4311 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 4312 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 4313 size_t remaining = JxlDecoderReleaseInput(dec); 4314 size_t consumed = avail_in - remaining; 4315 file_pos += consumed; 4316 avail_in += increment; 4317 avail_in = std::min<size_t>(avail_in, data.size() - file_pos); 4318 if (status == JXL_DEC_BASIC_INFO) { 4319 EXPECT_EQ(file_pos, streampos.basic_info); 4320 } else if (status == JXL_DEC_COLOR_ENCODING) { 4321 EXPECT_EQ(file_pos, streampos.frames[0].frame_start); 4322 } else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) { 4323 EXPECT_EQ(file_pos, streampos.frames[0].toc_end); 4324 size_t buffer_size; 4325 EXPECT_EQ(JXL_DEC_SUCCESS, 4326 JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)); 4327 EXPECT_GE(pixels2.size(), buffer_size); 4328 EXPECT_EQ(JXL_DEC_SUCCESS, 4329 JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(), 4330 buffer_size)); 4331 } else if (status == JXL_DEC_PREVIEW_IMAGE) { 4332 EXPECT_EQ(file_pos, streampos.frames[1].frame_start); 4333 } else if (status == JXL_DEC_FRAME) { 4334 EXPECT_EQ(file_pos, streampos.frames[2].toc_end); 4335 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kDC)); 4336 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 4337 EXPECT_EQ(file_pos, streampos.frames[2].toc_end); 4338 size_t buffer_size; 4339 EXPECT_EQ(JXL_DEC_SUCCESS, 4340 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4341 EXPECT_EQ(pixels2.size(), buffer_size); 4342 EXPECT_EQ(JXL_DEC_SUCCESS, 4343 JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(), 4344 pixels2.size())); 4345 } else if (status == JXL_DEC_FRAME_PROGRESSION) { 4346 EXPECT_EQ(file_pos, streampos.frames[2].section_end[1]); 4347 } else if (status == JXL_DEC_FULL_IMAGE) { 4348 EXPECT_EQ(file_pos, streampos.codestream_end); 4349 } else if (status == JXL_DEC_SUCCESS) { 4350 EXPECT_EQ(file_pos, streampos.codestream_end); 4351 break; 4352 } else if (status == JXL_DEC_NEED_MORE_INPUT) { 4353 EXPECT_LT(remaining, 12); 4354 if ((i == kCSBF_None && file_pos >= 2) || 4355 (box_index > 0 && box_index < streampos.box_start.size() && 4356 file_pos >= streampos.box_start[box_index - 1] + 12 && 4357 file_pos < streampos.box_start[box_index])) { 4358 EXPECT_EQ(remaining, 0); 4359 } 4360 if (file_pos == data.size()) break; 4361 } else if (status == JXL_DEC_BOX) { 4362 ASSERT_LT(box_index, streampos.box_start.size()); 4363 EXPECT_EQ(file_pos, streampos.box_start[box_index++]); 4364 } else { 4365 printf("Unexpected status: 0x%x\n", static_cast<int>(status)); 4366 FAIL(); 4367 } 4368 } 4369 JxlDecoderDestroy(dec); 4370 } 4371 } 4372 } 4373 4374 TEST(DecodeTest, FlushTest) { 4375 // Size large enough for multiple groups, required to have progressive 4376 // stages 4377 size_t xsize = 333; 4378 size_t ysize = 300; 4379 uint32_t num_channels = 3; 4380 std::vector<uint8_t> pixels = 4381 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4382 jxl::TestCodestreamParams params; 4383 params.preview_mode = jxl::kSmallPreview; 4384 std::vector<uint8_t> data = 4385 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4386 xsize, ysize, num_channels, params); 4387 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4388 4389 std::vector<uint8_t> pixels2; 4390 pixels2.resize(pixels.size()); 4391 4392 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4393 4394 EXPECT_EQ(JXL_DEC_SUCCESS, 4395 JxlDecoderSubscribeEvents( 4396 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 4397 4398 // Ensure that the first part contains at least the full DC of the image, 4399 // otherwise flush does not work. 4400 size_t first_part = data.size() - 1; 4401 4402 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part)); 4403 4404 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4405 JxlBasicInfo info; 4406 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4407 EXPECT_EQ(info.xsize, xsize); 4408 EXPECT_EQ(info.ysize, ysize); 4409 4410 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4411 4412 // Output buffer not yet set 4413 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 4414 4415 size_t buffer_size; 4416 EXPECT_EQ(JXL_DEC_SUCCESS, 4417 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4418 EXPECT_EQ(pixels2.size(), buffer_size); 4419 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 4420 dec, &format, pixels2.data(), pixels2.size())); 4421 4422 // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if 4423 // data was already input before, since the processing of the frame only 4424 // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME. 4425 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4426 4427 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4428 4429 // Crude test of actual pixel data: pixel threshold of about 4% (2560/65535). 4430 // 29000 pixels can be above the threshold 4431 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4432 ysize, format, format, 2560.0), 4433 29000u); 4434 4435 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4436 4437 size_t consumed = first_part - JxlDecoderReleaseInput(dec); 4438 4439 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed, 4440 data.size() - consumed)); 4441 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4442 // Lower threshold for the final (still lossy) image 4443 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4444 ysize, format, format, 2560.0), 4445 11000u); 4446 4447 JxlDecoderDestroy(dec); 4448 } 4449 4450 TEST(DecodeTest, FlushTestImageOutCallback) { 4451 // Size large enough for multiple groups, required to have progressive 4452 // stages 4453 size_t xsize = 333; 4454 size_t ysize = 300; 4455 uint32_t num_channels = 3; 4456 std::vector<uint8_t> pixels = 4457 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4458 jxl::TestCodestreamParams params; 4459 params.preview_mode = jxl::kSmallPreview; 4460 std::vector<uint8_t> data = 4461 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4462 xsize, ysize, num_channels, params); 4463 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4464 4465 std::vector<uint8_t> pixels2; 4466 pixels2.resize(pixels.size()); 4467 4468 size_t bytes_per_pixel = format.num_channels * 2; 4469 size_t stride = bytes_per_pixel * xsize; 4470 auto callback = [&](size_t x, size_t y, size_t num_pixels, 4471 const void* pixels_row) { 4472 memcpy(pixels2.data() + stride * y + bytes_per_pixel * x, pixels_row, 4473 num_pixels * bytes_per_pixel); 4474 }; 4475 4476 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4477 4478 EXPECT_EQ(JXL_DEC_SUCCESS, 4479 JxlDecoderSubscribeEvents( 4480 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 4481 4482 // Ensure that the first part contains at least the full DC of the image, 4483 // otherwise flush does not work. 4484 size_t first_part = data.size() - 1; 4485 4486 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part)); 4487 4488 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4489 JxlBasicInfo info; 4490 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4491 EXPECT_EQ(info.xsize, xsize); 4492 EXPECT_EQ(info.ysize, ysize); 4493 4494 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4495 4496 // Output callback not yet set 4497 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 4498 4499 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutCallback( 4500 dec, &format, 4501 [](void* opaque, size_t x, size_t y, 4502 size_t xsize, const void* pixels_row) { 4503 auto cb = 4504 static_cast<decltype(&callback)>(opaque); 4505 (*cb)(x, y, xsize, pixels_row); 4506 }, 4507 /*opaque=*/&callback)); 4508 4509 // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if 4510 // data was already input before, since the processing of the frame only 4511 // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME. 4512 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4513 4514 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4515 4516 // Crude test of actual pixel data: pixel threshold of about 4% (2560/65535). 4517 // 29000 pixels can be above the threshold 4518 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4519 ysize, format, format, 2560.0), 4520 29000u); 4521 4522 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4523 4524 size_t consumed = first_part - JxlDecoderReleaseInput(dec); 4525 4526 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed, 4527 data.size() - consumed)); 4528 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4529 // Lower threshold for the final (still lossy) image 4530 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4531 ysize, format, format, 2560.0), 4532 11000u); 4533 4534 JxlDecoderDestroy(dec); 4535 } 4536 4537 TEST(DecodeTest, FlushTestLossyProgressiveAlpha) { 4538 // Size large enough for multiple groups, required to have progressive 4539 // stages 4540 size_t xsize = 333; 4541 size_t ysize = 300; 4542 uint32_t num_channels = 4; 4543 std::vector<uint8_t> pixels = 4544 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4545 jxl::TestCodestreamParams params; 4546 params.preview_mode = jxl::kSmallPreview; 4547 std::vector<uint8_t> data = 4548 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4549 xsize, ysize, num_channels, params); 4550 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4551 4552 std::vector<uint8_t> pixels2; 4553 pixels2.resize(pixels.size()); 4554 4555 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4556 4557 EXPECT_EQ(JXL_DEC_SUCCESS, 4558 JxlDecoderSubscribeEvents( 4559 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 4560 4561 // Ensure that the first part contains at least the full DC of the image, 4562 // otherwise flush does not work. 4563 size_t first_part = data.size() - 1; 4564 4565 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part)); 4566 4567 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4568 JxlBasicInfo info; 4569 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4570 EXPECT_EQ(info.xsize, xsize); 4571 EXPECT_EQ(info.ysize, ysize); 4572 4573 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4574 4575 // Output buffer not yet set 4576 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 4577 4578 size_t buffer_size; 4579 EXPECT_EQ(JXL_DEC_SUCCESS, 4580 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4581 EXPECT_EQ(pixels2.size(), buffer_size); 4582 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 4583 dec, &format, pixels2.data(), pixels2.size())); 4584 4585 // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if 4586 // data was already input before, since the processing of the frame only 4587 // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME. 4588 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4589 4590 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4591 4592 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4593 ysize, format, format, 2560.0), 4594 30000u); 4595 4596 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4597 4598 size_t consumed = first_part - JxlDecoderReleaseInput(dec); 4599 4600 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed, 4601 data.size() - consumed)); 4602 4603 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4604 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4605 ysize, format, format, 2560.0), 4606 11000u); 4607 4608 JxlDecoderDestroy(dec); 4609 } 4610 TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) { 4611 size_t xsize = 533; 4612 size_t ysize = 401; 4613 uint32_t num_channels = 4; 4614 std::vector<uint8_t> pixels = 4615 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4616 jxl::TestCodestreamParams params; 4617 params.cparams.resampling = 2; 4618 params.cparams.ec_resampling = 4; 4619 params.preview_mode = jxl::kSmallPreview; 4620 std::vector<uint8_t> data = 4621 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4622 xsize, ysize, num_channels, params); 4623 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4624 4625 std::vector<uint8_t> pixels2; 4626 pixels2.resize(pixels.size()); 4627 4628 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4629 4630 EXPECT_EQ(JXL_DEC_SUCCESS, 4631 JxlDecoderSubscribeEvents( 4632 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 4633 4634 // Ensure that the first part contains at least the full DC of the image, 4635 // otherwise flush does not work. 4636 size_t first_part = data.size() * 2 / 3; 4637 4638 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part)); 4639 4640 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4641 JxlBasicInfo info; 4642 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4643 EXPECT_EQ(info.xsize, xsize); 4644 EXPECT_EQ(info.ysize, ysize); 4645 4646 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4647 4648 // Output buffer not yet set 4649 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 4650 4651 size_t buffer_size; 4652 EXPECT_EQ(JXL_DEC_SUCCESS, 4653 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4654 EXPECT_EQ(pixels2.size(), buffer_size); 4655 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 4656 dec, &format, pixels2.data(), pixels2.size())); 4657 4658 // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if 4659 // data was already input before, since the processing of the frame only 4660 // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME. 4661 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4662 4663 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4664 4665 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4666 ysize, format, format, 2560.0), 4667 125000u); 4668 4669 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4670 4671 size_t consumed = first_part - JxlDecoderReleaseInput(dec); 4672 4673 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed, 4674 data.size() - consumed)); 4675 4676 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4677 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4678 ysize, format, format, 2560.0), 4679 70000u); 4680 4681 JxlDecoderDestroy(dec); 4682 } 4683 TEST(DecodeTest, FlushTestLosslessProgressiveAlpha) { 4684 // Size large enough for multiple groups, required to have progressive 4685 // stages 4686 size_t xsize = 333; 4687 size_t ysize = 300; 4688 uint32_t num_channels = 4; 4689 std::vector<uint8_t> pixels = 4690 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4691 jxl::TestCodestreamParams params; 4692 params.cparams.SetLossless(); 4693 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 4694 params.cparams.responsive = 1; 4695 params.cparams.modular_group_size_shift = 1; 4696 params.preview_mode = jxl::kSmallPreview; 4697 std::vector<uint8_t> data = 4698 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4699 xsize, ysize, num_channels, params); 4700 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4701 4702 std::vector<uint8_t> pixels2; 4703 pixels2.resize(pixels.size()); 4704 4705 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4706 4707 EXPECT_EQ(JXL_DEC_SUCCESS, 4708 JxlDecoderSubscribeEvents( 4709 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE)); 4710 4711 // Ensure that the first part contains at least the full DC of the image, 4712 // otherwise flush does not work. 4713 size_t first_part = data.size() / 2; 4714 4715 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part)); 4716 4717 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 4718 JxlBasicInfo info; 4719 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4720 EXPECT_EQ(info.xsize, xsize); 4721 EXPECT_EQ(info.ysize, ysize); 4722 4723 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 4724 4725 // Output buffer not yet set 4726 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec)); 4727 4728 size_t buffer_size; 4729 EXPECT_EQ(JXL_DEC_SUCCESS, 4730 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4731 EXPECT_EQ(pixels2.size(), buffer_size); 4732 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 4733 dec, &format, pixels2.data(), pixels2.size())); 4734 4735 // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if 4736 // data was already input before, since the processing of the frame only 4737 // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME. 4738 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4739 4740 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4741 4742 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4743 ysize, format, format, 2560.0), 4744 2700u); 4745 4746 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 4747 4748 size_t consumed = first_part - JxlDecoderReleaseInput(dec); 4749 4750 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed, 4751 data.size() - consumed)); 4752 4753 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 4754 EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize, 4755 ysize, format, format), 4756 0u); 4757 4758 JxlDecoderDestroy(dec); 4759 } 4760 4761 class DecodeProgressiveTest : public ::testing::TestWithParam<int> {}; 4762 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeProgressiveTestInstantiation, 4763 DecodeProgressiveTest, 4764 ::testing::Range(0, 8)); 4765 TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { 4766 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 4767 const int params = GetParam(); 4768 bool single_group = ((params & 1) != 0); 4769 bool lossless = (((params >> 1) & 1) != 0); 4770 uint32_t num_channels = 3 + ((params >> 2) & 1); 4771 bool has_alpha = ((num_channels & 1) == 0); 4772 std::set<JxlProgressiveDetail> progressive_details = {kDC, kLastPasses, 4773 kPasses}; 4774 for (auto prog_detail : progressive_details) { 4775 // Only few combinations are expected to support outputting 4776 // intermediate flushes for complete DC and complete passes. 4777 // The test can be updated if more cases are expected to support it. 4778 bool expect_flush = !has_alpha && !lossless; 4779 size_t xsize; 4780 size_t ysize; 4781 if (single_group) { 4782 // An image smaller than 256x256 ensures it contains only 1 group. 4783 xsize = 99; 4784 ysize = 100; 4785 } else { 4786 xsize = 277; 4787 ysize = 280; 4788 } 4789 std::vector<uint8_t> pixels = 4790 jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); 4791 JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 4792 jxl::ColorEncoding color_encoding = jxl::ColorEncoding::SRGB(false); 4793 jxl::CodecInOut io{memory_manager}; 4794 EXPECT_TRUE(jxl::ConvertFromExternal( 4795 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, color_encoding, 4796 /*bits_per_sample=*/16, format, 4797 /*pool=*/nullptr, &io.Main())); 4798 jxl::TestCodestreamParams params; 4799 if (lossless) { 4800 params.cparams.SetLossless(); 4801 } else { 4802 params.cparams.butteraugli_distance = 0.5f; 4803 } 4804 jxl::PassDefinition passes[] = { 4805 {2, 0, 4}, {4, 0, 4}, {8, 2, 2}, {8, 1, 2}, {8, 0, 1}}; 4806 const int kNumPasses = 5; 4807 jxl::ProgressiveMode progressive_mode{passes}; 4808 params.cparams.custom_progressive_mode = &progressive_mode; 4809 std::vector<uint8_t> data = 4810 jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), 4811 xsize, ysize, num_channels, params); 4812 4813 for (size_t increment : {static_cast<size_t>(1), data.size()}) { 4814 printf( 4815 "Testing with single_group=%s, lossless=%s, " 4816 "num_channels=%d, prog_detail=%d, increment=%d\n", 4817 BoolToCStr(single_group), BoolToCStr(lossless), 4818 static_cast<int>(num_channels), static_cast<int>(prog_detail), 4819 static_cast<int>(increment)); 4820 std::vector<std::vector<uint8_t>> passes(kNumPasses + 1); 4821 for (int i = 0; i <= kNumPasses; ++i) { 4822 passes[i].resize(pixels.size()); 4823 } 4824 4825 JxlDecoder* dec = JxlDecoderCreate(nullptr); 4826 4827 EXPECT_EQ(JXL_DEC_SUCCESS, 4828 JxlDecoderSubscribeEvents( 4829 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | 4830 JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION)); 4831 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kFrames)); 4832 EXPECT_EQ(JXL_DEC_ERROR, 4833 JxlDecoderSetProgressiveDetail(dec, kDCProgressive)); 4834 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kDCGroups)); 4835 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kGroups)); 4836 EXPECT_EQ(JXL_DEC_SUCCESS, 4837 JxlDecoderSetProgressiveDetail(dec, prog_detail)); 4838 4839 uint8_t* next_in = data.data(); 4840 size_t avail_in = 0; 4841 size_t pos = 0; 4842 4843 auto process_input = [&]() { 4844 for (;;) { 4845 EXPECT_EQ(JXL_DEC_SUCCESS, 4846 JxlDecoderSetInput(dec, next_in, avail_in)); 4847 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 4848 size_t remaining = JxlDecoderReleaseInput(dec); 4849 EXPECT_LE(remaining, avail_in); 4850 next_in += avail_in - remaining; 4851 avail_in = remaining; 4852 if (status == JXL_DEC_NEED_MORE_INPUT && pos < data.size()) { 4853 size_t chunk = std::min<size_t>(increment, data.size() - pos); 4854 pos += chunk; 4855 avail_in += chunk; 4856 continue; 4857 } 4858 return status; 4859 } 4860 }; 4861 4862 EXPECT_EQ(JXL_DEC_BASIC_INFO, process_input()); 4863 JxlBasicInfo info; 4864 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 4865 EXPECT_EQ(info.xsize, xsize); 4866 EXPECT_EQ(info.ysize, ysize); 4867 4868 EXPECT_EQ(JXL_DEC_FRAME, process_input()); 4869 4870 size_t buffer_size; 4871 EXPECT_EQ(JXL_DEC_SUCCESS, 4872 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 4873 EXPECT_EQ(pixels.size(), buffer_size); 4874 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 4875 dec, &format, passes[kNumPasses].data(), 4876 passes[kNumPasses].size())); 4877 4878 auto next_pass = [&](int pass) { 4879 if (prog_detail <= kDC) return kNumPasses; 4880 if (prog_detail <= kLastPasses) { 4881 return std::min(pass + 2, kNumPasses); 4882 } 4883 return pass + 1; 4884 }; 4885 4886 if (expect_flush) { 4887 // Return a particular downsampling ratio only after the last 4888 // pass for that downsampling was processed. 4889 int expected_downsampling_ratios[] = {8, 8, 4, 4, 2}; 4890 for (int p = 0; p < kNumPasses; p = next_pass(p)) { 4891 EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, process_input()); 4892 EXPECT_EQ(expected_downsampling_ratios[p], 4893 JxlDecoderGetIntendedDownsamplingRatio(dec)); 4894 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec)); 4895 passes[p] = passes[kNumPasses]; 4896 } 4897 } 4898 4899 EXPECT_EQ(JXL_DEC_FULL_IMAGE, process_input()); 4900 EXPECT_EQ(JXL_DEC_SUCCESS, process_input()); 4901 4902 JxlDecoderDestroy(dec); 4903 4904 if (!expect_flush) { 4905 continue; 4906 } 4907 jxl::ButteraugliParams butteraugli_params; 4908 std::vector<float> distances(kNumPasses + 1); 4909 for (int p = 0;; p = next_pass(p)) { 4910 jxl::CodecInOut io1{memory_manager}; 4911 EXPECT_TRUE(jxl::ConvertFromExternal( 4912 jxl::Bytes(passes[p].data(), passes[p].size()), xsize, ysize, 4913 color_encoding, 4914 /*bits_per_sample=*/16, format, 4915 /*pool=*/nullptr, &io1.Main())); 4916 distances[p] = 4917 ButteraugliDistance(io.frames, io1.frames, butteraugli_params, 4918 *JxlGetDefaultCms(), nullptr, nullptr); 4919 if (p == kNumPasses) break; 4920 } 4921 const float kMaxDistance[kNumPasses + 1] = {30.0f, 20.0f, 10.0f, 4922 5.0f, 3.0f, 2.0f}; 4923 EXPECT_LT(distances[kNumPasses], kMaxDistance[kNumPasses]); 4924 for (int p = 0; p < kNumPasses;) { 4925 int next_p = next_pass(p); 4926 EXPECT_LT(distances[p], kMaxDistance[p]); 4927 // Verify that the returned pass image is actually not the 4928 // same as the next pass image, by checking that it has a bit 4929 // worse butteraugli score. 4930 EXPECT_LT(distances[next_p] * 1.1f, distances[p]); 4931 p = next_p; 4932 } 4933 } 4934 } 4935 } 4936 4937 void VerifyJPEGReconstruction(jxl::Span<const uint8_t> container, 4938 jxl::Span<const uint8_t> jpeg_bytes) { 4939 JxlDecoderPtr dec = JxlDecoderMake(nullptr); 4940 EXPECT_EQ(JXL_DEC_SUCCESS, 4941 JxlDecoderSubscribeEvents( 4942 dec.get(), JXL_DEC_JPEG_RECONSTRUCTION | JXL_DEC_FULL_IMAGE)); 4943 JxlDecoderSetInput(dec.get(), container.data(), container.size()); 4944 EXPECT_EQ(JXL_DEC_JPEG_RECONSTRUCTION, JxlDecoderProcessInput(dec.get())); 4945 std::vector<uint8_t> reconstructed_buffer(128); 4946 EXPECT_EQ(JXL_DEC_SUCCESS, 4947 JxlDecoderSetJPEGBuffer(dec.get(), reconstructed_buffer.data(), 4948 reconstructed_buffer.size())); 4949 size_t used = 0; 4950 JxlDecoderStatus process_result = JXL_DEC_JPEG_NEED_MORE_OUTPUT; 4951 while (process_result == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { 4952 used = reconstructed_buffer.size() - JxlDecoderReleaseJPEGBuffer(dec.get()); 4953 reconstructed_buffer.resize(reconstructed_buffer.size() * 2); 4954 EXPECT_EQ( 4955 JXL_DEC_SUCCESS, 4956 JxlDecoderSetJPEGBuffer(dec.get(), reconstructed_buffer.data() + used, 4957 reconstructed_buffer.size() - used)); 4958 process_result = JxlDecoderProcessInput(dec.get()); 4959 } 4960 ASSERT_EQ(JXL_DEC_FULL_IMAGE, process_result); 4961 used = reconstructed_buffer.size() - JxlDecoderReleaseJPEGBuffer(dec.get()); 4962 ASSERT_EQ(used, jpeg_bytes.size()); 4963 EXPECT_EQ(0, memcmp(reconstructed_buffer.data(), jpeg_bytes.data(), used)); 4964 } 4965 4966 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructTestCodestream) { 4967 TEST_LIBJPEG_SUPPORT(); 4968 size_t xsize = 123; 4969 size_t ysize = 77; 4970 size_t channels = 3; 4971 std::vector<uint8_t> pixels = 4972 jxl::test::GetSomeTestImage(xsize, ysize, channels, /*seed=*/0); 4973 std::vector<uint8_t> jpeg_codestream; 4974 jxl::TestCodestreamParams params; 4975 params.cparams.color_transform = jxl::ColorTransform::kNone; 4976 params.box_format = kCSBF_Single; 4977 params.jpeg_codestream = &jpeg_codestream; 4978 params.preview_mode = jxl::kSmallPreview; 4979 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 4980 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, channels, params); 4981 VerifyJPEGReconstruction(jxl::Bytes(compressed), jxl::Bytes(jpeg_codestream)); 4982 } 4983 4984 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructionTest) { 4985 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 4986 const std::string jpeg_path = "jxl/flower/flower.png.im_q85_420.jpg"; 4987 const std::vector<uint8_t> orig = jxl::test::ReadTestData(jpeg_path); 4988 jxl::CodecInOut orig_io{memory_manager}; 4989 ASSERT_TRUE(jxl::jpeg::DecodeImageJPG(jxl::Bytes(orig), &orig_io)); 4990 jxl::jpeg::JPEGData jpeg_data_copy = *orig_io.Main().jpeg_data; 4991 orig_io.metadata.m.xyb_encoded = false; 4992 jxl::BitWriter writer{memory_manager}; 4993 ASSERT_TRUE(WriteCodestreamHeaders(&orig_io.metadata, &writer, nullptr)); 4994 writer.ZeroPadToByte(); 4995 jxl::CompressParams cparams; 4996 cparams.color_transform = jxl::ColorTransform::kNone; 4997 ASSERT_TRUE(jxl::EncodeFrame(memory_manager, cparams, jxl::FrameInfo{}, 4998 &orig_io.metadata, orig_io.Main(), 4999 *JxlGetDefaultCms(), 5000 /*pool=*/nullptr, &writer, 5001 /*aux_out=*/nullptr)); 5002 5003 std::vector<uint8_t> jpeg_data; 5004 ASSERT_TRUE( 5005 EncodeJPEGData(memory_manager, jpeg_data_copy, &jpeg_data, cparams)); 5006 std::vector<uint8_t> container; 5007 jxl::Bytes(jxl::kContainerHeader).AppendTo(container); 5008 jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, 5009 &container); 5010 jxl::Bytes(jpeg_data).AppendTo(container); 5011 jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), 0, true, &container); 5012 jxl::PaddedBytes codestream = std::move(writer).TakeBytes(); 5013 jxl::Bytes(codestream).AppendTo(container); 5014 VerifyJPEGReconstruction(jxl::Bytes(container), jxl::Bytes(orig)); 5015 } 5016 5017 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructionMetadataTest) { 5018 const std::string jpeg_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jpg"; 5019 const std::string jxl_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jxl"; 5020 const std::vector<uint8_t> jpeg = jxl::test::ReadTestData(jpeg_path); 5021 const std::vector<uint8_t> jxl = jxl::test::ReadTestData(jxl_path); 5022 VerifyJPEGReconstruction(jxl::Bytes(jxl), jxl::Bytes(jpeg)); 5023 } 5024 5025 TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) { 5026 size_t xsize = 80; 5027 size_t ysize = 90; 5028 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 5029 jxl::TestCodestreamParams params; 5030 params.box_format = kCSBF_Multi_Other_Terminated; 5031 params.add_icc_profile = true; 5032 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 5033 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 5034 StreamPositions streampos; 5035 AnalyzeCodestream(compressed, &streampos); 5036 5037 // The non-essential final box size including 8-byte header 5038 size_t final_box_size = unk3_box_size + 8; 5039 size_t last_box_begin = compressed.size() - final_box_size; 5040 // Verify that the test is indeed setup correctly to be at the beginning of 5041 // the 'unkn' box header. 5042 ASSERT_EQ(compressed[last_box_begin + 3], final_box_size); 5043 ASSERT_EQ(compressed[last_box_begin + 4], 'u'); 5044 ASSERT_EQ(compressed[last_box_begin + 5], 'n'); 5045 ASSERT_EQ(compressed[last_box_begin + 6], 'k'); 5046 ASSERT_EQ(compressed[last_box_begin + 7], '3'); 5047 5048 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5049 5050 EXPECT_EQ(JXL_DEC_SUCCESS, 5051 JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME)); 5052 5053 EXPECT_EQ(JXL_DEC_SUCCESS, 5054 JxlDecoderSetInput(dec, compressed.data(), last_box_begin)); 5055 5056 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 5057 EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); 5058 // The decoder returns success despite not having seen the final unknown box 5059 // yet. This is because calling JxlDecoderCloseInput is not mandatory for 5060 // backwards compatibility, so it doesn't know more bytes follow, the current 5061 // bytes ended at a perfectly valid place. 5062 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 5063 5064 size_t remaining = JxlDecoderReleaseInput(dec); 5065 // Since the test was set up to end exactly at the boundary of the final 5066 // codestream box, and the decoder returned success, all bytes are expected to 5067 // be consumed until the end of the frame header. 5068 EXPECT_EQ(remaining, last_box_begin - streampos.frames[0].toc_end); 5069 5070 // Now set the remaining non-codestream box as input. 5071 EXPECT_EQ(JXL_DEC_SUCCESS, 5072 JxlDecoderSetInput(dec, compressed.data() + last_box_begin, 5073 compressed.size() - last_box_begin)); 5074 // Even though JxlDecoderProcessInput already returned JXL_DEC_SUCCESS before, 5075 // when calling it again now after setting more input, success is expected, no 5076 // event occurs but the box has been successfully skipped. 5077 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 5078 5079 JxlDecoderDestroy(dec); 5080 } 5081 5082 namespace { 5083 bool BoxTypeEquals(const std::string& type_string, const JxlBoxType type) { 5084 return type_string.size() == 4 && type_string[0] == type[0] && 5085 type_string[1] == type[1] && type_string[2] == type[2] && 5086 type_string[3] == type[3]; 5087 } 5088 } // namespace 5089 5090 TEST(DecodeTest, ExtendedBoxSizeTest) { 5091 const std::string jxl_path = "jxl/boxes/square-extended-size-container.jxl"; 5092 const std::vector<uint8_t> orig = jxl::test::ReadTestData(jxl_path); 5093 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5094 5095 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX)); 5096 5097 JxlBoxType type; 5098 uint64_t box_size; 5099 uint64_t contents_size; 5100 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, orig.data(), orig.size())); 5101 EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); 5102 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5103 EXPECT_TRUE(BoxTypeEquals("JXL ", type)); 5104 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); 5105 EXPECT_EQ(12, box_size); 5106 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); 5107 EXPECT_EQ(contents_size + 8, box_size); 5108 EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); 5109 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5110 EXPECT_TRUE(BoxTypeEquals("ftyp", type)); 5111 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); 5112 EXPECT_EQ(20, box_size); 5113 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); 5114 EXPECT_EQ(contents_size + 8, box_size); 5115 EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); 5116 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5117 EXPECT_TRUE(BoxTypeEquals("jxlc", type)); 5118 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); 5119 EXPECT_EQ(72, box_size); 5120 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); 5121 // This is an extended box, hence the difference between `box_size` and 5122 // `contents_size` is 16. 5123 EXPECT_EQ(contents_size + 8 + 8, box_size); 5124 5125 JxlDecoderDestroy(dec); 5126 } 5127 5128 JXL_BOXES_TEST(DecodeTest, BoxTest) { 5129 size_t xsize = 1; 5130 size_t ysize = 1; 5131 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 5132 jxl::TestCodestreamParams params; 5133 params.box_format = kCSBF_Multi_Other_Terminated; 5134 params.add_icc_profile = true; 5135 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 5136 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 5137 5138 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5139 5140 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX)); 5141 5142 std::vector<std::string> expected_box_types = { 5143 "JXL ", "ftyp", "jxlp", "unk1", "unk2", "jxlp", "jxlp", "jxlp", "unk3"}; 5144 5145 // Value 0 means to not test the size: codestream is not required to be a 5146 // particular exact size. 5147 std::vector<size_t> expected_box_sizes = {12, 20, 0, 34, 18, 0, 0, 0, 20}; 5148 5149 JxlBoxType type; 5150 uint64_t box_size; 5151 uint64_t contents_size; 5152 std::vector<uint8_t> contents(50); 5153 size_t expected_release_size = 0; 5154 5155 // Cannot get these when decoding didn't start yet 5156 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5157 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetBoxSizeRaw(dec, &box_size)); 5158 5159 uint8_t* next_in = compressed.data(); 5160 size_t avail_in = compressed.size(); 5161 for (size_t i = 0; i < expected_box_types.size(); i++) { 5162 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 5163 EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); 5164 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5165 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); 5166 EXPECT_TRUE(BoxTypeEquals(expected_box_types[i], type)); 5167 if (expected_box_sizes[i]) { 5168 EXPECT_EQ(expected_box_sizes[i], box_size); 5169 EXPECT_EQ(JXL_DEC_SUCCESS, 5170 JxlDecoderGetBoxSizeContents(dec, &contents_size)); 5171 EXPECT_EQ(contents_size + 8, box_size); 5172 } 5173 5174 if (expected_release_size > 0) { 5175 EXPECT_EQ(expected_release_size, JxlDecoderReleaseBoxBuffer(dec)); 5176 expected_release_size = 0; 5177 } 5178 5179 if (type[0] == 'u' && type[1] == 'n' && type[2] == 'k') { 5180 JxlDecoderSetBoxBuffer(dec, contents.data(), contents.size()); 5181 size_t expected_box_contents_size = 5182 type[3] == '1' ? unk1_box_size 5183 : (type[3] == '2' ? unk2_box_size : unk3_box_size); 5184 expected_release_size = contents.size() - expected_box_contents_size; 5185 } 5186 size_t consumed = avail_in - JxlDecoderReleaseInput(dec); 5187 next_in += consumed; 5188 avail_in -= consumed; 5189 } 5190 5191 // After the last DEC_BOX event, check that the input position is exactly at 5192 // the stat of the box header. 5193 EXPECT_EQ(avail_in, expected_box_sizes.back()); 5194 5195 // Even though all input is given, the decoder cannot assume there aren't 5196 // more boxes if the input was not closed. 5197 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); 5198 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec)); 5199 JxlDecoderCloseInput(dec); 5200 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 5201 5202 JxlDecoderDestroy(dec); 5203 } 5204 5205 JXL_BOXES_TEST(DecodeTest, ExifBrobBoxTest) { 5206 size_t xsize = 1; 5207 size_t ysize = 1; 5208 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 5209 jxl::TestCodestreamParams params; 5210 // Lossless to verify pixels exactly after roundtrip. 5211 params.cparams.SetLossless(); 5212 params.box_format = kCSBF_Brob_Exif; 5213 params.add_icc_profile = true; 5214 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 5215 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 5216 5217 // Test raw brob box, not brotli-decompressing 5218 for (int streaming = 0; streaming < 2; ++streaming) { 5219 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5220 5221 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX)); 5222 if (!streaming) { 5223 EXPECT_EQ(JXL_DEC_SUCCESS, 5224 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 5225 JxlDecoderCloseInput(dec); 5226 } 5227 // for streaming input case 5228 const uint8_t* next_in = compressed.data(); 5229 size_t avail_in = 0; 5230 size_t total_in = 0; 5231 size_t step_size = 64; 5232 5233 std::vector<uint8_t> box_buffer; 5234 size_t box_num_output; 5235 bool seen_brob_begin = false; 5236 bool seen_brob_end = false; 5237 5238 for (;;) { 5239 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 5240 if (status == JXL_DEC_NEED_MORE_INPUT) { 5241 if (streaming) { 5242 size_t remaining = JxlDecoderReleaseInput(dec); 5243 EXPECT_LE(remaining, avail_in); 5244 next_in += avail_in - remaining; 5245 avail_in = remaining; 5246 size_t amount = step_size; 5247 if (total_in + amount > compressed.size()) { 5248 amount = compressed.size() - total_in; 5249 } 5250 avail_in += amount; 5251 total_in += amount; 5252 EXPECT_EQ(JXL_DEC_SUCCESS, 5253 JxlDecoderSetInput(dec, next_in, avail_in)); 5254 if (total_in == compressed.size()) JxlDecoderCloseInput(dec); 5255 } else { 5256 FAIL(); 5257 break; 5258 } 5259 } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) { 5260 if (!box_buffer.empty()) { 5261 EXPECT_EQ(false, seen_brob_end); 5262 seen_brob_end = true; 5263 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5264 box_num_output = box_buffer.size() - remaining; 5265 EXPECT_EQ(box_num_output, box_brob_exif_size - 8); 5266 EXPECT_EQ( 5267 0, memcmp(box_buffer.data(), box_brob_exif + 8, box_num_output)); 5268 box_buffer.clear(); 5269 } 5270 if (status == JXL_DEC_SUCCESS) break; 5271 JxlBoxType type; 5272 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5273 if (BoxTypeEquals("brob", type)) { 5274 EXPECT_EQ(false, seen_brob_begin); 5275 seen_brob_begin = true; 5276 box_buffer.resize(8); 5277 JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size()); 5278 } 5279 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 5280 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5281 box_num_output = box_buffer.size() - remaining; 5282 box_buffer.resize(box_buffer.size() * 2); 5283 JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output, 5284 box_buffer.size() - box_num_output); 5285 } else { 5286 // We do not expect any other events or errors 5287 FAIL(); 5288 break; 5289 } 5290 } 5291 5292 EXPECT_EQ(true, seen_brob_begin); 5293 EXPECT_EQ(true, seen_brob_end); 5294 5295 JxlDecoderDestroy(dec); 5296 } 5297 5298 // Test decompressed brob box 5299 for (int streaming = 0; streaming < 2; ++streaming) { 5300 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5301 5302 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX)); 5303 if (!streaming) { 5304 EXPECT_EQ(JXL_DEC_SUCCESS, 5305 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 5306 JxlDecoderCloseInput(dec); 5307 } 5308 // for streaming input case 5309 const uint8_t* next_in = compressed.data(); 5310 size_t avail_in = 0; 5311 size_t total_in = 0; 5312 size_t step_size = 64; 5313 5314 std::vector<uint8_t> box_buffer; 5315 size_t box_num_output; 5316 bool seen_exif_begin = false; 5317 bool seen_exif_end = false; 5318 5319 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetDecompressBoxes(dec, JXL_TRUE)); 5320 5321 for (;;) { 5322 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 5323 if (status == JXL_DEC_NEED_MORE_INPUT) { 5324 if (streaming) { 5325 size_t remaining = JxlDecoderReleaseInput(dec); 5326 EXPECT_LE(remaining, avail_in); 5327 next_in += avail_in - remaining; 5328 avail_in = remaining; 5329 size_t amount = step_size; 5330 if (total_in + amount > compressed.size()) { 5331 amount = compressed.size() - total_in; 5332 } 5333 avail_in += amount; 5334 total_in += amount; 5335 EXPECT_EQ(JXL_DEC_SUCCESS, 5336 JxlDecoderSetInput(dec, next_in, avail_in)); 5337 if (total_in == compressed.size()) JxlDecoderCloseInput(dec); 5338 } else { 5339 FAIL(); 5340 break; 5341 } 5342 } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) { 5343 if (!box_buffer.empty()) { 5344 EXPECT_EQ(false, seen_exif_end); 5345 seen_exif_end = true; 5346 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5347 box_num_output = box_buffer.size() - remaining; 5348 // Expect that the output has the same size and contents as the 5349 // uncompressed exif data. Only check contents if the sizes match to 5350 // avoid comparing uninitialized memory in the test. 5351 EXPECT_EQ(box_num_output, exif_uncompressed_size); 5352 if (box_num_output == exif_uncompressed_size) { 5353 EXPECT_EQ(0, memcmp(box_buffer.data(), exif_uncompressed, 5354 exif_uncompressed_size)); 5355 } 5356 box_buffer.clear(); 5357 } 5358 if (status == JXL_DEC_SUCCESS) break; 5359 JxlBoxType type; 5360 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_TRUE)); 5361 if (BoxTypeEquals("Exif", type)) { 5362 EXPECT_EQ(false, seen_exif_begin); 5363 seen_exif_begin = true; 5364 box_buffer.resize(8); 5365 JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size()); 5366 } 5367 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 5368 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5369 box_num_output = box_buffer.size() - remaining; 5370 box_buffer.resize(box_buffer.size() * 2); 5371 JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output, 5372 box_buffer.size() - box_num_output); 5373 } else { 5374 // We do not expect any other events or errors 5375 FAIL(); 5376 break; 5377 } 5378 } 5379 5380 EXPECT_EQ(true, seen_exif_begin); 5381 EXPECT_EQ(true, seen_exif_end); 5382 5383 JxlDecoderDestroy(dec); 5384 } 5385 } 5386 5387 JXL_BOXES_TEST(DecodeTest, PartialCodestreamBoxTest) { 5388 size_t xsize = 23; 5389 size_t ysize = 81; 5390 std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); 5391 JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; 5392 // Lossless to verify pixels exactly after roundtrip. 5393 jxl::TestCodestreamParams params; 5394 params.cparams.SetLossless(); 5395 params.cparams.speed_tier = jxl::SpeedTier::kThunder; 5396 params.box_format = kCSBF_Multi; 5397 params.add_icc_profile = true; 5398 std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( 5399 jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params); 5400 5401 std::vector<uint8_t> extracted_codestream; 5402 5403 { 5404 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5405 5406 EXPECT_EQ(JXL_DEC_SUCCESS, 5407 JxlDecoderSubscribeEvents( 5408 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX)); 5409 EXPECT_EQ(JXL_DEC_SUCCESS, 5410 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 5411 JxlDecoderCloseInput(dec); 5412 5413 size_t num_jxlp = 0; 5414 5415 std::vector<uint8_t> pixels2; 5416 pixels2.resize(pixels.size()); 5417 5418 std::vector<uint8_t> box_buffer; 5419 size_t box_num_output; 5420 5421 for (;;) { 5422 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 5423 if (status == JXL_DEC_NEED_MORE_INPUT) { 5424 FAIL(); 5425 break; 5426 } else if (status == JXL_DEC_BASIC_INFO) { 5427 JxlBasicInfo info; 5428 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 5429 EXPECT_EQ(info.xsize, xsize); 5430 EXPECT_EQ(info.ysize, ysize); 5431 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 5432 EXPECT_EQ(JXL_DEC_SUCCESS, 5433 JxlDecoderSetImageOutBuffer(dec, &format_orig, pixels2.data(), 5434 pixels2.size())); 5435 } else if (status == JXL_DEC_FULL_IMAGE) { 5436 continue; 5437 } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) { 5438 if (!box_buffer.empty()) { 5439 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5440 box_num_output = box_buffer.size() - remaining; 5441 EXPECT_GE(box_num_output, 4); 5442 // Do not insert the first 4 bytes, which are not part of the 5443 // codestream, but the partial codestream box index 5444 extracted_codestream.insert(extracted_codestream.end(), 5445 box_buffer.begin() + 4, 5446 box_buffer.begin() + box_num_output); 5447 box_buffer.clear(); 5448 } 5449 if (status == JXL_DEC_SUCCESS) break; 5450 JxlBoxType type; 5451 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); 5452 if (BoxTypeEquals("jxlp", type)) { 5453 num_jxlp++; 5454 box_buffer.resize(8); 5455 JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size()); 5456 } 5457 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 5458 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5459 box_num_output = box_buffer.size() - remaining; 5460 box_buffer.resize(box_buffer.size() * 2); 5461 JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output, 5462 box_buffer.size() - box_num_output); 5463 } else { 5464 // We do not expect any other events or errors 5465 FAIL(); 5466 break; 5467 } 5468 } 5469 5470 // The test file created with kCSBF_Multi is expected to have 4 jxlp boxes. 5471 EXPECT_EQ(4, num_jxlp); 5472 5473 EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 5474 ysize, format_orig, format_orig)); 5475 5476 JxlDecoderDestroy(dec); 5477 } 5478 5479 // Now test whether the codestream extracted from the jxlp boxes can itself 5480 // also be decoded and gives the same pixels 5481 { 5482 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5483 5484 EXPECT_EQ(JXL_DEC_SUCCESS, 5485 JxlDecoderSubscribeEvents( 5486 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX)); 5487 EXPECT_EQ(JXL_DEC_SUCCESS, 5488 JxlDecoderSetInput(dec, extracted_codestream.data(), 5489 extracted_codestream.size())); 5490 JxlDecoderCloseInput(dec); 5491 5492 size_t num_boxes = 0; 5493 5494 std::vector<uint8_t> pixels2; 5495 pixels2.resize(pixels.size()); 5496 5497 std::vector<uint8_t> box_buffer; 5498 size_t box_num_output; 5499 5500 for (;;) { 5501 JxlDecoderStatus status = JxlDecoderProcessInput(dec); 5502 if (status == JXL_DEC_NEED_MORE_INPUT) { 5503 FAIL(); 5504 break; 5505 } else if (status == JXL_DEC_BASIC_INFO) { 5506 JxlBasicInfo info; 5507 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); 5508 EXPECT_EQ(info.xsize, xsize); 5509 EXPECT_EQ(info.ysize, ysize); 5510 } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { 5511 EXPECT_EQ(JXL_DEC_SUCCESS, 5512 JxlDecoderSetImageOutBuffer(dec, &format_orig, pixels2.data(), 5513 pixels2.size())); 5514 } else if (status == JXL_DEC_FULL_IMAGE) { 5515 continue; 5516 } else if (status == JXL_DEC_BOX) { 5517 num_boxes++; 5518 } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) { 5519 size_t remaining = JxlDecoderReleaseBoxBuffer(dec); 5520 box_num_output = box_buffer.size() - remaining; 5521 box_buffer.resize(box_buffer.size() * 2); 5522 JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output, 5523 box_buffer.size() - box_num_output); 5524 } else if (status == JXL_DEC_SUCCESS) { 5525 break; 5526 } else { 5527 // We do not expect any other events or errors 5528 FAIL(); 5529 break; 5530 } 5531 } 5532 5533 EXPECT_EQ(0, num_boxes); // The data does not use the container format. 5534 EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, 5535 ysize, format_orig, format_orig)); 5536 5537 JxlDecoderDestroy(dec); 5538 } 5539 } 5540 5541 TEST(DecodeTest, SpotColorTest) { 5542 JxlMemoryManager* memory_manager = jxl::test::MemoryManager(); 5543 jxl::CodecInOut io{memory_manager}; 5544 size_t xsize = 55; 5545 size_t ysize = 257; 5546 io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB(); 5547 JXL_TEST_ASSIGN_OR_DIE(Image3F main, 5548 Image3F::Create(memory_manager, xsize, ysize)); 5549 JXL_TEST_ASSIGN_OR_DIE(ImageF spot, 5550 ImageF::Create(memory_manager, xsize, ysize)); 5551 jxl::ZeroFillImage(&main); 5552 jxl::ZeroFillImage(&spot); 5553 5554 for (size_t y = 0; y < ysize; y++) { 5555 float* JXL_RESTRICT rowm = main.PlaneRow(1, y); 5556 float* JXL_RESTRICT rows = spot.Row(y); 5557 for (size_t x = 0; x < xsize; x++) { 5558 rowm[x] = (x + y) * (1.f / 255.f); 5559 rows[x] = ((x ^ y) & 255) * (1.f / 255.f); 5560 } 5561 } 5562 ASSERT_TRUE( 5563 io.SetFromImage(std::move(main), jxl::ColorEncoding::LinearSRGB())); 5564 jxl::ExtraChannelInfo info; 5565 info.bit_depth.bits_per_sample = 8; 5566 info.dim_shift = 0; 5567 info.type = jxl::ExtraChannel::kSpotColor; 5568 info.spot_color[0] = 0.5f; 5569 info.spot_color[1] = 0.2f; 5570 info.spot_color[2] = 1.f; 5571 info.spot_color[3] = 0.5f; 5572 5573 io.metadata.m.extra_channel_info.push_back(info); 5574 std::vector<ImageF> ec; 5575 ec.push_back(std::move(spot)); 5576 ASSERT_TRUE(io.frames[0].SetExtraChannels(std::move(ec))); 5577 5578 jxl::CompressParams cparams; 5579 cparams.speed_tier = jxl::SpeedTier::kLightning; 5580 cparams.modular_mode = true; 5581 cparams.color_transform = jxl::ColorTransform::kNone; 5582 cparams.butteraugli_distance = 0.f; 5583 5584 std::vector<uint8_t> compressed; 5585 EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); 5586 5587 for (size_t render_spot = 0; render_spot < 2; render_spot++) { 5588 JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; 5589 5590 JxlDecoder* dec = JxlDecoderCreate(nullptr); 5591 5592 EXPECT_EQ(JXL_DEC_SUCCESS, 5593 JxlDecoderSubscribeEvents( 5594 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE)); 5595 if (!render_spot) { 5596 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetRenderSpotcolors(dec, JXL_FALSE)); 5597 } 5598 5599 EXPECT_EQ(JXL_DEC_SUCCESS, 5600 JxlDecoderSetInput(dec, compressed.data(), compressed.size())); 5601 EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); 5602 JxlBasicInfo binfo; 5603 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &binfo)); 5604 EXPECT_EQ(1u, binfo.num_extra_channels); 5605 EXPECT_EQ(xsize, binfo.xsize); 5606 EXPECT_EQ(ysize, binfo.ysize); 5607 5608 JxlExtraChannelInfo extra_info; 5609 EXPECT_EQ(JXL_DEC_SUCCESS, 5610 JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info)); 5611 EXPECT_EQ(static_cast<unsigned int>(jxl::ExtraChannel::kSpotColor), 5612 extra_info.type); 5613 5614 EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); 5615 size_t buffer_size; 5616 EXPECT_EQ(JXL_DEC_SUCCESS, 5617 JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)); 5618 size_t extra_size; 5619 EXPECT_EQ(JXL_DEC_SUCCESS, 5620 JxlDecoderExtraChannelBufferSize(dec, &format, &extra_size, 0)); 5621 5622 std::vector<uint8_t> image(buffer_size); 5623 std::vector<uint8_t> extra(extra_size); 5624 size_t bytes_per_pixel = format.num_channels * 5625 jxl::test::GetDataBits(format.data_type) / 5626 jxl::kBitsPerByte; 5627 size_t stride = bytes_per_pixel * binfo.xsize; 5628 5629 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( 5630 dec, &format, image.data(), image.size())); 5631 EXPECT_EQ(JXL_DEC_SUCCESS, 5632 JxlDecoderSetExtraChannelBuffer(dec, &format, extra.data(), 5633 extra.size(), 0)); 5634 5635 EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); 5636 5637 // After the full image was output, JxlDecoderProcessInput should return 5638 // success to indicate all is done. 5639 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec)); 5640 JxlDecoderDestroy(dec); 5641 5642 for (size_t y = 0; y < ysize; y++) { 5643 uint8_t* JXL_RESTRICT rowm = image.data() + stride * y; 5644 uint8_t* JXL_RESTRICT rows = extra.data() + xsize * y; 5645 for (size_t x = 0; x < xsize; x++) { 5646 if (!render_spot) { 5647 // if spot color isn't rendered, main image should be as we made it 5648 // (red and blue are all zeroes) 5649 5650 EXPECT_EQ(rowm[x * 3 + 0], 0); 5651 EXPECT_EQ(rowm[x * 3 + 1], (x + y > 255 ? 255 : x + y)); 5652 EXPECT_EQ(rowm[x * 3 + 2], 0); 5653 } 5654 if (render_spot) { 5655 // if spot color is rendered, expect red and blue to look like the 5656 // spot color channel 5657 EXPECT_LT(abs(rowm[x * 3 + 0] - (rows[x] * 0.25f)), 1); 5658 EXPECT_LT(abs(rowm[x * 3 + 2] - (rows[x] * 0.5f)), 1); 5659 } 5660 EXPECT_EQ(rows[x], ((x ^ y) & 255)); 5661 } 5662 } 5663 } 5664 } 5665 5666 TEST(DecodeTest, CloseInput) { 5667 std::vector<uint8_t> partial_file = {0xff}; 5668 5669 JxlDecoderPtr dec = JxlDecoderMake(nullptr); 5670 EXPECT_EQ(JXL_DEC_SUCCESS, 5671 JxlDecoderSubscribeEvents(dec.get(), 5672 JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE)); 5673 EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec.get(), partial_file.data(), 5674 partial_file.size())); 5675 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get())); 5676 EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get())); 5677 JxlDecoderCloseInput(dec.get()); 5678 EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderProcessInput(dec.get())); 5679 }