modular_test.cc (19209B)
1 // Copyright (c) the JPEG XL Project Authors. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 #include <jxl/cms.h> 7 #include <jxl/encode.h> 8 #include <jxl/types.h> 9 10 #include <cmath> 11 #include <cstddef> 12 #include <cstdint> 13 #include <sstream> 14 #include <string> 15 #include <utility> 16 #include <vector> 17 18 #include "lib/extras/codec.h" 19 #include "lib/extras/dec/jxl.h" 20 #include "lib/extras/enc/jxl.h" 21 #include "lib/extras/metrics.h" 22 #include "lib/extras/packed_image.h" 23 #include "lib/jxl/base/compiler_specific.h" 24 #include "lib/jxl/base/data_parallel.h" 25 #include "lib/jxl/base/random.h" 26 #include "lib/jxl/base/span.h" 27 #include "lib/jxl/base/status.h" 28 #include "lib/jxl/codec_in_out.h" 29 #include "lib/jxl/color_encoding_internal.h" 30 #include "lib/jxl/dec_bit_reader.h" 31 #include "lib/jxl/enc_aux_out.h" 32 #include "lib/jxl/enc_bit_writer.h" 33 #include "lib/jxl/enc_fields.h" 34 #include "lib/jxl/enc_params.h" 35 #include "lib/jxl/enc_toc.h" 36 #include "lib/jxl/fields.h" 37 #include "lib/jxl/frame_header.h" 38 #include "lib/jxl/headers.h" 39 #include "lib/jxl/image.h" 40 #include "lib/jxl/image_bundle.h" 41 #include "lib/jxl/image_metadata.h" 42 #include "lib/jxl/image_ops.h" 43 #include "lib/jxl/image_test_utils.h" 44 #include "lib/jxl/modular/encoding/enc_encoding.h" 45 #include "lib/jxl/modular/encoding/encoding.h" 46 #include "lib/jxl/modular/modular_image.h" 47 #include "lib/jxl/modular/options.h" 48 #include "lib/jxl/modular/transform/transform.h" 49 #include "lib/jxl/padded_bytes.h" 50 #include "lib/jxl/test_image.h" 51 #include "lib/jxl/test_utils.h" 52 #include "lib/jxl/testing.h" 53 54 namespace jxl { 55 namespace { 56 57 using test::ButteraugliDistance; 58 using test::ReadTestData; 59 using test::Roundtrip; 60 using test::TestImage; 61 62 void TestLosslessGroups(size_t group_size_shift) { 63 const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png"); 64 TestImage t; 65 t.DecodeFromBytes(orig).ClearMetadata(); 66 t.SetDimensions(t.ppf().xsize() / 4, t.ppf().ysize() / 4); 67 68 extras::JXLCompressParams cparams; 69 cparams.distance = 0.0f; 70 cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, group_size_shift); 71 extras::JXLDecompressParams dparams; 72 dparams.accepted_formats = {{3, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0}}; 73 74 extras::PackedPixelFile ppf_out; 75 size_t compressed_size = 76 Roundtrip(t.ppf(), cparams, dparams, nullptr, &ppf_out); 77 EXPECT_LE(compressed_size, 280000u); 78 EXPECT_EQ(0.0f, test::ComputeDistance2(t.ppf(), ppf_out)); 79 } 80 81 TEST(ModularTest, RoundtripLosslessGroups128) { TestLosslessGroups(0); } 82 83 TEST(ModularTest, JXL_TSAN_SLOW_TEST(RoundtripLosslessGroups512)) { 84 TestLosslessGroups(2); 85 } 86 87 TEST(ModularTest, JXL_TSAN_SLOW_TEST(RoundtripLosslessGroups1024)) { 88 TestLosslessGroups(3); 89 } 90 91 TEST(ModularTest, RoundtripLosslessCustomWP_PermuteRCT) { 92 const std::vector<uint8_t> orig = 93 ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); 94 TestImage t; 95 t.DecodeFromBytes(orig).ClearMetadata(); 96 t.SetDimensions(100, 100); 97 98 extras::JXLCompressParams cparams; 99 cparams.distance = 0.0f; 100 // 9 = permute to GBR, to test the special case of permutation-only 101 cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE, 9); 102 cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR, 103 static_cast<int64_t>(Predictor::Weighted)); 104 // slowest speed so different WP modes are tried 105 cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 9); 106 extras::JXLDecompressParams dparams; 107 dparams.accepted_formats = {{3, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0}}; 108 109 extras::PackedPixelFile ppf_out; 110 size_t compressed_size = 111 Roundtrip(t.ppf(), cparams, dparams, nullptr, &ppf_out); 112 EXPECT_LE(compressed_size, 10169u); 113 EXPECT_EQ(0.0f, test::ComputeDistance2(t.ppf(), ppf_out)); 114 } 115 116 TEST(ModularTest, RoundtripLossyDeltaPalette) { 117 const std::vector<uint8_t> orig = 118 ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); 119 CompressParams cparams; 120 cparams.modular_mode = true; 121 cparams.color_transform = jxl::ColorTransform::kNone; 122 cparams.lossy_palette = true; 123 cparams.palette_colors = 0; 124 125 CodecInOut io_out; 126 127 CodecInOut io; 128 ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); 129 io.ShrinkTo(300, 100); 130 131 size_t compressed_size; 132 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); 133 EXPECT_LE(compressed_size, 6800u); 134 EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), 135 *JxlGetDefaultCms(), 136 /*distmap=*/nullptr), 137 IsSlightlyBelow(1.5)); 138 } 139 TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { 140 const std::vector<uint8_t> orig = 141 ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); 142 CompressParams cparams; 143 cparams.SetLossless(); 144 cparams.lossy_palette = true; 145 cparams.palette_colors = 0; 146 cparams.options.predictor = jxl::Predictor::Weighted; 147 148 CodecInOut io_out; 149 150 CodecInOut io; 151 ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); 152 io.ShrinkTo(300, 100); 153 154 size_t compressed_size; 155 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); 156 EXPECT_LE(compressed_size, 7000u); 157 EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), 158 *JxlGetDefaultCms(), 159 /*distmap=*/nullptr), 160 IsSlightlyBelow(10.1)); 161 } 162 163 TEST(ModularTest, RoundtripLossy) { 164 const std::vector<uint8_t> orig = 165 ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); 166 CompressParams cparams; 167 cparams.modular_mode = true; 168 cparams.butteraugli_distance = 2.f; 169 cparams.SetCms(*JxlGetDefaultCms()); 170 171 CodecInOut io_out; 172 173 CodecInOut io; 174 ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); 175 176 size_t compressed_size; 177 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); 178 EXPECT_LE(compressed_size, 30000u); 179 EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), 180 *JxlGetDefaultCms(), 181 /*distmap=*/nullptr), 182 IsSlightlyBelow(2.3)); 183 } 184 185 TEST(ModularTest, RoundtripLossy16) { 186 const std::vector<uint8_t> orig = 187 ReadTestData("external/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png"); 188 CompressParams cparams; 189 cparams.modular_mode = true; 190 cparams.butteraugli_distance = 2.f; 191 192 CodecInOut io_out; 193 194 CodecInOut io; 195 ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); 196 JXL_CHECK(!io.metadata.m.have_preview); 197 JXL_CHECK(io.frames.size() == 1); 198 JXL_CHECK( 199 io.frames[0].TransformTo(ColorEncoding::SRGB(), *JxlGetDefaultCms())); 200 io.metadata.m.color_encoding = ColorEncoding::SRGB(); 201 202 size_t compressed_size; 203 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); 204 EXPECT_LE(compressed_size, 300u); 205 EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), 206 *JxlGetDefaultCms(), 207 /*distmap=*/nullptr), 208 IsSlightlyBelow(1.6)); 209 } 210 211 TEST(ModularTest, RoundtripExtraProperties) { 212 constexpr size_t kSize = 250; 213 JXL_ASSIGN_OR_DIE(Image image, 214 Image::Create(kSize, kSize, /*bitdepth=*/8, 3)); 215 ModularOptions options; 216 options.max_properties = 4; 217 options.predictor = Predictor::Zero; 218 Rng rng(0); 219 for (size_t y = 0; y < kSize; y++) { 220 for (size_t x = 0; x < kSize; x++) { 221 image.channel[0].plane.Row(y)[x] = image.channel[2].plane.Row(y)[x] = 222 rng.UniformU(0, 9); 223 } 224 } 225 ZeroFillImage(&image.channel[1].plane); 226 BitWriter writer; 227 ASSERT_TRUE(ModularGenericCompress(image, options, &writer)); 228 writer.ZeroPadToByte(); 229 JXL_ASSIGN_OR_DIE(Image decoded, Image::Create(kSize, kSize, /*bitdepth=*/8, 230 image.channel.size())); 231 for (size_t i = 0; i < image.channel.size(); i++) { 232 const Channel& ch = image.channel[i]; 233 JXL_ASSIGN_OR_DIE(decoded.channel[i], 234 Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift)); 235 } 236 Status status = true; 237 { 238 BitReader reader(writer.GetSpan()); 239 BitReaderScopedCloser closer(&reader, &status); 240 ASSERT_TRUE(ModularGenericDecompress(&reader, decoded, /*header=*/nullptr, 241 /*group_id=*/0, &options)); 242 } 243 ASSERT_TRUE(status); 244 ASSERT_EQ(image.channel.size(), decoded.channel.size()); 245 for (size_t c = 0; c < image.channel.size(); c++) { 246 for (size_t y = 0; y < image.channel[c].plane.ysize(); y++) { 247 for (size_t x = 0; x < image.channel[c].plane.xsize(); x++) { 248 EXPECT_EQ(image.channel[c].plane.Row(y)[x], 249 decoded.channel[c].plane.Row(y)[x]) 250 << "c = " << c << ", x = " << x << ", y = " << y; 251 } 252 } 253 } 254 } 255 256 struct RoundtripLosslessConfig { 257 int bitdepth; 258 int responsive; 259 }; 260 class ModularTestParam 261 : public ::testing::TestWithParam<RoundtripLosslessConfig> {}; 262 263 std::vector<RoundtripLosslessConfig> GenerateLosslessTests() { 264 std::vector<RoundtripLosslessConfig> all; 265 for (int responsive = 0; responsive <= 1; responsive++) { 266 for (int bitdepth = 1; bitdepth < 32; bitdepth++) { 267 if (responsive && bitdepth > 30) continue; 268 all.push_back({bitdepth, responsive}); 269 } 270 } 271 return all; 272 } 273 std::string LosslessTestDescription( 274 const testing::TestParamInfo<ModularTestParam::ParamType>& info) { 275 std::stringstream name; 276 name << info.param.bitdepth << "bit"; 277 if (info.param.responsive) name << "Squeeze"; 278 return name.str(); 279 } 280 281 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(RoundtripLossless, ModularTestParam, 282 testing::ValuesIn(GenerateLosslessTests()), 283 LosslessTestDescription); 284 285 TEST_P(ModularTestParam, RoundtripLossless) { 286 RoundtripLosslessConfig config = GetParam(); 287 int bitdepth = config.bitdepth; 288 int responsive = config.responsive; 289 290 ThreadPool* pool = nullptr; 291 Rng generator(123); 292 const std::vector<uint8_t> orig = 293 ReadTestData("external/wesaturate/500px/u76c0g_bliznaca_srgb8.png"); 294 CodecInOut io1; 295 ASSERT_TRUE(SetFromBytes(Bytes(orig), &io1, pool)); 296 297 // vary the dimensions a bit, in case of bugs related to 298 // even vs odd width or height. 299 size_t xsize = 423 + bitdepth; 300 size_t ysize = 467 + bitdepth; 301 302 CodecInOut io; 303 io.SetSize(xsize, ysize); 304 io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false); 305 io.metadata.m.SetUintSamples(bitdepth); 306 307 double factor = ((1lu << bitdepth) - 1lu); 308 double ifactor = 1.0 / factor; 309 JXL_ASSIGN_OR_DIE(Image3F noise_added, Image3F::Create(xsize, ysize)); 310 311 for (size_t c = 0; c < 3; c++) { 312 for (size_t y = 0; y < ysize; y++) { 313 const float* in = io1.Main().color()->PlaneRow(c, y); 314 float* out = noise_added.PlaneRow(c, y); 315 for (size_t x = 0; x < xsize; x++) { 316 // make the least significant bits random 317 float f = in[x] + generator.UniformF(0.0f, 1.f / 255.f); 318 if (f > 1.f) f = 1.f; 319 // quantize to the bitdepth we're testing 320 unsigned int u = static_cast<unsigned int>(std::lround(f * factor)); 321 out[x] = u * ifactor; 322 } 323 } 324 } 325 io.SetFromImage(std::move(noise_added), jxl::ColorEncoding::SRGB(false)); 326 327 CompressParams cparams; 328 cparams.modular_mode = true; 329 cparams.color_transform = jxl::ColorTransform::kNone; 330 cparams.butteraugli_distance = 0.f; 331 cparams.options.predictor = {Predictor::Zero}; 332 cparams.speed_tier = SpeedTier::kThunder; 333 cparams.responsive = responsive; 334 CodecInOut io2; 335 size_t compressed_size; 336 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); 337 EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3.0 * 1.1); 338 EXPECT_LE(0, ComputeDistance2(io.Main(), io2.Main(), *JxlGetDefaultCms())); 339 size_t different = 0; 340 for (size_t c = 0; c < 3; c++) { 341 for (size_t y = 0; y < ysize; y++) { 342 const float* in = io.Main().color()->PlaneRow(c, y); 343 const float* out = io2.Main().color()->PlaneRow(c, y); 344 for (size_t x = 0; x < xsize; x++) { 345 uint32_t uin = in[x] * factor + 0.5; 346 uint32_t uout = out[x] * factor + 0.5; 347 // check that the integer values are identical 348 if (uin != uout) different++; 349 } 350 } 351 } 352 EXPECT_EQ(different, 0); 353 } 354 355 TEST(ModularTest, RoundtripLosslessCustomFloat) { 356 CodecInOut io; 357 size_t xsize = 100; 358 size_t ysize = 300; 359 io.SetSize(xsize, ysize); 360 io.metadata.m.bit_depth.bits_per_sample = 18; 361 io.metadata.m.bit_depth.exponent_bits_per_sample = 6; 362 io.metadata.m.bit_depth.floating_point_sample = true; 363 io.metadata.m.modular_16_bit_buffer_sufficient = false; 364 ColorEncoding color_encoding; 365 color_encoding.Tf().SetTransferFunction(TransferFunction::kLinear); 366 color_encoding.SetColorSpace(ColorSpace::kRGB); 367 JXL_ASSIGN_OR_DIE(Image3F testimage, Image3F::Create(xsize, ysize)); 368 float factor = 1.f / (1 << 14); 369 for (size_t c = 0; c < 3; c++) { 370 for (size_t y = 0; y < ysize; y++) { 371 float* const JXL_RESTRICT row = testimage.PlaneRow(c, y); 372 for (size_t x = 0; x < xsize; x++) { 373 row[x] = factor * (x ^ y); 374 } 375 } 376 } 377 io.SetFromImage(std::move(testimage), color_encoding); 378 io.metadata.m.color_encoding = color_encoding; 379 io.metadata.m.SetIntensityTarget(255); 380 381 CompressParams cparams; 382 cparams.modular_mode = true; 383 cparams.color_transform = jxl::ColorTransform::kNone; 384 cparams.butteraugli_distance = 0.f; 385 cparams.options.predictor = {Predictor::Zero}; 386 cparams.speed_tier = SpeedTier::kThunder; 387 cparams.decoding_speed_tier = 2; 388 389 CodecInOut io2; 390 size_t compressed_size; 391 JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); 392 EXPECT_LE(compressed_size, 23000u); 393 JXL_EXPECT_OK(SamePixels(*io.Main().color(), *io2.Main().color(), _)); 394 } 395 396 void WriteHeaders(BitWriter* writer, size_t xsize, size_t ysize) { 397 BitWriter::Allotment allotment(writer, 16); 398 writer->Write(8, 0xFF); 399 writer->Write(8, kCodestreamMarker); 400 allotment.ReclaimAndCharge(writer, 0, nullptr); 401 CodecMetadata metadata; 402 EXPECT_TRUE(metadata.size.Set(xsize, ysize)); 403 EXPECT_TRUE(WriteSizeHeader(metadata.size, writer, 0, nullptr)); 404 metadata.m.color_encoding = ColorEncoding::LinearSRGB(/*is_gray=*/true); 405 metadata.m.xyb_encoded = false; 406 metadata.m.SetUintSamples(31); 407 EXPECT_TRUE(WriteImageMetadata(metadata.m, writer, 0, nullptr)); 408 metadata.transform_data.nonserialized_xyb_encoded = metadata.m.xyb_encoded; 409 EXPECT_TRUE(Bundle::Write(metadata.transform_data, writer, 0, nullptr)); 410 writer->ZeroPadToByte(); 411 FrameHeader frame_header(&metadata); 412 frame_header.encoding = FrameEncoding::kModular; 413 frame_header.loop_filter.gab = false; 414 frame_header.loop_filter.epf_iters = 0; 415 EXPECT_TRUE(WriteFrameHeader(frame_header, writer, nullptr)); 416 } 417 418 // Tree with single node, zero predictor, offset is 1 and multiplier is 1, 419 // entropy code is prefix tree with alphabet size 256 and all bits lengths 8. 420 void WriteHistograms(BitWriter* writer) { 421 writer->Write(1, 1); // default DC quant 422 writer->Write(1, 1); // has_tree 423 // tree histograms 424 writer->Write(1, 0); // LZ77 disabled 425 writer->Write(3, 1); // simple context map 426 writer->Write(1, 1); // prefix code 427 writer->Write(7, 0x63); // UnintConfig(3, 2, 1) 428 writer->Write(12, 0xfef); // alphabet_size = 256 429 writer->Write(32, 0x10003); // all bit lengths 8 430 // tree tokens 431 writer->Write(8, 0); // tree leaf 432 writer->Write(8, 0); // zero predictor 433 writer->Write(8, 64); // offset = UnpackSigned(ReverseBits(64)) = 1 434 writer->Write(16, 0); // multiplier = 1 435 // histograms 436 writer->Write(1, 0); // LZ77 disabled 437 writer->Write(1, 1); // prefix code 438 writer->Write(7, 0x63); // UnintConfig(3, 2, 1) 439 writer->Write(12, 0xfef); // alphabet_size = 256 440 writer->Write(32, 0x10003); // all bit lengths 8 441 } 442 443 TEST(ModularTest, PredictorIntegerOverflow) { 444 const size_t xsize = 1; 445 const size_t ysize = 1; 446 BitWriter writer; 447 WriteHeaders(&writer, xsize, ysize); 448 std::vector<BitWriter> group_codes(1); 449 { 450 BitWriter* bw = group_codes.data(); 451 BitWriter::Allotment allotment(bw, 1 << 20); 452 WriteHistograms(bw); 453 GroupHeader header; 454 header.use_global_tree = true; 455 EXPECT_TRUE(Bundle::Write(header, bw, 0, nullptr)); 456 // After UnpackSigned this becomes (1 << 31) - 1, the largest pixel_type, 457 // and after adding the offset we get -(1 << 31). 458 bw->Write(8, 119); 459 bw->Write(28, 0xfffffff); 460 bw->ZeroPadToByte(); 461 allotment.ReclaimAndCharge(bw, 0, nullptr); 462 } 463 EXPECT_TRUE(WriteGroupOffsets(group_codes, {}, &writer, nullptr)); 464 writer.AppendByteAligned(group_codes); 465 466 PaddedBytes compressed = std::move(writer).TakeBytes(); 467 extras::PackedPixelFile ppf; 468 extras::JXLDecompressParams params; 469 params.accepted_formats.push_back({1, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}); 470 EXPECT_TRUE(DecodeImageJXL(compressed.data(), compressed.size(), params, 471 nullptr, &ppf)); 472 ASSERT_EQ(1, ppf.frames.size()); 473 const auto& img = ppf.frames[0].color; 474 const auto* pixels = reinterpret_cast<const float*>(img.pixels()); 475 EXPECT_EQ(-1.0f, pixels[0]); 476 } 477 478 TEST(ModularTest, UnsqueezeIntegerOverflow) { 479 // Image width is 9 so we can test both the SIMD and non-vector code paths. 480 const size_t xsize = 9; 481 const size_t ysize = 2; 482 BitWriter writer; 483 WriteHeaders(&writer, xsize, ysize); 484 std::vector<BitWriter> group_codes(1); 485 { 486 BitWriter* bw = group_codes.data(); 487 BitWriter::Allotment allotment(bw, 1 << 20); 488 WriteHistograms(bw); 489 GroupHeader header; 490 header.use_global_tree = true; 491 header.transforms.emplace_back(); 492 header.transforms[0].id = TransformId::kSqueeze; 493 SqueezeParams params; 494 params.horizontal = false; 495 params.in_place = true; 496 params.begin_c = 0; 497 params.num_c = 1; 498 header.transforms[0].squeezes.emplace_back(params); 499 EXPECT_TRUE(Bundle::Write(header, bw, 0, nullptr)); 500 for (size_t i = 0; i < xsize * ysize; ++i) { 501 // After UnpackSigned and adding offset, this becomes (1 << 31) - 1, both 502 // in the image and in the residual channels, and unsqueeze makes them 503 // ~(3 << 30) and (1 << 30) (in pixel_type_w) and the first wraps around 504 // to about -(1 << 30). 505 bw->Write(8, 119); 506 bw->Write(28, 0xffffffe); 507 } 508 bw->ZeroPadToByte(); 509 allotment.ReclaimAndCharge(bw, 0, nullptr); 510 } 511 EXPECT_TRUE(WriteGroupOffsets(group_codes, {}, &writer, nullptr)); 512 writer.AppendByteAligned(group_codes); 513 514 PaddedBytes compressed = std::move(writer).TakeBytes(); 515 extras::PackedPixelFile ppf; 516 extras::JXLDecompressParams params; 517 params.accepted_formats.push_back({1, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}); 518 EXPECT_TRUE(DecodeImageJXL(compressed.data(), compressed.size(), params, 519 nullptr, &ppf)); 520 ASSERT_EQ(1, ppf.frames.size()); 521 const auto& img = ppf.frames[0].color; 522 const float* pixels = reinterpret_cast<const float*>(img.pixels()); 523 for (size_t x = 0; x < xsize; ++x) { 524 EXPECT_NEAR(-0.5f, pixels[x], 1e-10); 525 EXPECT_NEAR(0.5f, pixels[xsize + x], 1e-10); 526 } 527 } 528 529 } // namespace 530 } // namespace jxl