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