libjxl

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

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, &section_offsets,
   3929                                  &section_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 }