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