tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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