libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

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