tor-browser

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

decode_test.cc (232012B)


      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/butteraugli/butteraugli.h"
     44 #include "lib/jxl/cms/color_encoding_cms.h"
     45 #include "lib/jxl/color_encoding_internal.h"
     46 #include "lib/jxl/common.h"  // SpeedTier
     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/jpeg/jpeg_data.h"
     67 #include "lib/jxl/padded_bytes.h"
     68 #include "lib/jxl/test_image.h"
     69 #include "lib/jxl/test_memory_manager.h"
     70 #include "lib/jxl/test_utils.h"
     71 #include "lib/jxl/testing.h"
     72 #include "lib/jxl/toc.h"
     73 using ::jxl::test::GetIccTestProfile;
     74 ////////////////////////////////////////////////////////////////////////////////
     75 
     76 namespace {
     77 void AppendU32BE(uint32_t u32, std::vector<uint8_t>* bytes) {
     78  bytes->push_back(u32 >> 24);
     79  bytes->push_back(u32 >> 16);
     80  bytes->push_back(u32 >> 8);
     81  bytes->push_back(u32 >> 0);
     82 }
     83 
     84 // What type of codestream format in the boxes to use for testing
     85 enum CodeStreamBoxFormat {
     86  // Do not use box format at all, only pure codestream
     87  kCSBF_None,
     88  // Have a single codestream box, with its actual size given in the box
     89  kCSBF_Single,
     90  // Have a single codestream box, with box size 0 (final box running to end)
     91  kCSBF_Single_Zero_Terminated,
     92  // Single codestream box, with another unknown box behind it
     93  kCSBF_Single_Other,
     94  // Have multiple partial codestream boxes
     95  kCSBF_Multi,
     96  // Have multiple partial codestream boxes, with final box size 0 (running
     97  // to end)
     98  kCSBF_Multi_Zero_Terminated,
     99  // Have multiple partial codestream boxes, terminated by non-codestream box
    100  kCSBF_Multi_Other_Terminated,
    101  // Have multiple partial codestream boxes, terminated by non-codestream box
    102  // that has its size set to 0 (running to end)
    103  kCSBF_Multi_Other_Zero_Terminated,
    104  // Have multiple partial codestream boxes, and the first one has a content
    105  // of zero length
    106  kCSBF_Multi_First_Empty,
    107  // Have multiple partial codestream boxes, and the last one has a content
    108  // of zero length and there is an unknown empty box at the end
    109  kCSBF_Multi_Last_Empty_Other,
    110  // Have a compressed exif box before a regular codestream box
    111  kCSBF_Brob_Exif,
    112  // Not a value but used for counting amount of enum entries
    113  kCSBF_NUM_ENTRIES,
    114 };
    115 
    116 // Unknown boxes for testing
    117 const char* unk1_box_type = "unk1";
    118 const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz";
    119 const size_t unk1_box_size = strlen(unk1_box_contents);
    120 const char* unk2_box_type = "unk2";
    121 const char* unk2_box_contents = "0123456789";
    122 const size_t unk2_box_size = strlen(unk2_box_contents);
    123 const char* unk3_box_type = "unk3";
    124 const char* unk3_box_contents = "ABCDEF123456";
    125 const size_t unk3_box_size = strlen(unk3_box_contents);
    126 // Box with brob-compressed exif, including header
    127 const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>(
    128    "\0\0\0@brobExif\241\350\2\300\177\244v\2525\304\360\27=?\267{"
    129    "\33\37\314\332\214QX17PT\"\256\0\0\202s\214\313t\333\310\320k\20\276\30"
    130    "\204\277l$\326c#\1\b");
    131 size_t box_brob_exif_size = 64;
    132 // The uncompressed Exif data from the brob box
    133 const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>(
    134    "\0\0\0\0MM\0*"
    135    "\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"
    136    "\0\1\0\0\0R\1("
    137    "\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"
    138    "\0\1\0\0\0\1");
    139 size_t exif_uncompressed_size = 94;
    140 
    141 }  // namespace
    142 
    143 namespace jxl {
    144 namespace {
    145 
    146 void AppendTestBox(const char* type, const char* contents, size_t contents_size,
    147                   bool unbounded, std::vector<uint8_t>* bytes) {
    148  AppendU32BE(contents_size + 8, bytes);
    149  bytes->push_back(type[0]);
    150  bytes->push_back(type[1]);
    151  bytes->push_back(type[2]);
    152  bytes->push_back(type[3]);
    153  const uint8_t* contents_u = reinterpret_cast<const uint8_t*>(contents);
    154  Bytes(contents_u, contents_size).AppendTo(*bytes);
    155 }
    156 
    157 enum PreviewMode {
    158  kNoPreview,
    159  kSmallPreview,
    160  kBigPreview,
    161  kNumPreviewModes,
    162 };
    163 
    164 void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) {
    165  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    166  if (preview_mode == kSmallPreview) {
    167    ASSERT_TRUE(ib->ShrinkTo(ib->xsize() / 7, ib->ysize() / 7));
    168  } else if (preview_mode == kBigPreview) {
    169    auto upsample7 = [&](const ImageF& in, ImageF* out) {
    170      for (size_t y = 0; y < out->ysize(); ++y) {
    171        for (size_t x = 0; x < out->xsize(); ++x) {
    172          out->Row(y)[x] = in.ConstRow(y / 7)[x / 7];
    173        }
    174      }
    175    };
    176    JXL_TEST_ASSIGN_OR_DIE(
    177        Image3F preview,
    178        Image3F::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7));
    179    for (size_t c = 0; c < 3; ++c) {
    180      upsample7(ib->color()->Plane(c), &preview.Plane(c));
    181    }
    182    std::vector<ImageF> extra_channels;
    183    for (size_t i = 0; i < ib->extra_channels().size(); ++i) {
    184      JXL_TEST_ASSIGN_OR_DIE(
    185          ImageF ec,
    186          ImageF::Create(memory_manager, ib->xsize() * 7, ib->ysize() * 7));
    187      upsample7(ib->extra_channels()[i], &ec);
    188      extra_channels.emplace_back(std::move(ec));
    189    }
    190    ib->RemoveColor();
    191    ib->ClearExtraChannels();
    192    ASSERT_TRUE(ib->SetFromImage(std::move(preview), ib->c_current()));
    193    ASSERT_TRUE(ib->SetExtraChannels(std::move(extra_channels)));
    194  }
    195 }
    196 
    197 struct TestCodestreamParams {
    198  CompressParams cparams;
    199  CodeStreamBoxFormat box_format = kCSBF_None;
    200  JxlOrientation orientation = JXL_ORIENT_IDENTITY;
    201  PreviewMode preview_mode = kNoPreview;
    202  bool add_intrinsic_size = false;
    203  bool add_icc_profile = false;
    204  float intensity_target = 0.0;
    205  std::string color_space;
    206  std::vector<uint8_t>* jpeg_codestream = nullptr;
    207 };
    208 
    209 // Input pixels always given as 16-bit RGBA, 8 bytes per pixel.
    210 // include_alpha determines if the encoded image should contain the alpha
    211 // channel.
    212 // add_icc_profile: if false, encodes the image as sRGB using the JXL fields,
    213 // for grayscale or RGB images. If true, encodes the image using the ICC profile
    214 // returned by GetIccTestProfile, without the JXL fields, this requires the
    215 // image is RGB, not grayscale.
    216 // Providing jpeg_codestream will populate the jpeg_codestream with compressed
    217 // JPEG bytes, and make it possible to reconstruct those exact JPEG bytes using
    218 // the return value _if_ add_container indicates a box format.
    219 std::vector<uint8_t> CreateTestJXLCodestream(
    220    Span<const uint8_t> pixels, size_t xsize, size_t ysize, size_t num_channels,
    221    const TestCodestreamParams& params) {
    222  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    223  // Compress the pixels with JPEG XL.
    224  bool grayscale = (num_channels <= 2);
    225  bool have_alpha = ((num_channels & 1) == 0);
    226  bool include_alpha = have_alpha && params.jpeg_codestream == nullptr;
    227  size_t bitdepth = params.jpeg_codestream == nullptr ? 16 : 8;
    228  CodecInOut io{jxl::test::MemoryManager()};
    229  EXPECT_TRUE(io.SetSize(xsize, ysize));
    230  ColorEncoding color_encoding;
    231  if (params.add_icc_profile) {
    232    // the hardcoded ICC profile we attach requires RGB.
    233    EXPECT_EQ(false, grayscale);
    234    EXPECT_TRUE(params.color_space.empty());
    235    EXPECT_TRUE(color_encoding.SetICC(GetIccTestProfile(), JxlGetDefaultCms()));
    236  } else if (!params.color_space.empty()) {
    237    JxlColorEncoding c;
    238    EXPECT_TRUE(jxl::ParseDescription(params.color_space, &c));
    239    EXPECT_TRUE(color_encoding.FromExternal(c));
    240    EXPECT_EQ(color_encoding.IsGray(), grayscale);
    241  } else {
    242    color_encoding = jxl::ColorEncoding::SRGB(/*is_gray=*/grayscale);
    243  }
    244  io.metadata.m.SetUintSamples(bitdepth);
    245  if (include_alpha) {
    246    io.metadata.m.SetAlphaBits(bitdepth);
    247  }
    248  if (params.intensity_target != 0) {
    249    io.metadata.m.SetIntensityTarget(params.intensity_target);
    250  }
    251  JxlPixelFormat format = {static_cast<uint32_t>(num_channels), JXL_TYPE_UINT16,
    252                           JXL_BIG_ENDIAN, 0};
    253  // Make the grayscale-ness of the io metadata color_encoding and the packed
    254  // image match.
    255  io.metadata.m.color_encoding = color_encoding;
    256  EXPECT_TRUE(ConvertFromExternal(pixels, xsize, ysize, color_encoding,
    257                                  /*bits_per_sample=*/16, format,
    258                                  /* pool */ nullptr, &io.Main()));
    259  std::vector<uint8_t> jpeg_data;
    260  if (params.jpeg_codestream != nullptr) {
    261    if (jxl::extras::CanDecode(jxl::extras::Codec::kJPG)) {
    262      std::vector<uint8_t> jpeg_bytes;
    263      extras::PackedPixelFile ppf;
    264      JXL_TEST_ASSIGN_OR_DIE(extras::PackedFrame frame,
    265                             extras::PackedFrame::Create(xsize, ysize, format));
    266      EXPECT_TRUE(frame.color.pixels_size == pixels.size());
    267      memcpy(frame.color.pixels(0, 0, 0), pixels.data(), pixels.size());
    268      ppf.frames.emplace_back(std::move(frame));
    269      ppf.info.xsize = xsize;
    270      ppf.info.ysize = ysize;
    271      ppf.info.num_color_channels = grayscale ? 1 : 3;
    272      ppf.info.bits_per_sample = 16;
    273      auto encoder = extras::GetJPEGEncoder();
    274      encoder->SetOption("quality", "70");
    275      extras::EncodedImage encoded;
    276      EXPECT_TRUE(encoder->Encode(ppf, &encoded, nullptr));
    277      jpeg_bytes = encoded.bitstreams[0];
    278      Bytes(jpeg_bytes).AppendTo(*params.jpeg_codestream);
    279      EXPECT_TRUE(jxl::jpeg::DecodeImageJPG(
    280          jxl::Bytes(jpeg_bytes.data(), jpeg_bytes.size()), &io));
    281      EXPECT_TRUE(EncodeJPEGData(memory_manager, *io.Main().jpeg_data,
    282                                 &jpeg_data, params.cparams));
    283      io.metadata.m.xyb_encoded = false;
    284    } else {
    285      ADD_FAILURE();
    286    }
    287  }
    288  if (params.preview_mode) {
    289    JXL_TEST_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy());
    290    GeneratePreview(params.preview_mode, &io.preview_frame);
    291    io.metadata.m.have_preview = true;
    292    EXPECT_TRUE(io.metadata.m.preview_size.Set(io.preview_frame.xsize(),
    293                                               io.preview_frame.ysize()));
    294  }
    295  if (params.add_intrinsic_size) {
    296    EXPECT_TRUE(io.metadata.m.intrinsic_size.Set(xsize / 3, ysize / 3));
    297  }
    298  io.metadata.m.orientation = params.orientation;
    299  std::vector<uint8_t> compressed;
    300  EXPECT_TRUE(test::EncodeFile(params.cparams, &io, &compressed));
    301  CodeStreamBoxFormat add_container = params.box_format;
    302  if (add_container != kCSBF_None) {
    303    // Header with signature box and ftyp box.
    304    const uint8_t header[] = {0,    0,    0,    0xc,  0x4a, 0x58, 0x4c, 0x20,
    305                              0xd,  0xa,  0x87, 0xa,  0,    0,    0,    0x14,
    306                              0x66, 0x74, 0x79, 0x70, 0x6a, 0x78, 0x6c, 0x20,
    307                              0,    0,    0,    0,    0x6a, 0x78, 0x6c, 0x20};
    308 
    309    bool is_multi = add_container == kCSBF_Multi ||
    310                    add_container == kCSBF_Multi_Zero_Terminated ||
    311                    add_container == kCSBF_Multi_Other_Terminated ||
    312                    add_container == kCSBF_Multi_Other_Zero_Terminated ||
    313                    add_container == kCSBF_Multi_First_Empty ||
    314                    add_container == kCSBF_Multi_Last_Empty_Other;
    315 
    316    if (is_multi) {
    317      size_t third = compressed.size() / 3;
    318      std::vector<uint8_t> compressed0(compressed.data(),
    319                                       compressed.data() + third);
    320      std::vector<uint8_t> compressed1(compressed.data() + third,
    321                                       compressed.data() + 2 * third);
    322      std::vector<uint8_t> compressed2(compressed.data() + 2 * third,
    323                                       compressed.data() + compressed.size());
    324 
    325      std::vector<uint8_t> c;
    326      Bytes(header).AppendTo(c);
    327      if (params.jpeg_codestream != nullptr) {
    328        jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
    329                             &c);
    330        Bytes(jpeg_data).AppendTo(c);
    331      }
    332      uint32_t jxlp_index = 0;
    333      if (add_container == kCSBF_Multi_First_Empty) {
    334        // Empty placeholder codestream part
    335        AppendU32BE(12, &c);
    336        c.push_back('j');
    337        c.push_back('x');
    338        c.push_back('l');
    339        c.push_back('p');
    340        AppendU32BE(jxlp_index++, &c);
    341      }
    342      // First codestream part
    343      AppendU32BE(compressed0.size() + 12, &c);
    344      c.push_back('j');
    345      c.push_back('x');
    346      c.push_back('l');
    347      c.push_back('p');
    348      AppendU32BE(jxlp_index++, &c);
    349      Bytes(compressed0).AppendTo(c);
    350      // A few non-codestream boxes in between
    351      AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, &c);
    352      AppendTestBox(unk2_box_type, unk2_box_contents, unk2_box_size, false, &c);
    353      // Empty placeholder codestream part
    354      AppendU32BE(12, &c);
    355      c.push_back('j');
    356      c.push_back('x');
    357      c.push_back('l');
    358      c.push_back('p');
    359      AppendU32BE(jxlp_index++, &c);
    360      // Second codestream part
    361      AppendU32BE(compressed1.size() + 12, &c);
    362      c.push_back('j');
    363      c.push_back('x');
    364      c.push_back('l');
    365      c.push_back('p');
    366      AppendU32BE(jxlp_index++, &c);
    367      Bytes(compressed1).AppendTo(c);
    368      // Third (last) codestream part
    369      AppendU32BE(add_container == kCSBF_Multi_Zero_Terminated
    370                      ? 0
    371                      : (compressed2.size() + 12),
    372                  &c);
    373      c.push_back('j');
    374      c.push_back('x');
    375      c.push_back('l');
    376      c.push_back('p');
    377      if (add_container != kCSBF_Multi_Last_Empty_Other) {
    378        AppendU32BE(jxlp_index++ | 0x80000000, &c);
    379      } else {
    380        AppendU32BE(jxlp_index++, &c);
    381      }
    382      Bytes(compressed2).AppendTo(c);
    383      if (add_container == kCSBF_Multi_Last_Empty_Other) {
    384        // Empty placeholder codestream part
    385        AppendU32BE(12, &c);
    386        c.push_back('j');
    387        c.push_back('x');
    388        c.push_back('l');
    389        c.push_back('p');
    390        AppendU32BE(jxlp_index++ | 0x80000000, &c);
    391        AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, false,
    392                      &c);
    393      }
    394      if (add_container == kCSBF_Multi_Other_Terminated) {
    395        AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, false,
    396                      &c);
    397      }
    398      if (add_container == kCSBF_Multi_Other_Zero_Terminated) {
    399        AppendTestBox(unk3_box_type, unk3_box_contents, unk3_box_size, true,
    400                      &c);
    401      }
    402      compressed.swap(c);
    403    } else {
    404      std::vector<uint8_t> c;
    405      Bytes(header).AppendTo(c);
    406      if (params.jpeg_codestream != nullptr) {
    407        jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
    408                             &c);
    409        Bytes(jpeg_data).AppendTo(c);
    410      }
    411      if (add_container == kCSBF_Brob_Exif) {
    412        Bytes(box_brob_exif, box_brob_exif_size).AppendTo(c);
    413      }
    414      AppendU32BE(add_container == kCSBF_Single_Zero_Terminated
    415                      ? 0
    416                      : (compressed.size() + 8),
    417                  &c);
    418      c.push_back('j');
    419      c.push_back('x');
    420      c.push_back('l');
    421      c.push_back('c');
    422      Bytes(compressed).AppendTo(c);
    423      if (add_container == kCSBF_Single_Other) {
    424        AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false,
    425                      &c);
    426      }
    427      compressed.swap(c);
    428    }
    429  }
    430 
    431  return compressed;
    432 }
    433 
    434 JxlDecoderStatus ProcessInputIgnoreBoxes(JxlDecoder* dec) {
    435  JxlDecoderStatus status = JXL_DEC_BOX;
    436  while (status == JXL_DEC_BOX) {
    437    status = JxlDecoderProcessInput(dec);
    438  }
    439  return status;
    440 }
    441 
    442 // Decodes one-shot with the API for non-streaming decoding tests.
    443 std::vector<uint8_t> DecodeWithAPI(JxlDecoder* dec,
    444                                   Span<const uint8_t> compressed,
    445                                   const JxlPixelFormat& format,
    446                                   bool use_callback, bool set_buffer_early,
    447                                   bool use_resizable_runner,
    448                                   bool require_boxes, bool expect_success,
    449                                   std::vector<uint8_t>* icc = nullptr) {
    450  JxlThreadParallelRunnerPtr runner_fixed;
    451  JxlResizableParallelRunnerPtr runner_resizable;
    452  JxlParallelRunner runner_fn;
    453  void* runner;
    454 
    455  if (use_resizable_runner) {
    456    runner_resizable = JxlResizableParallelRunnerMake(nullptr);
    457    runner = runner_resizable.get();
    458    runner_fn = JxlResizableParallelRunner;
    459  } else {
    460    size_t hw_threads = JxlThreadParallelRunnerDefaultNumWorkerThreads();
    461    runner_fixed =
    462        JxlThreadParallelRunnerMake(nullptr, std::min<size_t>(hw_threads, 16));
    463    runner = runner_fixed.get();
    464    runner_fn = JxlThreadParallelRunner;
    465  }
    466  EXPECT_EQ(JXL_DEC_SUCCESS,
    467            JxlDecoderSetParallelRunner(dec, runner_fn, runner));
    468 
    469  auto process_input =
    470      require_boxes ? ProcessInputIgnoreBoxes : JxlDecoderProcessInput;
    471 
    472  EXPECT_EQ(
    473      JXL_DEC_SUCCESS,
    474      JxlDecoderSubscribeEvents(
    475          dec, JXL_DEC_BASIC_INFO | (set_buffer_early ? JXL_DEC_FRAME : 0) |
    476                   JXL_DEC_PREVIEW_IMAGE | JXL_DEC_FULL_IMAGE |
    477                   (require_boxes ? JXL_DEC_BOX : 0) |
    478                   (icc != nullptr ? JXL_DEC_COLOR_ENCODING : 0)));
    479 
    480  EXPECT_EQ(JXL_DEC_SUCCESS,
    481            JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
    482  EXPECT_EQ(JXL_DEC_BASIC_INFO, process_input(dec));
    483  size_t buffer_size;
    484  EXPECT_EQ(JXL_DEC_SUCCESS,
    485            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
    486  JxlBasicInfo info;
    487  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
    488  if (use_resizable_runner) {
    489    JxlResizableParallelRunnerSetThreads(
    490        runner,
    491        JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
    492  }
    493 
    494  std::vector<uint8_t> pixels(buffer_size);
    495  size_t bytes_per_pixel = format.num_channels *
    496                           test::GetDataBits(format.data_type) /
    497                           jxl::kBitsPerByte;
    498  size_t stride = bytes_per_pixel * info.xsize;
    499  if (format.align > 1) {
    500    stride = jxl::DivCeil(stride, format.align) * format.align;
    501  }
    502  auto callback = [&](size_t x, size_t y, size_t num_pixels,
    503                      const void* pixels_row) {
    504    memcpy(pixels.data() + stride * y + bytes_per_pixel * x, pixels_row,
    505           num_pixels * bytes_per_pixel);
    506  };
    507 
    508  JxlDecoderStatus status = process_input(dec);
    509 
    510  if (status == JXL_DEC_COLOR_ENCODING) {
    511    size_t icc_size = 0;
    512    EXPECT_EQ(JXL_DEC_SUCCESS,
    513              JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA,
    514                                          &icc_size));
    515    icc->resize(icc_size);
    516    EXPECT_EQ(JXL_DEC_SUCCESS,
    517              JxlDecoderGetColorAsICCProfile(dec, JXL_COLOR_PROFILE_TARGET_DATA,
    518                                             icc->data(), icc_size));
    519 
    520    status = process_input(dec);
    521  }
    522 
    523  std::vector<uint8_t> preview;
    524  if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) {
    525    size_t buffer_size;
    526    EXPECT_EQ(JXL_DEC_SUCCESS,
    527              JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size));
    528    preview.resize(buffer_size);
    529    EXPECT_EQ(JXL_DEC_SUCCESS,
    530              JxlDecoderSetPreviewOutBuffer(dec, &format, preview.data(),
    531                                            preview.size()));
    532    EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, process_input(dec));
    533 
    534    status = process_input(dec);
    535  }
    536 
    537  if (set_buffer_early) {
    538    EXPECT_EQ(JXL_DEC_FRAME, status);
    539  } else {
    540    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, status);
    541  }
    542 
    543  if (use_callback) {
    544    EXPECT_EQ(JXL_DEC_SUCCESS,
    545              JxlDecoderSetImageOutCallback(
    546                  dec, &format,
    547                  [](void* opaque, size_t x, size_t y, size_t xsize,
    548                     const void* pixels_row) {
    549                    auto cb = static_cast<decltype(&callback)>(opaque);
    550                    (*cb)(x, y, xsize, pixels_row);
    551                  },
    552                  /*opaque=*/&callback));
    553  } else {
    554    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
    555                                   dec, &format, pixels.data(), pixels.size()));
    556  }
    557 
    558  EXPECT_EQ(JXL_DEC_FULL_IMAGE, process_input(dec));
    559 
    560  // After the full image was output, JxlDecoderProcessInput should return
    561  // success to indicate all is done, unless we requested boxes and the last
    562  // box was not a terminal unbounded box, in which case it should ask for
    563  // more input.
    564  JxlDecoderStatus expected_status =
    565      expect_success ? JXL_DEC_SUCCESS : JXL_DEC_NEED_MORE_INPUT;
    566  EXPECT_EQ(expected_status, process_input(dec));
    567 
    568  return pixels;
    569 }
    570 
    571 // Decodes one-shot with the API for non-streaming decoding tests.
    572 std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed,
    573                                   const JxlPixelFormat& format,
    574                                   bool use_callback, bool set_buffer_early,
    575                                   bool use_resizable_runner,
    576                                   bool require_boxes, bool expect_success) {
    577  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    578  std::vector<uint8_t> pixels =
    579      DecodeWithAPI(dec, compressed, format, use_callback, set_buffer_early,
    580                    use_resizable_runner, require_boxes, expect_success);
    581  JxlDecoderDestroy(dec);
    582  return pixels;
    583 }
    584 
    585 }  // namespace
    586 }  // namespace jxl
    587 
    588 ////////////////////////////////////////////////////////////////////////////////
    589 
    590 using ::jxl::Image3F;
    591 using ::jxl::ImageF;
    592 using ::jxl::test::BoolToCStr;
    593 using ::jxl::test::ButteraugliDistance;
    594 
    595 TEST(DecodeTest, JxlSignatureCheckTest) {
    596  std::vector<std::pair<int, std::vector<uint8_t>>> tests = {
    597      // No JPEGXL header starts with 'a'.
    598      {JXL_SIG_INVALID, {'a'}},
    599      {JXL_SIG_INVALID, {'a', 'b', 'c', 'd', 'e', 'f'}},
    600 
    601      // Empty file is not enough bytes.
    602      {JXL_SIG_NOT_ENOUGH_BYTES, {}},
    603 
    604      // JPEGXL headers.
    605      {JXL_SIG_NOT_ENOUGH_BYTES, {0xff}},  // Part of a signature.
    606      {JXL_SIG_INVALID, {0xff, 0xD8}},     // JPEG-1
    607      {JXL_SIG_CODESTREAM, {0xff, 0x0a}},
    608 
    609      // JPEGXL container file.
    610      {JXL_SIG_CONTAINER,
    611       {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87, 0xA}},
    612      // Ending with invalid byte.
    613      {JXL_SIG_INVALID, {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87, 0}},
    614      // Part of signature.
    615      {JXL_SIG_NOT_ENOUGH_BYTES,
    616       {0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xD, 0xA, 0x87}},
    617      {JXL_SIG_NOT_ENOUGH_BYTES, {0}},
    618  };
    619  for (const auto& test : tests) {
    620    EXPECT_EQ(test.first,
    621              JxlSignatureCheck(test.second.data(), test.second.size()))
    622        << "Where test data is " << ::testing::PrintToString(test.second);
    623  }
    624 }
    625 
    626 TEST(DecodeTest, DefaultAllocTest) {
    627  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    628  EXPECT_NE(nullptr, dec);
    629  JxlDecoderDestroy(dec);
    630 }
    631 
    632 TEST(DecodeTest, CustomAllocTest) {
    633  struct CalledCounters {
    634    int allocs = 0;
    635    int frees = 0;
    636  } counters;
    637 
    638  JxlMemoryManager mm;
    639  mm.opaque = &counters;
    640  mm.alloc = [](void* opaque, size_t size) {
    641    reinterpret_cast<CalledCounters*>(opaque)->allocs++;
    642    return malloc(size);
    643  };
    644  mm.free = [](void* opaque, void* address) {
    645    reinterpret_cast<CalledCounters*>(opaque)->frees++;
    646    free(address);
    647  };
    648 
    649  JxlDecoder* dec = JxlDecoderCreate(&mm);
    650  EXPECT_NE(nullptr, dec);
    651  EXPECT_LE(1, counters.allocs);
    652  EXPECT_EQ(0, counters.frees);
    653  JxlDecoderDestroy(dec);
    654  EXPECT_LE(1, counters.frees);
    655 }
    656 
    657 // TODO(lode): add multi-threaded test when multithreaded pixel decoding from
    658 // API is implemented.
    659 TEST(DecodeTest, DefaultParallelRunnerTest) {
    660  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    661  EXPECT_NE(nullptr, dec);
    662  EXPECT_EQ(JXL_DEC_SUCCESS,
    663            JxlDecoderSetParallelRunner(dec, nullptr, nullptr));
    664  JxlDecoderDestroy(dec);
    665 }
    666 
    667 // Creates the header of a JPEG XL file with various custom parameters for
    668 // testing.
    669 // xsize, ysize: image dimensions to store in the SizeHeader, max 512.
    670 // bits_per_sample, orientation: a selection of header parameters to test with.
    671 // orientation: image orientation to set in the metadata
    672 // alpha_bits: if non-0, alpha extra channel bits to set in the metadata. Also
    673 //   gives the alpha channel the name "alpha_test"
    674 // have_container: add box container format around the codestream.
    675 // metadata_default: if true, ImageMetadata is set to default and
    676 //   bits_per_sample, orientation and alpha_bits are ignored.
    677 // insert_box: insert an extra box before the codestream box, making the header
    678 // farther away from the front than is ideal. Only used if have_container.
    679 std::vector<uint8_t> GetTestHeader(size_t xsize, size_t ysize,
    680                                   size_t bits_per_sample, size_t orientation,
    681                                   size_t alpha_bits, bool xyb_encoded,
    682                                   bool have_container, bool metadata_default,
    683                                   bool insert_extra_box,
    684                                   const jxl::IccBytes& icc_profile) {
    685  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
    686  jxl::BitWriter writer{memory_manager};
    687  EXPECT_TRUE(writer.WithMaxBits(
    688      65536,  // Large enough
    689      jxl::LayerType::Header, nullptr, [&] {
    690        if (have_container) {
    691          const std::vector<uint8_t> signature_box = {
    692              0, 0, 0, 0xc, 'J', 'X', 'L', ' ', 0xd, 0xa, 0x87, 0xa};
    693          const std::vector<uint8_t> filetype_box = {
    694              0,   0,   0, 0x14, 'f', 't', 'y', 'p', 'j', 'x',
    695              'l', ' ', 0, 0,    0,   0,   'j', 'x', 'l', ' '};
    696          const std::vector<uint8_t> extra_box_header = {0,   0,   0,   0xff,
    697                                                         't', 'e', 's', 't'};
    698          // Beginning of codestream box, with an arbitrary size certainly large
    699          // enough to contain the header
    700          const std::vector<uint8_t> codestream_box_header = {
    701              0, 0, 0, 0xff, 'j', 'x', 'l', 'c'};
    702 
    703          for (uint8_t c : signature_box) {
    704            writer.Write(8, c);
    705          }
    706          for (uint8_t c : filetype_box) {
    707            writer.Write(8, c);
    708          }
    709          if (insert_extra_box) {
    710            for (uint8_t c : extra_box_header) {
    711              writer.Write(8, c);
    712            }
    713            for (size_t i = 0; i < 255 - 8; i++) {
    714              writer.Write(8, 0);
    715            }
    716          }
    717          for (uint8_t c : codestream_box_header) {
    718            writer.Write(8, c);
    719          }
    720        }
    721 
    722        // JXL signature
    723        writer.Write(8, 0xff);
    724        writer.Write(8, 0x0a);
    725 
    726        // SizeHeader
    727        jxl::CodecMetadata metadata;
    728        EXPECT_TRUE(metadata.size.Set(xsize, ysize));
    729        EXPECT_TRUE(WriteSizeHeader(metadata.size, &writer,
    730                                    jxl::LayerType::Header, nullptr));
    731 
    732        if (!metadata_default) {
    733          metadata.m.SetUintSamples(bits_per_sample);
    734          metadata.m.orientation = orientation;
    735          metadata.m.SetAlphaBits(alpha_bits);
    736          metadata.m.xyb_encoded = xyb_encoded;
    737          if (alpha_bits != 0) {
    738            metadata.m.extra_channel_info[0].name = "alpha_test";
    739          }
    740        }
    741 
    742        if (!icc_profile.empty()) {
    743          jxl::IccBytes copy = icc_profile;
    744          EXPECT_TRUE(metadata.m.color_encoding.SetICC(std::move(copy),
    745                                                       JxlGetDefaultCms()));
    746        }
    747 
    748        EXPECT_TRUE(jxl::Bundle::Write(metadata.m, &writer,
    749                                       jxl::LayerType::Header, nullptr));
    750        metadata.transform_data.nonserialized_xyb_encoded =
    751            metadata.m.xyb_encoded;
    752        EXPECT_TRUE(jxl::Bundle::Write(metadata.transform_data, &writer,
    753                                       jxl::LayerType::Header, nullptr));
    754 
    755        if (!icc_profile.empty()) {
    756          EXPECT_TRUE(metadata.m.color_encoding.WantICC());
    757          EXPECT_TRUE(jxl::WriteICC(jxl::Span<const uint8_t>(icc_profile),
    758                                    &writer, jxl::LayerType::Header, nullptr));
    759        }
    760 
    761        writer.ZeroPadToByte();
    762        return true;
    763      }));
    764  jxl::Bytes bytes = writer.GetSpan();
    765  return std::vector<uint8_t>(bytes.data(), bytes.data() + bytes.size());
    766 }
    767 
    768 TEST(DecodeTest, BasicInfoTest) {
    769  size_t xsize[2] = {50, 33};
    770  size_t ysize[2] = {50, 77};
    771  size_t bits_per_sample[2] = {8, 23};
    772  size_t orientation[2] = {3, 5};
    773  size_t alpha_bits[2] = {0, 8};
    774  bool have_container[2] = {false, true};
    775  bool xyb_encoded = false;
    776 
    777  std::vector<std::vector<uint8_t>> test_samples;
    778  // Test with direct codestream
    779  test_samples.push_back(GetTestHeader(
    780      xsize[0], ysize[0], bits_per_sample[0], orientation[0], alpha_bits[0],
    781      xyb_encoded, have_container[0], /*metadata_default=*/false,
    782      /*insert_extra_box=*/false, {}));
    783  // Test with container and different parameters
    784  test_samples.push_back(GetTestHeader(
    785      xsize[1], ysize[1], bits_per_sample[1], orientation[1], alpha_bits[1],
    786      xyb_encoded, have_container[1], /*metadata_default=*/false,
    787      /*insert_extra_box=*/false, {}));
    788 
    789  for (size_t i = 0; i < test_samples.size(); ++i) {
    790    const std::vector<uint8_t>& data = test_samples[i];
    791    // Test decoding too small header first, until we reach the final byte.
    792    for (size_t size = 0; size <= data.size(); ++size) {
    793      // Test with a new decoder for each tested byte size.
    794      JxlDecoder* dec = JxlDecoderCreate(nullptr);
    795      EXPECT_EQ(JXL_DEC_SUCCESS,
    796                JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
    797      const uint8_t* next_in = data.data();
    798      size_t avail_in = size;
    799      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
    800      JxlDecoderStatus status = JxlDecoderProcessInput(dec);
    801 
    802      JxlBasicInfo info;
    803      JxlDecoderStatus bi_status = JxlDecoderGetBasicInfo(dec, &info);
    804      bool have_basic_info = (bi_status == JXL_DEC_SUCCESS);
    805 
    806      if (size == data.size()) {
    807        EXPECT_EQ(JXL_DEC_BASIC_INFO, status);
    808 
    809        // All header bytes given so the decoder must have the basic info.
    810        EXPECT_EQ(true, have_basic_info);
    811        EXPECT_EQ(have_container[i], FROM_JXL_BOOL(info.have_container));
    812        EXPECT_EQ(alpha_bits[i], info.alpha_bits);
    813        // Orientations 5..8 swap the dimensions
    814        if (orientation[i] >= 5) {
    815          EXPECT_EQ(xsize[i], info.ysize);
    816          EXPECT_EQ(ysize[i], info.xsize);
    817        } else {
    818          EXPECT_EQ(xsize[i], info.xsize);
    819          EXPECT_EQ(ysize[i], info.ysize);
    820        }
    821        // The API should set the orientation to identity by default since it
    822        // already applies the transformation internally by default.
    823        EXPECT_EQ(1u, info.orientation);
    824 
    825        EXPECT_EQ(3u, info.num_color_channels);
    826 
    827        if (alpha_bits[i] != 0) {
    828          // Expect an extra channel
    829          EXPECT_EQ(1u, info.num_extra_channels);
    830          JxlExtraChannelInfo extra;
    831          EXPECT_EQ(0, JxlDecoderGetExtraChannelInfo(dec, 0, &extra));
    832          EXPECT_EQ(alpha_bits[i], extra.bits_per_sample);
    833          EXPECT_EQ(JXL_CHANNEL_ALPHA, extra.type);
    834          EXPECT_EQ(0, extra.alpha_premultiplied);
    835          // Verify the name "alpha_test" given to the alpha channel
    836          EXPECT_EQ(10u, extra.name_length);
    837          char name[11];
    838          EXPECT_EQ(0,
    839                    JxlDecoderGetExtraChannelName(dec, 0, name, sizeof(name)));
    840          EXPECT_EQ(std::string("alpha_test"), std::string(name));
    841        } else {
    842          EXPECT_EQ(0u, info.num_extra_channels);
    843        }
    844 
    845        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
    846      } else {
    847        // If we did not give the full header, the basic info should not be
    848        // available. Allow a few bytes of slack due to some bits for default
    849        // opsinmatrix/extension bits.
    850        if (size + 2 < data.size()) {
    851          EXPECT_EQ(false, have_basic_info);
    852          EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, status);
    853        }
    854      }
    855 
    856      // Test that decoder doesn't allow setting a setting required at beginning
    857      // unless it's reset
    858      EXPECT_EQ(JXL_DEC_ERROR,
    859                JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
    860      JxlDecoderReset(dec);
    861      EXPECT_EQ(JXL_DEC_SUCCESS,
    862                JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
    863 
    864      JxlDecoderDestroy(dec);
    865    }
    866  }
    867 }
    868 
    869 TEST(DecodeTest, BufferSizeTest) {
    870  size_t xsize = 33;
    871  size_t ysize = 77;
    872  size_t bits_per_sample = 8;
    873  size_t orientation = 1;
    874  size_t alpha_bits = 8;
    875  bool have_container = false;
    876  bool xyb_encoded = false;
    877 
    878  std::vector<uint8_t> header =
    879      GetTestHeader(xsize, ysize, bits_per_sample, orientation, alpha_bits,
    880                    xyb_encoded, have_container, /*metadata_default=*/false,
    881                    /*insert_extra_box=*/false, {});
    882 
    883  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    884  EXPECT_EQ(JXL_DEC_SUCCESS,
    885            JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
    886  const uint8_t* next_in = header.data();
    887  size_t avail_in = header.size();
    888  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
    889  JxlDecoderStatus status = JxlDecoderProcessInput(dec);
    890  EXPECT_EQ(JXL_DEC_BASIC_INFO, status);
    891 
    892  JxlBasicInfo info;
    893  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
    894  EXPECT_EQ(xsize, info.xsize);
    895  EXPECT_EQ(ysize, info.ysize);
    896 
    897  JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
    898  size_t image_out_size;
    899  EXPECT_EQ(JXL_DEC_SUCCESS,
    900            JxlDecoderImageOutBufferSize(dec, &format, &image_out_size));
    901  EXPECT_EQ(xsize * ysize * 4, image_out_size);
    902 
    903  JxlDecoderDestroy(dec);
    904 }
    905 
    906 TEST(DecodeTest, BasicInfoSizeHintTest) {
    907  // Test on a file where the size hint is too small initially due to inserting
    908  // a box before the codestream (something that is normally not recommended)
    909  size_t xsize = 50;
    910  size_t ysize = 50;
    911  size_t bits_per_sample = 16;
    912  size_t orientation = 1;
    913  size_t alpha_bits = 0;
    914  bool xyb_encoded = false;
    915  std::vector<uint8_t> data = GetTestHeader(
    916      xsize, ysize, bits_per_sample, orientation, alpha_bits, xyb_encoded,
    917      /*have_container=*/true, /*metadata_default=*/false,
    918      /*insert_extra_box=*/true, {});
    919 
    920  JxlDecoderStatus status;
    921  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    922  EXPECT_EQ(JXL_DEC_SUCCESS,
    923            JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
    924 
    925  size_t hint0 = JxlDecoderSizeHintBasicInfo(dec);
    926  // Test that the test works as intended: we construct a file on purpose to
    927  // be larger than the first hint by having that extra box.
    928  EXPECT_LT(hint0, data.size());
    929  const uint8_t* next_in = data.data();
    930  // Do as if we have only as many bytes as indicated by the hint available
    931  size_t avail_in = std::min(hint0, data.size());
    932  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
    933  status = JxlDecoderProcessInput(dec);
    934  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, status);
    935  // Basic info cannot be available yet due to the extra inserted box.
    936  EXPECT_EQ(false, !JxlDecoderGetBasicInfo(dec, nullptr));
    937 
    938  size_t num_read = avail_in - JxlDecoderReleaseInput(dec);
    939  EXPECT_LT(num_read, data.size());
    940 
    941  size_t hint1 = JxlDecoderSizeHintBasicInfo(dec);
    942  // The hint must be larger than the previous hint (taking already processed
    943  // bytes into account, the hint is a hint for the next avail_in) since the
    944  // decoder now knows there is a box in between.
    945  EXPECT_GT(hint1 + num_read, hint0);
    946  avail_in = std::min<size_t>(hint1, data.size() - num_read);
    947  next_in += num_read;
    948 
    949  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
    950  status = JxlDecoderProcessInput(dec);
    951  EXPECT_EQ(JXL_DEC_BASIC_INFO, status);
    952  JxlBasicInfo info;
    953  // We should have the basic info now, since we only added one box in-between,
    954  // and the decoder should have known its size, its implementation can return
    955  // a correct hint.
    956  EXPECT_EQ(true, !JxlDecoderGetBasicInfo(dec, &info));
    957 
    958  // Also test if the basic info is correct.
    959  EXPECT_EQ(1, info.have_container);
    960  EXPECT_EQ(xsize, info.xsize);
    961  EXPECT_EQ(ysize, info.ysize);
    962  EXPECT_EQ(orientation, info.orientation);
    963  EXPECT_EQ(bits_per_sample, info.bits_per_sample);
    964 
    965  JxlDecoderDestroy(dec);
    966 }
    967 
    968 std::vector<uint8_t> GetIccTestHeader(const jxl::IccBytes& icc_profile,
    969                                      bool xyb_encoded) {
    970  size_t xsize = 50;
    971  size_t ysize = 50;
    972  size_t bits_per_sample = 16;
    973  size_t orientation = 1;
    974  size_t alpha_bits = 0;
    975  return GetTestHeader(xsize, ysize, bits_per_sample, orientation, alpha_bits,
    976                       xyb_encoded,
    977                       /*have_container=*/false, /*metadata_default=*/false,
    978                       /*insert_extra_box=*/false, icc_profile);
    979 }
    980 
    981 // Tests the case where pixels and metadata ICC profile are the same
    982 TEST(DecodeTest, IccProfileTestOriginal) {
    983  jxl::IccBytes icc_profile = GetIccTestProfile();
    984  bool xyb_encoded = false;
    985  std::vector<uint8_t> data = GetIccTestHeader(icc_profile, xyb_encoded);
    986 
    987  JxlDecoder* dec = JxlDecoderCreate(nullptr);
    988  EXPECT_EQ(JXL_DEC_SUCCESS,
    989            JxlDecoderSubscribeEvents(
    990                dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING));
    991  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size()));
    992 
    993  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
    994 
    995  // Expect the opposite of xyb_encoded for uses_original_profile
    996  JxlBasicInfo info;
    997  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
    998  EXPECT_EQ(JXL_TRUE, info.uses_original_profile);
    999 
   1000  EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   1001 
   1002  // the encoded color profile expected to be not available, since the image
   1003  // has an ICC profile instead
   1004  EXPECT_EQ(JXL_DEC_ERROR,
   1005            JxlDecoderGetColorAsEncodedProfile(
   1006                dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, nullptr));
   1007 
   1008  size_t dec_profile_size;
   1009  EXPECT_EQ(JXL_DEC_SUCCESS,
   1010            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1011                                        &dec_profile_size));
   1012 
   1013  // Check that can get return status with NULL size
   1014  EXPECT_EQ(JXL_DEC_SUCCESS,
   1015            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1016                                        nullptr));
   1017 
   1018  // The profiles must be equal. This requires they have equal size, and if
   1019  // they do, we can get the profile and compare the contents.
   1020  EXPECT_EQ(icc_profile.size(), dec_profile_size);
   1021  if (icc_profile.size() == dec_profile_size) {
   1022    jxl::IccBytes icc_profile2(icc_profile.size());
   1023    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile(
   1024                                   dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1025                                   icc_profile2.data(), icc_profile2.size()));
   1026    EXPECT_EQ(icc_profile, icc_profile2);
   1027  }
   1028 
   1029  // the data is not xyb_encoded, so same result expected for the pixel data
   1030  // color profile
   1031  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetColorAsEncodedProfile(
   1032                               dec, JXL_COLOR_PROFILE_TARGET_DATA, nullptr));
   1033 
   1034  EXPECT_EQ(JXL_DEC_SUCCESS,
   1035            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA,
   1036                                        &dec_profile_size));
   1037  EXPECT_EQ(icc_profile.size(), dec_profile_size);
   1038 
   1039  JxlDecoderDestroy(dec);
   1040 }
   1041 
   1042 // Tests the case where pixels and metadata ICC profile are different
   1043 TEST(DecodeTest, IccProfileTestXybEncoded) {
   1044  jxl::IccBytes icc_profile = GetIccTestProfile();
   1045  bool xyb_encoded = true;
   1046  std::vector<uint8_t> data = GetIccTestHeader(icc_profile, xyb_encoded);
   1047 
   1048  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1049  EXPECT_EQ(JXL_DEC_SUCCESS,
   1050            JxlDecoderSubscribeEvents(
   1051                dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING));
   1052 
   1053  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size()));
   1054  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   1055 
   1056  // Expect the opposite of xyb_encoded for uses_original_profile
   1057  JxlBasicInfo info;
   1058  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   1059  EXPECT_EQ(JXL_FALSE, info.uses_original_profile);
   1060 
   1061  EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   1062 
   1063  // the encoded color profile expected to be not available, since the image
   1064  // has an ICC profile instead
   1065  EXPECT_EQ(JXL_DEC_ERROR,
   1066            JxlDecoderGetColorAsEncodedProfile(
   1067                dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, nullptr));
   1068 
   1069  // Check that can get return status with NULL size
   1070  EXPECT_EQ(JXL_DEC_SUCCESS,
   1071            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1072                                        nullptr));
   1073 
   1074  size_t dec_profile_size;
   1075  EXPECT_EQ(JXL_DEC_SUCCESS,
   1076            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1077                                        &dec_profile_size));
   1078 
   1079  // The profiles must be equal. This requires they have equal size, and if
   1080  // they do, we can get the profile and compare the contents.
   1081  EXPECT_EQ(icc_profile.size(), dec_profile_size);
   1082  if (icc_profile.size() == dec_profile_size) {
   1083    jxl::IccBytes icc_profile2(icc_profile.size());
   1084    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile(
   1085                                   dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL,
   1086                                   icc_profile2.data(), icc_profile2.size()));
   1087    EXPECT_EQ(icc_profile, icc_profile2);
   1088  }
   1089 
   1090  // Data is xyb_encoded, so the data profile is a different profile, encoded
   1091  // as structured profile.
   1092  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsEncodedProfile(
   1093                                 dec, JXL_COLOR_PROFILE_TARGET_DATA, nullptr));
   1094  JxlColorEncoding pixel_encoding;
   1095  EXPECT_EQ(JXL_DEC_SUCCESS,
   1096            JxlDecoderGetColorAsEncodedProfile(
   1097                dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding));
   1098  EXPECT_EQ(JXL_PRIMARIES_SRGB, pixel_encoding.primaries);
   1099  // The API returns LINEAR by default when the colorspace cannot be represented
   1100  // by enum values.
   1101  EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function);
   1102 
   1103  // Test the same but with integer format.
   1104  EXPECT_EQ(JXL_DEC_SUCCESS,
   1105            JxlDecoderGetColorAsEncodedProfile(
   1106                dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding));
   1107  EXPECT_EQ(JXL_PRIMARIES_SRGB, pixel_encoding.primaries);
   1108  EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function);
   1109 
   1110  // Test after setting the preferred color profile to non-linear sRGB:
   1111  // for XYB images with ICC profile, this setting is expected to take effect.
   1112  jxl::ColorEncoding temp_jxl_srgb = jxl::ColorEncoding::SRGB(false);
   1113  JxlColorEncoding pixel_encoding_srgb = temp_jxl_srgb.ToExternal();
   1114  EXPECT_EQ(JXL_DEC_SUCCESS,
   1115            JxlDecoderSetPreferredColorProfile(dec, &pixel_encoding_srgb));
   1116  EXPECT_EQ(JXL_DEC_SUCCESS,
   1117            JxlDecoderGetColorAsEncodedProfile(
   1118                dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding));
   1119  EXPECT_EQ(JXL_TRANSFER_FUNCTION_SRGB, pixel_encoding.transfer_function);
   1120 
   1121  // The decoder can also output this as a generated ICC profile anyway, and
   1122  // we're certain that it will differ from the above defined profile since
   1123  // the sRGB data should not have swapped R/G/B primaries.
   1124 
   1125  EXPECT_EQ(JXL_DEC_SUCCESS,
   1126            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA,
   1127                                        &dec_profile_size));
   1128  // We don't need to dictate exactly what size the generated ICC profile
   1129  // must be (since there are many ways to represent the same color space),
   1130  // but it should not be zero.
   1131  EXPECT_NE(0u, dec_profile_size);
   1132  jxl::IccBytes icc_profile2(dec_profile_size);
   1133  if (0 != dec_profile_size) {
   1134    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile(
   1135                                   dec, JXL_COLOR_PROFILE_TARGET_DATA,
   1136                                   icc_profile2.data(), icc_profile2.size()));
   1137    // expected not equal
   1138    EXPECT_NE(icc_profile, icc_profile2);
   1139  }
   1140 
   1141  // Test setting another different preferred profile, to verify that the
   1142  // returned JXL_COLOR_PROFILE_TARGET_DATA ICC profile is correctly
   1143  // updated.
   1144 
   1145  jxl::ColorEncoding temp_jxl_linear = jxl::ColorEncoding::LinearSRGB(false);
   1146  JxlColorEncoding pixel_encoding_linear = temp_jxl_linear.ToExternal();
   1147 
   1148  EXPECT_EQ(JXL_DEC_SUCCESS,
   1149            JxlDecoderSetPreferredColorProfile(dec, &pixel_encoding_linear));
   1150  EXPECT_EQ(JXL_DEC_SUCCESS,
   1151            JxlDecoderGetColorAsEncodedProfile(
   1152                dec, JXL_COLOR_PROFILE_TARGET_DATA, &pixel_encoding));
   1153  EXPECT_EQ(JXL_TRANSFER_FUNCTION_LINEAR, pixel_encoding.transfer_function);
   1154  EXPECT_EQ(JXL_DEC_SUCCESS,
   1155            JxlDecoderGetICCProfileSize(dec, JXL_COLOR_PROFILE_TARGET_DATA,
   1156                                        &dec_profile_size));
   1157  EXPECT_NE(0u, dec_profile_size);
   1158  jxl::IccBytes icc_profile3(dec_profile_size);
   1159  if (0 != dec_profile_size) {
   1160    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetColorAsICCProfile(
   1161                                   dec, JXL_COLOR_PROFILE_TARGET_DATA,
   1162                                   icc_profile3.data(), icc_profile3.size()));
   1163    // expected not equal to the previously set preferred profile.
   1164    EXPECT_NE(icc_profile2, icc_profile3);
   1165  }
   1166 
   1167  JxlDecoderDestroy(dec);
   1168 }
   1169 
   1170 // Test decoding ICC from partial files byte for byte.
   1171 // This test must pass also if JXL_CRASH_ON_ERROR is enabled, that is, the
   1172 // decoding of the ANS histogram and stream of the encoded ICC profile must also
   1173 // handle the case of not enough input bytes with StatusCode::kNotEnoughBytes
   1174 // rather than fatal error status codes.
   1175 TEST(DecodeTest, ICCPartialTest) {
   1176  jxl::IccBytes icc_profile = GetIccTestProfile();
   1177  std::vector<uint8_t> data = GetIccTestHeader(icc_profile, false);
   1178 
   1179  const uint8_t* next_in = data.data();
   1180  size_t avail_in = 0;
   1181 
   1182  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1183 
   1184  EXPECT_EQ(JXL_DEC_SUCCESS,
   1185            JxlDecoderSubscribeEvents(
   1186                dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING));
   1187 
   1188  bool seen_basic_info = false;
   1189  bool seen_color_encoding = false;
   1190  size_t total_size = 0;
   1191 
   1192  for (;;) {
   1193    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   1194    JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   1195    size_t remaining = JxlDecoderReleaseInput(dec);
   1196    EXPECT_LE(remaining, avail_in);
   1197    next_in += avail_in - remaining;
   1198    avail_in = remaining;
   1199    if (status == JXL_DEC_NEED_MORE_INPUT) {
   1200      if (total_size >= data.size()) {
   1201        // End of partial codestream with codestrema headers and ICC profile
   1202        // reached, it should not require more input since full image is not
   1203        // requested
   1204        FAIL();
   1205        break;
   1206      }
   1207      size_t increment = 1;
   1208      if (total_size + increment > data.size()) {
   1209        increment = data.size() - total_size;
   1210      }
   1211      total_size += increment;
   1212      avail_in += increment;
   1213    } else if (status == JXL_DEC_BASIC_INFO) {
   1214      EXPECT_FALSE(seen_basic_info);
   1215      seen_basic_info = true;
   1216    } else if (status == JXL_DEC_COLOR_ENCODING) {
   1217      EXPECT_TRUE(seen_basic_info);
   1218      EXPECT_FALSE(seen_color_encoding);
   1219      seen_color_encoding = true;
   1220 
   1221      // Sanity check that the ICC profile was decoded correctly
   1222      size_t dec_profile_size;
   1223      EXPECT_EQ(JXL_DEC_SUCCESS,
   1224                JxlDecoderGetICCProfileSize(
   1225                    dec, JXL_COLOR_PROFILE_TARGET_ORIGINAL, &dec_profile_size));
   1226      EXPECT_EQ(icc_profile.size(), dec_profile_size);
   1227 
   1228    } else if (status == JXL_DEC_SUCCESS) {
   1229      EXPECT_TRUE(seen_color_encoding);
   1230      break;
   1231    } else {
   1232      // We do not expect any other events or errors
   1233      FAIL();
   1234      break;
   1235    }
   1236  }
   1237 
   1238  EXPECT_TRUE(seen_basic_info);
   1239  EXPECT_TRUE(seen_color_encoding);
   1240 
   1241  JxlDecoderDestroy(dec);
   1242 }
   1243 
   1244 struct PixelTestConfig {
   1245  // Input image definition.
   1246  bool grayscale;
   1247  bool include_alpha;
   1248  size_t xsize;
   1249  size_t ysize;
   1250  jxl::PreviewMode preview_mode;
   1251  bool add_intrinsic_size;
   1252  // Output format.
   1253  JxlEndianness endianness;
   1254  JxlDataType data_type;
   1255  uint32_t output_channels;
   1256  // Container options.
   1257  CodeStreamBoxFormat add_container;
   1258  // Decoding mode.
   1259  bool use_callback;
   1260  bool set_buffer_early;
   1261  bool use_resizable_runner;
   1262  // Exif orientation, 1-8
   1263  JxlOrientation orientation;
   1264  bool keep_orientation;
   1265  size_t upsampling;
   1266 };
   1267 
   1268 class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {};
   1269 
   1270 TEST_P(DecodeTestParam, PixelTest) {
   1271  PixelTestConfig config = GetParam();
   1272  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1273 
   1274  if (config.keep_orientation) {
   1275    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetKeepOrientation(dec, JXL_TRUE));
   1276  }
   1277 
   1278  size_t num_pixels = config.xsize * config.ysize;
   1279  uint32_t orig_channels =
   1280      (config.grayscale ? 1 : 3) + (config.include_alpha ? 1 : 0);
   1281  std::vector<uint8_t> pixels =
   1282      jxl::test::GetSomeTestImage(config.xsize, config.ysize, orig_channels, 0);
   1283  JxlPixelFormat format_orig = {orig_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN,
   1284                                0};
   1285  jxl::TestCodestreamParams params;
   1286  // Lossless to verify pixels exactly after roundtrip.
   1287  params.cparams.SetLossless();
   1288  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   1289  params.cparams.resampling = config.upsampling;
   1290  params.cparams.ec_resampling = config.upsampling;
   1291  params.box_format = config.add_container;
   1292  params.orientation = config.orientation;
   1293  params.preview_mode = config.preview_mode;
   1294  params.add_intrinsic_size = config.add_intrinsic_size;
   1295  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   1296      jxl::Bytes(pixels.data(), pixels.size()), config.xsize, config.ysize,
   1297      orig_channels, params);
   1298 
   1299  JxlPixelFormat format = {config.output_channels, config.data_type,
   1300                           config.endianness, 0};
   1301 
   1302  bool swap_xy = !config.keep_orientation && (config.orientation > 4);
   1303  size_t xsize = swap_xy ? config.ysize : config.xsize;
   1304  size_t ysize = swap_xy ? config.xsize : config.ysize;
   1305 
   1306  std::vector<uint8_t> pixels2 =
   1307      jxl::DecodeWithAPI(dec, jxl::Bytes(compressed.data(), compressed.size()),
   1308                         format, config.use_callback, config.set_buffer_early,
   1309                         config.use_resizable_runner, /*require_boxes=*/false,
   1310                         /*expect_success=*/true);
   1311  JxlDecoderReset(dec);
   1312  EXPECT_EQ(num_pixels * config.output_channels *
   1313                jxl::test::GetDataBits(config.data_type) / jxl::kBitsPerByte,
   1314            pixels2.size());
   1315 
   1316  // If an orientation transformation is expected, to compare the pixels, also
   1317  // apply this transformation to the original pixels. ConvertToExternal is
   1318  // used to achieve this, with a temporary conversion to CodecInOut and back.
   1319  if (config.orientation > 1 && !config.keep_orientation) {
   1320    jxl::Span<const uint8_t> bytes(pixels.data(), pixels.size());
   1321    jxl::ColorEncoding color_encoding =
   1322        jxl::ColorEncoding::SRGB(config.grayscale);
   1323 
   1324    jxl::CodecInOut io{jxl::test::MemoryManager()};
   1325    if (config.include_alpha) io.metadata.m.SetAlphaBits(16);
   1326    io.metadata.m.color_encoding = color_encoding;
   1327    ASSERT_TRUE(io.SetSize(config.xsize, config.ysize));
   1328 
   1329    EXPECT_TRUE(ConvertFromExternal(bytes, config.xsize, config.ysize,
   1330                                    color_encoding, 16, format_orig, nullptr,
   1331                                    &io.Main()));
   1332 
   1333    for (uint8_t& pixel : pixels) pixel = 0;
   1334    EXPECT_TRUE(ConvertToExternal(
   1335        io.Main(), 16,
   1336        /*float_out=*/false, orig_channels, JXL_BIG_ENDIAN,
   1337        xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(),
   1338        /*out_callback=*/{},
   1339        static_cast<jxl::Orientation>(config.orientation)));
   1340  }
   1341  if (config.upsampling == 1) {
   1342    EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   1343                                           ysize, format_orig, format));
   1344  } else {
   1345    // resampling is of course not lossless, so as a rough check:
   1346    // count pixels that are more than off-by-25 in the 8-bit value of one of
   1347    // the channels
   1348    EXPECT_LE(
   1349        jxl::test::ComparePixels(
   1350            pixels.data(), pixels2.data(), xsize, ysize, format_orig, format,
   1351            50.0 * (config.data_type == JXL_TYPE_UINT8 ? 1.0 : 256.0)),
   1352        300u);
   1353  }
   1354 
   1355  JxlDecoderDestroy(dec);
   1356 }
   1357 
   1358 std::vector<PixelTestConfig> GeneratePixelTests() {
   1359  std::vector<PixelTestConfig> all_tests;
   1360  struct ChannelInfo {
   1361    bool grayscale;
   1362    bool include_alpha;
   1363    size_t output_channels;
   1364  };
   1365  ChannelInfo ch_info[] = {
   1366      {false, true, 4},   // RGBA -> RGBA
   1367      {true, false, 1},   // G -> G
   1368      {true, true, 1},    // GA -> G
   1369      {true, true, 2},    // GA -> GA
   1370      {false, false, 3},  // RGB -> RGB
   1371      {false, true, 3},   // RGBA -> RGB
   1372      {false, false, 4},  // RGB -> RGBA
   1373  };
   1374 
   1375  struct OutputFormat {
   1376    JxlEndianness endianness;
   1377    JxlDataType data_type;
   1378  };
   1379  OutputFormat out_formats[] = {
   1380      {JXL_NATIVE_ENDIAN, JXL_TYPE_UINT8},
   1381      {JXL_LITTLE_ENDIAN, JXL_TYPE_UINT16},
   1382      {JXL_BIG_ENDIAN, JXL_TYPE_UINT16},
   1383      {JXL_NATIVE_ENDIAN, JXL_TYPE_FLOAT16},
   1384      {JXL_LITTLE_ENDIAN, JXL_TYPE_FLOAT},
   1385      {JXL_BIG_ENDIAN, JXL_TYPE_FLOAT},
   1386  };
   1387 
   1388  auto make_test = [&](ChannelInfo ch, size_t xsize, size_t ysize,
   1389                       jxl::PreviewMode preview_mode, bool intrinsic_size,
   1390                       CodeStreamBoxFormat box, JxlOrientation orientation,
   1391                       bool keep_orientation, OutputFormat format,
   1392                       bool use_callback, bool set_buffer_early,
   1393                       bool resizable_runner, size_t upsampling) {
   1394    PixelTestConfig c;
   1395    c.grayscale = ch.grayscale;
   1396    c.include_alpha = ch.include_alpha;
   1397    c.preview_mode = preview_mode;
   1398    c.add_intrinsic_size = intrinsic_size;
   1399    c.xsize = xsize;
   1400    c.ysize = ysize;
   1401    c.add_container = box;
   1402    c.output_channels = ch.output_channels;
   1403    c.data_type = format.data_type;
   1404    c.endianness = format.endianness;
   1405    c.use_callback = use_callback;
   1406    c.set_buffer_early = set_buffer_early;
   1407    c.use_resizable_runner = resizable_runner;
   1408    c.orientation = orientation;
   1409    c.keep_orientation = keep_orientation;
   1410    c.upsampling = upsampling;
   1411    all_tests.push_back(c);
   1412  };
   1413 
   1414  // Test output formats and methods.
   1415  for (ChannelInfo ch : ch_info) {
   1416    for (bool use_callback : {false, true}) {
   1417      for (size_t upsampling : {1, 2, 4, 8}) {
   1418        for (OutputFormat fmt : out_formats) {
   1419          make_test(ch, 301, 33, jxl::kNoPreview,
   1420                    /*add_intrinsic_size=*/false,
   1421                    CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
   1422                    /*keep_orientation=*/false, fmt, use_callback,
   1423                    /*set_buffer_early=*/false, /*resizable_runner=*/false,
   1424                    upsampling);
   1425        }
   1426      }
   1427    }
   1428  }
   1429  // Test codestream formats.
   1430  for (size_t box = 1; box < kCSBF_NUM_ENTRIES; ++box) {
   1431    make_test(ch_info[0], 77, 33, jxl::kNoPreview,
   1432              /*add_intrinsic_size=*/false,
   1433              static_cast<CodeStreamBoxFormat>(box), JXL_ORIENT_IDENTITY,
   1434              /*keep_orientation=*/false, out_formats[0],
   1435              /*use_callback=*/false,
   1436              /*set_buffer_early=*/false, /*resizable_runner=*/false, 1);
   1437  }
   1438  // Test previews.
   1439  for (int preview_mode = 0; preview_mode < jxl::kNumPreviewModes;
   1440       preview_mode++) {
   1441    make_test(ch_info[0], 77, 33, static_cast<jxl::PreviewMode>(preview_mode),
   1442              /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None,
   1443              JXL_ORIENT_IDENTITY,
   1444              /*keep_orientation=*/false, out_formats[0],
   1445              /*use_callback=*/false, /*set_buffer_early=*/false,
   1446              /*resizable_runner=*/false, 1);
   1447  }
   1448  // Test intrinsic sizes.
   1449  for (bool add_intrinsic_size : {false, true}) {
   1450    make_test(ch_info[0], 55, 34, jxl::kNoPreview, add_intrinsic_size,
   1451              CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
   1452              /*keep_orientation=*/false, out_formats[0],
   1453              /*use_callback=*/false, /*set_buffer_early=*/false,
   1454              /*resizable_runner=*/false, 1);
   1455  }
   1456  // Test setting buffers early.
   1457  make_test(ch_info[0], 300, 33, jxl::kNoPreview,
   1458            /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None,
   1459            JXL_ORIENT_IDENTITY,
   1460            /*keep_orientation=*/false, out_formats[0],
   1461            /*use_callback=*/false, /*set_buffer_early=*/true,
   1462            /*resizable_runner=*/false, 1);
   1463 
   1464  // Test using the resizable runner
   1465  for (size_t i = 0; i < 4; i++) {
   1466    make_test(ch_info[0], 300 << i, 33 << i, jxl::kNoPreview,
   1467              /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None,
   1468              JXL_ORIENT_IDENTITY,
   1469              /*keep_orientation=*/false, out_formats[0],
   1470              /*use_callback=*/false, /*set_buffer_early=*/false,
   1471              /*resizable_runner=*/true, 1);
   1472  }
   1473 
   1474  // Test orientations.
   1475  for (int orientation = 2; orientation <= 8; ++orientation) {
   1476    for (bool keep_orientation : {false, true}) {
   1477      for (bool use_callback : {false, true}) {
   1478        for (ChannelInfo ch : ch_info) {
   1479          for (OutputFormat fmt : out_formats) {
   1480            make_test(ch, 280, 12, jxl::kNoPreview,
   1481                      /*add_intrinsic_size=*/false,
   1482                      CodeStreamBoxFormat::kCSBF_None,
   1483                      static_cast<JxlOrientation>(orientation),
   1484                      /*keep_orientation=*/keep_orientation, fmt,
   1485                      /*use_callback=*/use_callback, /*set_buffer_early=*/true,
   1486                      /*resizable_runner=*/false, 1);
   1487          }
   1488        }
   1489      }
   1490    }
   1491  }
   1492 
   1493  return all_tests;
   1494 }
   1495 
   1496 std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) {
   1497  os << c.xsize << "x" << c.ysize;
   1498  const char* colors[] = {"", "G", "GA", "RGB", "RGBA"};
   1499  os << colors[(c.grayscale ? 1 : 3) + (c.include_alpha ? 1 : 0)];
   1500  os << "to";
   1501  os << colors[c.output_channels];
   1502  switch (c.data_type) {
   1503    case JXL_TYPE_UINT8:
   1504      os << "u8";
   1505      break;
   1506    case JXL_TYPE_UINT16:
   1507      os << "u16";
   1508      break;
   1509    case JXL_TYPE_FLOAT:
   1510      os << "f32";
   1511      break;
   1512    case JXL_TYPE_FLOAT16:
   1513      os << "f16";
   1514      break;
   1515    default:
   1516      ADD_FAILURE();
   1517  };
   1518  if (jxl::test::GetDataBits(c.data_type) > jxl::kBitsPerByte) {
   1519    if (c.endianness == JXL_NATIVE_ENDIAN) {
   1520      // add nothing
   1521    } else if (c.endianness == JXL_BIG_ENDIAN) {
   1522      os << "BE";
   1523    } else if (c.endianness == JXL_LITTLE_ENDIAN) {
   1524      os << "LE";
   1525    }
   1526  }
   1527  if (c.add_container != CodeStreamBoxFormat::kCSBF_None) {
   1528    os << "Box";
   1529    os << static_cast<size_t>(c.add_container);
   1530  }
   1531  if (c.preview_mode == jxl::kSmallPreview) os << "Preview";
   1532  if (c.preview_mode == jxl::kBigPreview) os << "BigPreview";
   1533  if (c.add_intrinsic_size) os << "IntrinicSize";
   1534  if (c.use_callback) os << "Callback";
   1535  if (c.set_buffer_early) os << "EarlyBuffer";
   1536  if (c.use_resizable_runner) os << "ResizableRunner";
   1537  if (c.orientation != 1) os << "O" << c.orientation;
   1538  if (c.keep_orientation) os << "Keep";
   1539  if (c.upsampling > 1) os << "x" << c.upsampling;
   1540  return os;
   1541 }
   1542 
   1543 std::string PixelTestDescription(
   1544    const testing::TestParamInfo<DecodeTestParam::ParamType>& info) {
   1545  std::stringstream name;
   1546  name << info.param;
   1547  return name.str();
   1548 }
   1549 
   1550 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeTest, DecodeTestParam,
   1551                                   testing::ValuesIn(GeneratePixelTests()),
   1552                                   PixelTestDescription);
   1553 
   1554 TEST(DecodeTest, PixelTestWithICCProfileLossless) {
   1555  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1556 
   1557  size_t xsize = 123;
   1558  size_t ysize = 77;
   1559  size_t num_pixels = xsize * ysize;
   1560  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   1561  JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1562  jxl::TestCodestreamParams params;
   1563  // Lossless to verify pixels exactly after roundtrip.
   1564  params.cparams.SetLossless();
   1565  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   1566  params.add_icc_profile = true;
   1567  // For variation: some have container and no preview, others have preview
   1568  // and no container.
   1569  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   1570      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   1571 
   1572  for (uint32_t channels = 3; channels <= 4; ++channels) {
   1573    {
   1574      JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
   1575 
   1576      std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   1577          dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   1578          /*use_callback=*/false, /*set_buffer_early=*/false,
   1579          /*use_resizable_runner=*/false, /*require_boxes=*/false,
   1580          /*expect_success=*/true);
   1581      JxlDecoderReset(dec);
   1582      EXPECT_EQ(num_pixels * channels, pixels2.size());
   1583      EXPECT_EQ(0u,
   1584                jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   1585                                         ysize, format_orig, format));
   1586    }
   1587    {
   1588      JxlPixelFormat format = {channels, JXL_TYPE_UINT16, JXL_LITTLE_ENDIAN, 0};
   1589 
   1590      // Test with the container for one of the pixel formats.
   1591      std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   1592          dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   1593          /*use_callback=*/true, /*set_buffer_early=*/true,
   1594          /*use_resizable_runner=*/false, /*require_boxes=*/false,
   1595          /*expect_success=*/true);
   1596      JxlDecoderReset(dec);
   1597      EXPECT_EQ(num_pixels * channels * 2, pixels2.size());
   1598      EXPECT_EQ(0u,
   1599                jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   1600                                         ysize, format_orig, format));
   1601    }
   1602 
   1603    {
   1604      JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, 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 * 4, pixels2.size());
   1613      EXPECT_EQ(0u,
   1614                jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   1615                                         ysize, format_orig, format));
   1616    }
   1617  }
   1618 
   1619  JxlDecoderDestroy(dec);
   1620 }
   1621 
   1622 TEST(DecodeTest, PixelTestWithICCProfileLossy) {
   1623  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   1624  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1625 
   1626  size_t xsize = 123;
   1627  size_t ysize = 77;
   1628  size_t num_pixels = xsize * ysize;
   1629  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   1630  JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1631  jxl::TestCodestreamParams params;
   1632  params.add_icc_profile = true;
   1633  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   1634      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   1635  uint32_t channels = 3;
   1636 
   1637  JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
   1638 
   1639  std::vector<uint8_t> icc_data;
   1640  std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   1641      dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   1642      /*use_callback=*/false, /*set_buffer_early=*/true,
   1643      /*use_resizable_runner=*/false, /*require_boxes=*/false,
   1644      /*expect_success=*/true, /*icc=*/&icc_data);
   1645  JxlDecoderReset(dec);
   1646  EXPECT_EQ(num_pixels * channels * 4, pixels2.size());
   1647 
   1648  // The input pixels use the profile matching GetIccTestProfile, since we set
   1649  // add_icc_profile for CreateTestJXLCodestream to true.
   1650  jxl::ColorEncoding color_encoding0;
   1651  EXPECT_TRUE(color_encoding0.SetICC(GetIccTestProfile(), JxlGetDefaultCms()));
   1652  jxl::Span<const uint8_t> span0(pixels.data(), pixels.size());
   1653  jxl::CodecInOut io0{memory_manager};
   1654  ASSERT_TRUE(io0.SetSize(xsize, ysize));
   1655  EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0,
   1656                                  /*bits_per_sample=*/16, format_orig,
   1657                                  /*pool=*/nullptr, &io0.Main()));
   1658 
   1659  jxl::ColorEncoding color_encoding1;
   1660  jxl::IccBytes icc;
   1661  jxl::Bytes(icc_data).AppendTo(icc);
   1662  EXPECT_TRUE(color_encoding1.SetICC(std::move(icc), JxlGetDefaultCms()));
   1663  jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
   1664  jxl::CodecInOut io1{memory_manager};
   1665  ASSERT_TRUE(io1.SetSize(xsize, ysize));
   1666  EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
   1667                                  /*bits_per_sample=*/32, format,
   1668                                  /*pool=*/nullptr, &io1.Main()));
   1669 
   1670  jxl::ButteraugliParams butteraugli_params;
   1671  EXPECT_SLIGHTLY_BELOW(
   1672      ButteraugliDistance(io0.frames, io1.frames, butteraugli_params,
   1673                          *JxlGetDefaultCms(),
   1674                          /*distmap=*/nullptr, nullptr),
   1675      0.58f);
   1676 
   1677  JxlDecoderDestroy(dec);
   1678 }
   1679 
   1680 std::string ColorDescription(JxlColorEncoding c) {
   1681  jxl::ColorEncoding color_encoding;
   1682  EXPECT_TRUE(color_encoding.FromExternal(c));
   1683  return Description(color_encoding);
   1684 }
   1685 
   1686 std::string GetOrigProfile(JxlDecoder* dec) {
   1687  JxlColorEncoding c;
   1688  JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_ORIGINAL;
   1689  EXPECT_EQ(JXL_DEC_SUCCESS,
   1690            JxlDecoderGetColorAsEncodedProfile(dec, target, &c));
   1691  return ColorDescription(c);
   1692 }
   1693 
   1694 std::string GetDataProfile(JxlDecoder* dec) {
   1695  JxlColorEncoding c;
   1696  JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA;
   1697  EXPECT_EQ(JXL_DEC_SUCCESS,
   1698            JxlDecoderGetColorAsEncodedProfile(dec, target, &c));
   1699  return ColorDescription(c);
   1700 }
   1701 
   1702 double ButteraugliDistance(size_t xsize, size_t ysize,
   1703                           const std::vector<uint8_t>& pixels_in,
   1704                           const jxl::ColorEncoding& color_in,
   1705                           float intensity_in,
   1706                           const std::vector<uint8_t>& pixels_out,
   1707                           const jxl::ColorEncoding& color_out,
   1708                           float intensity_out) {
   1709  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   1710  jxl::CodecInOut in{memory_manager};
   1711  in.metadata.m.color_encoding = color_in;
   1712  in.metadata.m.SetIntensityTarget(intensity_in);
   1713  JxlPixelFormat format_in = {static_cast<uint32_t>(color_in.Channels()),
   1714                              JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1715  EXPECT_TRUE(jxl::ConvertFromExternal(
   1716      jxl::Bytes(pixels_in.data(), pixels_in.size()), xsize, ysize, color_in,
   1717      /*bits_per_sample=*/16, format_in,
   1718      /*pool=*/nullptr, &in.Main()));
   1719  jxl::CodecInOut out{memory_manager};
   1720  out.metadata.m.color_encoding = color_out;
   1721  out.metadata.m.SetIntensityTarget(intensity_out);
   1722  JxlPixelFormat format_out = {static_cast<uint32_t>(color_out.Channels()),
   1723                               JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1724  EXPECT_TRUE(jxl::ConvertFromExternal(
   1725      jxl::Bytes(pixels_out.data(), pixels_out.size()), xsize, ysize, color_out,
   1726      /*bits_per_sample=*/16, format_out,
   1727      /*pool=*/nullptr, &out.Main()));
   1728  return ButteraugliDistance(in.frames, out.frames, jxl::ButteraugliParams(),
   1729                             *JxlGetDefaultCms(), nullptr, nullptr);
   1730 }
   1731 
   1732 class DecodeAllEncodingsTest
   1733    : public ::testing::TestWithParam<jxl::test::ColorEncodingDescriptor> {};
   1734 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
   1735    DecodeAllEncodingsTestInstantiation, DecodeAllEncodingsTest,
   1736    ::testing::ValuesIn(jxl::test::AllEncodings()));
   1737 TEST_P(DecodeAllEncodingsTest, PreserveOriginalProfileTest) {
   1738  size_t xsize = 123;
   1739  size_t ysize = 77;
   1740  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   1741  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1742  int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
   1743  const auto& cdesc = GetParam();
   1744  jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(cdesc);
   1745  if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return;
   1746  std::string color_space_in = Description(c_in);
   1747  float intensity_in = c_in.Tf().IsPQ() ? 10000 : 255;
   1748  printf("Testing input color space %s\n", color_space_in.c_str());
   1749  jxl::TestCodestreamParams params;
   1750  params.color_space = color_space_in;
   1751  params.intensity_target = intensity_in;
   1752  std::vector<uint8_t> data = jxl::CreateTestJXLCodestream(
   1753      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   1754  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1755  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events));
   1756  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), data.size()));
   1757  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   1758  JxlBasicInfo info;
   1759  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   1760  EXPECT_EQ(xsize, info.xsize);
   1761  EXPECT_EQ(ysize, info.ysize);
   1762  EXPECT_FALSE(info.uses_original_profile);
   1763  EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   1764  EXPECT_EQ(GetOrigProfile(dec), color_space_in);
   1765  EXPECT_EQ(GetDataProfile(dec), color_space_in);
   1766  EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   1767  std::vector<uint8_t> out(pixels.size());
   1768  EXPECT_EQ(JXL_DEC_SUCCESS,
   1769            JxlDecoderSetImageOutBuffer(dec, &format, out.data(), out.size()));
   1770  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   1771  double dist = ButteraugliDistance(xsize, ysize, pixels, c_in, intensity_in,
   1772                                    out, c_in, intensity_in);
   1773  EXPECT_LT(dist, 1.29);
   1774  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   1775  JxlDecoderDestroy(dec);
   1776 }
   1777 
   1778 namespace {
   1779 void SetPreferredColorProfileTest(
   1780    const jxl::test::ColorEncodingDescriptor& from, bool icc_dst,
   1781    bool use_cms) {
   1782  size_t xsize = 123;
   1783  size_t ysize = 77;
   1784  int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
   1785  jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(from);
   1786  if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return;
   1787  if (c_in.GetWhitePointType() != jxl::WhitePoint::kD65) return;
   1788  uint32_t num_channels = c_in.Channels();
   1789  std::vector<uint8_t> pixels =
   1790      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   1791 
   1792  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1793  std::string color_space_in = Description(c_in);
   1794  float intensity_in = c_in.Tf().IsPQ() ? 10000 : 255;
   1795  jxl::TestCodestreamParams params;
   1796  params.color_space = color_space_in;
   1797  params.intensity_target = intensity_in;
   1798  std::vector<uint8_t> data =
   1799      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   1800                                   xsize, ysize, num_channels, params);
   1801  auto all_encodings = jxl::test::AllEncodings();
   1802  // TODO(firsching): understand why XYB does not work together with icc_dst.
   1803  if (!icc_dst) {
   1804    all_encodings.push_back(
   1805        {jxl::ColorSpace::kXYB, jxl::WhitePoint::kD65, jxl::Primaries::kCustom,
   1806         jxl::TransferFunction::kUnknown, jxl::RenderingIntent::kPerceptual});
   1807  }
   1808  for (const auto& c1 : all_encodings) {
   1809    jxl::ColorEncoding c_out = jxl::test::ColorEncodingFromDescriptor(c1);
   1810    float intensity_out = intensity_in;
   1811    if (c_out.GetColorSpace() != jxl::ColorSpace::kXYB) {
   1812      if (c_out.GetRenderingIntent() != jxl::RenderingIntent::kRelative) {
   1813        continue;
   1814      }
   1815      if ((c_in.GetPrimariesType() == jxl::Primaries::k2100 &&
   1816           c_out.GetPrimariesType() != jxl::Primaries::k2100) ||
   1817          (c_in.GetPrimariesType() == jxl::Primaries::kP3 &&
   1818           c_out.GetPrimariesType() == jxl::Primaries::kSRGB)) {
   1819        // Converting to a narrower gamut does not work without gamut mapping.
   1820        continue;
   1821      }
   1822    }
   1823    if (c_out.Tf().IsHLG() && intensity_out > 300) {
   1824      // The Linear->HLG OOTF function at this intensity level can push
   1825      // saturated colors out of gamut, so we would need gamut mapping in
   1826      // this case too.
   1827      continue;
   1828    }
   1829    std::string color_space_out = Description(c_out);
   1830    if (color_space_in == color_space_out) continue;
   1831    printf("Testing input color space %s with output color space %s\n",
   1832           color_space_in.c_str(), color_space_out.c_str());
   1833    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1834    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events));
   1835    EXPECT_EQ(JXL_DEC_SUCCESS,
   1836              JxlDecoderSetInput(dec, data.data(), data.size()));
   1837    EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   1838    JxlBasicInfo info;
   1839    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   1840    EXPECT_EQ(xsize, info.xsize);
   1841    EXPECT_EQ(ysize, info.ysize);
   1842    EXPECT_FALSE(info.uses_original_profile);
   1843    EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   1844    EXPECT_EQ(GetOrigProfile(dec), color_space_in);
   1845    JxlColorEncoding encoding_out;
   1846    EXPECT_TRUE(jxl::ParseDescription(color_space_out, &encoding_out));
   1847    if (c_out.GetColorSpace() == jxl::ColorSpace::kXYB &&
   1848        (c_in.GetPrimariesType() != jxl::Primaries::kSRGB ||
   1849         c_in.Tf().IsPQ())) {
   1850      EXPECT_EQ(JXL_DEC_ERROR,
   1851                JxlDecoderSetPreferredColorProfile(dec, &encoding_out));
   1852      JxlDecoderDestroy(dec);
   1853      continue;
   1854    }
   1855    if (use_cms) {
   1856      JxlDecoderSetCms(dec, *JxlGetDefaultCms());
   1857    }
   1858    if (icc_dst) {
   1859      jxl::ColorEncoding internal_encoding_out;
   1860      EXPECT_TRUE(internal_encoding_out.FromExternal(encoding_out));
   1861      EXPECT_TRUE(internal_encoding_out.CreateICC());
   1862      std::vector<uint8_t> rewritten_icc = internal_encoding_out.ICC();
   1863 
   1864      EXPECT_EQ(use_cms ? JXL_DEC_SUCCESS : JXL_DEC_ERROR,
   1865                JxlDecoderSetOutputColorProfile(
   1866                    dec, nullptr, rewritten_icc.data(), rewritten_icc.size()));
   1867      if (!use_cms) {
   1868        // continue if we don't have a cms here
   1869        JxlDecoderDestroy(dec);
   1870        continue;
   1871      }
   1872    } else {
   1873      EXPECT_EQ(JXL_DEC_SUCCESS,
   1874                JxlDecoderSetPreferredColorProfile(dec, &encoding_out));
   1875    }
   1876    EXPECT_EQ(GetOrigProfile(dec), color_space_in);
   1877    if (icc_dst) {
   1878    } else {
   1879      EXPECT_EQ(GetDataProfile(dec), color_space_out);
   1880    }
   1881    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   1882    size_t buffer_size;
   1883    JxlPixelFormat out_format = format;
   1884    out_format.num_channels = c_out.Channels();
   1885    EXPECT_EQ(JXL_DEC_SUCCESS,
   1886              JxlDecoderImageOutBufferSize(dec, &out_format, &buffer_size));
   1887    std::vector<uint8_t> out(buffer_size);
   1888    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   1889                                   dec, &out_format, out.data(), out.size()));
   1890    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   1891    double dist = ButteraugliDistance(xsize, ysize, pixels, c_in, intensity_in,
   1892                                      out, c_out, intensity_out);
   1893 
   1894    if (c_in.GetWhitePointType() == c_out.GetWhitePointType()) {
   1895      EXPECT_LT(dist, 1.29);
   1896    } else {
   1897      EXPECT_LT(dist, 4.0);
   1898    }
   1899    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   1900    JxlDecoderDestroy(dec);
   1901  }
   1902 }
   1903 }  // namespace
   1904 
   1905 TEST(DecodeTest, SetPreferredColorProfileTestFromGray) {
   1906  jxl::test::ColorEncodingDescriptor gray = {
   1907      jxl::ColorSpace::kGray, jxl::WhitePoint::kD65, jxl::Primaries::kSRGB,
   1908      jxl::TransferFunction::kSRGB, jxl::RenderingIntent::kRelative};
   1909  SetPreferredColorProfileTest(gray, true, true);
   1910  SetPreferredColorProfileTest(gray, false, true);
   1911  SetPreferredColorProfileTest(gray, true, false);
   1912  SetPreferredColorProfileTest(gray, false, false);
   1913 }
   1914 
   1915 static std::string DecodeAllEncodingsVariantsTestName(
   1916    const ::testing::TestParamInfo<
   1917        std::tuple<jxl::test::ColorEncodingDescriptor, bool, bool>>& info) {
   1918  const auto& encoding = std::get<0>(info.param);
   1919  bool icc_dst = std::get<1>(info.param);
   1920  bool use_cms = std::get<2>(info.param);
   1921 
   1922  std::string encoding_name =
   1923      Description(ColorEncodingFromDescriptor(encoding));
   1924 
   1925  return "From_" + encoding_name +
   1926         (icc_dst ? "_with_icc_dst" : "_without_icc_dst") +
   1927         (use_cms ? "_with_cms" : "_without_cms");
   1928 }
   1929 
   1930 class DecodeAllEncodingsVariantsTest
   1931    : public ::testing::TestWithParam<
   1932          std::tuple<jxl::test::ColorEncodingDescriptor, bool, bool>> {};
   1933 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
   1934    DecodeAllEncodingsVariantsTestInstantiation, DecodeAllEncodingsVariantsTest,
   1935    ::testing::Combine(::testing::ValuesIn(jxl::test::AllEncodings()),
   1936                       ::testing::Bool(), ::testing::Bool()),
   1937    DecodeAllEncodingsVariantsTestName);
   1938 TEST_P(DecodeAllEncodingsVariantsTest, SetPreferredColorProfileTest) {
   1939  const auto& from = std::get<0>(GetParam());
   1940  bool icc_dst = std::get<1>(GetParam());
   1941  bool use_cms = std::get<2>(GetParam());
   1942  SetPreferredColorProfileTest(from, icc_dst, use_cms);
   1943 }
   1944 
   1945 void DecodeImageWithColorEncoding(const std::vector<uint8_t>& compressed,
   1946                                  jxl::ColorEncoding& color_encoding,
   1947                                  bool with_cms, std::vector<uint8_t>& out,
   1948                                  JxlBasicInfo& info) {
   1949  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   1950  int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
   1951  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events));
   1952  EXPECT_EQ(JXL_DEC_SUCCESS,
   1953            JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   1954  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   1955  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   1956  EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   1957  // TODO(eustas): why unused?
   1958  std::string color_space_in = GetOrigProfile(dec);
   1959  if (with_cms) {
   1960    JxlDecoderSetCms(dec, *JxlGetDefaultCms());
   1961    EXPECT_TRUE(color_encoding.CreateICC());
   1962    std::vector<uint8_t> rewritten_icc = color_encoding.ICC();
   1963    EXPECT_EQ(JXL_DEC_SUCCESS,
   1964              JxlDecoderSetOutputColorProfile(
   1965                  dec, nullptr, rewritten_icc.data(), rewritten_icc.size()));
   1966  } else {
   1967    JxlColorEncoding external_color_encoding = color_encoding.ToExternal();
   1968    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetOutputColorProfile(
   1969                                   dec, &external_color_encoding, nullptr, 0));
   1970  }
   1971  EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   1972 
   1973  size_t buffer_size;
   1974  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   1975 
   1976  JxlPixelFormat out_format = format;
   1977  out_format.num_channels = color_encoding.Channels();
   1978  EXPECT_EQ(JXL_DEC_SUCCESS,
   1979            JxlDecoderImageOutBufferSize(dec, &out_format, &buffer_size));
   1980  out.resize(buffer_size);
   1981  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   1982                                 dec, &out_format, out.data(), out.size()));
   1983  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   1984  JxlDecoderDestroy(dec);
   1985 }
   1986 
   1987 class DecodeAllEncodingsWithCMSTest
   1988    : public ::testing::TestWithParam<jxl::test::ColorEncodingDescriptor> {};
   1989 
   1990 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(
   1991    AllEncodings, DecodeAllEncodingsWithCMSTest,
   1992    testing::ValuesIn(jxl::test::AllEncodings()));
   1993 
   1994 TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) {
   1995  auto all_encodings = jxl::test::AllEncodings();
   1996  uint32_t num_channels = 3;
   1997  size_t xsize = 177;
   1998  size_t ysize = 123;
   1999  std::vector<uint8_t> pixels =
   2000      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   2001  jxl::TestCodestreamParams params;
   2002  std::vector<uint8_t> data =
   2003      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   2004                                   xsize, ysize, num_channels, params);
   2005 
   2006  jxl::ColorEncoding color_encoding =
   2007      jxl::test::ColorEncodingFromDescriptor(GetParam());
   2008  fprintf(stderr, "color_description: %s\n",
   2009          Description(color_encoding).c_str());
   2010 
   2011  std::vector<uint8_t> out_with_cms;
   2012  JxlBasicInfo info_with_cms;
   2013  DecodeImageWithColorEncoding(data, color_encoding, true, out_with_cms,
   2014                               info_with_cms);
   2015 
   2016  std::vector<uint8_t> out_without_cms;
   2017  JxlBasicInfo info_without_cms;
   2018  DecodeImageWithColorEncoding(data, color_encoding, false, out_without_cms,
   2019                               info_without_cms);
   2020 
   2021  EXPECT_EQ(info_with_cms.xsize, info_without_cms.xsize);
   2022  EXPECT_EQ(info_with_cms.ysize, info_without_cms.ysize);
   2023  EXPECT_EQ(out_with_cms.size(), out_without_cms.size());
   2024  double dist = ButteraugliDistance(xsize, ysize, out_with_cms, color_encoding,
   2025                                    255, out_without_cms, color_encoding, 255);
   2026 
   2027  EXPECT_LT(dist, .1);
   2028 }
   2029 
   2030 // Tests the case of lossy sRGB image without alpha channel, decoded to RGB8
   2031 // and to RGBA8
   2032 TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
   2033  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   2034  for (unsigned channels = 3; channels <= 4; channels++) {
   2035    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2036 
   2037    size_t xsize = 123;
   2038    size_t ysize = 77;
   2039    size_t num_pixels = xsize * ysize;
   2040    std::vector<uint8_t> pixels =
   2041        jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2042    JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2043    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2044        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3,
   2045        jxl::TestCodestreamParams());
   2046 
   2047    JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
   2048 
   2049    std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2050        dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   2051        /*use_callback=*/true, /*set_buffer_early=*/false,
   2052        /*use_resizable_runner=*/false, /*require_boxes=*/false,
   2053        /*expect_success*/ true);
   2054    JxlDecoderReset(dec);
   2055    EXPECT_EQ(num_pixels * channels, pixels2.size());
   2056 
   2057    jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false);
   2058    jxl::Span<const uint8_t> span0(pixels.data(), pixels.size());
   2059    jxl::CodecInOut io0{memory_manager};
   2060    ASSERT_TRUE(io0.SetSize(xsize, ysize));
   2061    EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0,
   2062                                    /*bits_per_sample=*/16, format_orig,
   2063                                    /*pool=*/nullptr, &io0.Main()));
   2064 
   2065    jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false);
   2066    jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
   2067    jxl::CodecInOut io1{memory_manager};
   2068    EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
   2069                                    /*bits_per_sample=*/8, format,
   2070                                    /*pool=*/nullptr, &io1.Main()));
   2071 
   2072    jxl::ButteraugliParams butteraugli_params;
   2073    EXPECT_SLIGHTLY_BELOW(
   2074        ButteraugliDistance(io0.frames, io1.frames, butteraugli_params,
   2075                            *JxlGetDefaultCms(),
   2076                            /*distmap=*/nullptr, nullptr),
   2077        0.6f);
   2078 
   2079    JxlDecoderDestroy(dec);
   2080  }
   2081 }
   2082 
   2083 // Opaque image with noise enabled, decoded to RGB8 and RGBA8.
   2084 TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) {
   2085  for (unsigned channels = 3; channels <= 4; channels++) {
   2086    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2087 
   2088    size_t xsize = 512;
   2089    size_t ysize = 300;
   2090    size_t num_pixels = xsize * ysize;
   2091    std::vector<uint8_t> pixels =
   2092        jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2093    JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2094    jxl::TestCodestreamParams params;
   2095    params.cparams.noise = jxl::Override::kOn;
   2096    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2097        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   2098 
   2099    JxlPixelFormat format = {channels, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
   2100 
   2101    std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2102        dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   2103        /*use_callback=*/false, /*set_buffer_early=*/true,
   2104        /*use_resizable_runner=*/false, /*require_boxes=*/false,
   2105        /*expect_success=*/true);
   2106    JxlDecoderReset(dec);
   2107    EXPECT_EQ(num_pixels * channels, pixels2.size());
   2108 
   2109    jxl::ColorEncoding color_encoding0 = jxl::ColorEncoding::SRGB(false);
   2110    jxl::Span<const uint8_t> span0(pixels.data(), pixels.size());
   2111    jxl::CodecInOut io0{jxl::test::MemoryManager()};
   2112    ASSERT_TRUE(io0.SetSize(xsize, ysize));
   2113    EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0,
   2114                                    /*bits_per_sample=*/16, format_orig,
   2115                                    /*pool=*/nullptr, &io0.Main()));
   2116 
   2117    jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false);
   2118    jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
   2119    jxl::CodecInOut io1{jxl::test::MemoryManager()};
   2120    EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
   2121                                    /*bits_per_sample=*/8, format,
   2122                                    /*pool=*/nullptr, &io1.Main()));
   2123 
   2124    jxl::ButteraugliParams butteraugli_params;
   2125    EXPECT_SLIGHTLY_BELOW(
   2126        ButteraugliDistance(io0.frames, io1.frames, butteraugli_params,
   2127                            *JxlGetDefaultCms(),
   2128                            /*distmap=*/nullptr, nullptr),
   2129        1.4f);
   2130 
   2131    JxlDecoderDestroy(dec);
   2132  }
   2133 }
   2134 
   2135 TEST(DecodeTest, ProcessEmptyInputWithBoxes) {
   2136  size_t xsize = 123;
   2137  size_t ysize = 77;
   2138  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2139  jxl::CompressParams cparams;
   2140  uint32_t channels = 3;
   2141  JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
   2142  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2143    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2144    jxl::TestCodestreamParams params;
   2145    params.box_format = static_cast<CodeStreamBoxFormat>(i);
   2146    printf("Testing empty input with box format %d\n",
   2147           static_cast<int>(params.box_format));
   2148    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2149        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   2150    const int events =
   2151        JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_COLOR_ENCODING;
   2152    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events));
   2153    EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   2154    EXPECT_EQ(JXL_DEC_SUCCESS,
   2155              JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   2156    EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2157    EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   2158    size_t buffer_size;
   2159    EXPECT_EQ(JXL_DEC_SUCCESS,
   2160              JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   2161    JxlBasicInfo info;
   2162    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2163    const size_t remaining = JxlDecoderReleaseInput(dec);
   2164    EXPECT_LE(remaining, compressed.size());
   2165    EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   2166    JxlDecoderDestroy(dec);
   2167  }
   2168 }
   2169 
   2170 TEST(DecodeTest, ExtraBytesAfterCompressedStream) {
   2171  size_t xsize = 123;
   2172  size_t ysize = 77;
   2173  size_t num_pixels = xsize * ysize;
   2174  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2175  jxl::CompressParams cparams;
   2176  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2177    CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i);
   2178    if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
   2179    printf("Testing with box format %d\n", static_cast<int>(box_format));
   2180    size_t last_unknown_box_size = 0;
   2181    if (box_format == kCSBF_Single_Other) {
   2182      last_unknown_box_size = unk1_box_size + 8;
   2183    } else if (box_format == kCSBF_Multi_Other_Terminated) {
   2184      last_unknown_box_size = unk3_box_size + 8;
   2185    } else if (box_format == kCSBF_Multi_Last_Empty_Other) {
   2186      // If boxes are not required, the decoder won't consume the last empty
   2187      // jxlp box.
   2188      last_unknown_box_size = 12 + unk3_box_size + 8;
   2189    }
   2190    jxl::TestCodestreamParams params;
   2191    params.box_format = box_format;
   2192    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2193        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   2194    // Add some more bytes after compressed data.
   2195    compressed.push_back(0);
   2196    compressed.push_back(1);
   2197    compressed.push_back(2);
   2198    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2199    uint32_t channels = 3;
   2200    JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
   2201    std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2202        dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   2203        /*use_callback=*/false, /*set_buffer_early=*/true,
   2204        /*use_resizable_runner=*/false, /*require_boxes=*/false,
   2205        /*expect_success=*/true);
   2206    size_t unconsumed_bytes = JxlDecoderReleaseInput(dec);
   2207    EXPECT_EQ(last_unknown_box_size + 3, unconsumed_bytes);
   2208    EXPECT_EQ(num_pixels * channels * 4, pixels2.size());
   2209    JxlDecoderDestroy(dec);
   2210  }
   2211 }
   2212 
   2213 TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) {
   2214  size_t xsize = 123;
   2215  size_t ysize = 77;
   2216  size_t num_pixels = xsize * ysize;
   2217  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2218  jxl::CompressParams cparams;
   2219  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2220    CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i);
   2221    if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
   2222    printf("Testing with box format %d\n", static_cast<int>(box_format));
   2223    bool expect_success = (box_format == kCSBF_None ||
   2224                           box_format == kCSBF_Single_Zero_Terminated ||
   2225                           box_format == kCSBF_Multi_Zero_Terminated);
   2226    jxl::TestCodestreamParams params;
   2227    params.box_format = box_format;
   2228    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2229        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   2230    // Add some more bytes after compressed data.
   2231    compressed.push_back(0);
   2232    compressed.push_back(1);
   2233    compressed.push_back(2);
   2234    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2235    uint32_t channels = 3;
   2236    JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
   2237    std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2238        dec, jxl::Bytes(compressed.data(), compressed.size()), format,
   2239        /*use_callback=*/false, /*set_buffer_early=*/true,
   2240        /*use_resizable_runner=*/false, /*require_boxes=*/true, expect_success);
   2241    size_t unconsumed_bytes = JxlDecoderReleaseInput(dec);
   2242    EXPECT_EQ(3, unconsumed_bytes);
   2243    EXPECT_EQ(num_pixels * channels * 4, pixels2.size());
   2244    JxlDecoderDestroy(dec);
   2245  }
   2246 }
   2247 
   2248 TEST(DecodeTest, ConcatenatedCompressedStreams) {
   2249  size_t xsize = 123;
   2250  size_t ysize = 77;
   2251  size_t num_pixels = xsize * ysize;
   2252  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2253  jxl::CompressParams cparams;
   2254  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2255    CodeStreamBoxFormat first_box_format = static_cast<CodeStreamBoxFormat>(i);
   2256    if (first_box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
   2257    jxl::TestCodestreamParams params1;
   2258    params1.box_format = first_box_format;
   2259    std::vector<uint8_t> compressed1 = jxl::CreateTestJXLCodestream(
   2260        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params1);
   2261    for (int j = 0; j < kCSBF_NUM_ENTRIES; ++j) {
   2262      CodeStreamBoxFormat second_box_format =
   2263          static_cast<CodeStreamBoxFormat>(j);
   2264      if (second_box_format == kCSBF_Multi_Other_Zero_Terminated) continue;
   2265      printf("Testing with box format pair %d, %d\n",
   2266             static_cast<int>(first_box_format),
   2267             static_cast<int>(second_box_format));
   2268      jxl::TestCodestreamParams params2;
   2269      params2.box_format = second_box_format;
   2270      std::vector<uint8_t> compressed2 = jxl::CreateTestJXLCodestream(
   2271          jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params2);
   2272      std::vector<uint8_t> concat;
   2273      jxl::Bytes(compressed1).AppendTo(concat);
   2274      jxl::Bytes(compressed2).AppendTo(concat);
   2275      uint32_t channels = 3;
   2276      JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
   2277      size_t remaining = concat.size();
   2278      for (int part = 0; part < 2; ++part) {
   2279        printf("  Decoding part %d\n", part + 1);
   2280        JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2281        size_t pos = concat.size() - remaining;
   2282        bool expect_success =
   2283            (part == 0 || second_box_format == kCSBF_None ||
   2284             second_box_format == kCSBF_Single_Zero_Terminated ||
   2285             second_box_format == kCSBF_Multi_Zero_Terminated);
   2286        std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2287            dec, jxl::Bytes(concat.data() + pos, remaining), format,
   2288            /*use_callback=*/false, /*set_buffer_early=*/true,
   2289            /*use_resizable_runner=*/false, /*require_boxes=*/true,
   2290            expect_success);
   2291        EXPECT_EQ(num_pixels * channels * 4, pixels2.size());
   2292        remaining = JxlDecoderReleaseInput(dec);
   2293        JxlDecoderDestroy(dec);
   2294      }
   2295      EXPECT_EQ(0, remaining);
   2296    }
   2297  }
   2298 }
   2299 
   2300 void TestPartialStream(bool reconstructible_jpeg) {
   2301  size_t xsize = 123;
   2302  size_t ysize = 77;
   2303  uint32_t channels = 4;
   2304  if (reconstructible_jpeg) {
   2305    channels = 3;
   2306  }
   2307  std::vector<uint8_t> pixels =
   2308      jxl::test::GetSomeTestImage(xsize, ysize, channels, 0);
   2309  JxlPixelFormat format_orig = {channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2310  jxl::TestCodestreamParams params;
   2311  if (reconstructible_jpeg) {
   2312    params.cparams.color_transform = jxl::ColorTransform::kNone;
   2313  } else {
   2314    // Lossless to verify pixels exactly after roundtrip.
   2315    params.cparams.SetLossless();
   2316  }
   2317 
   2318  std::vector<uint8_t> pixels2;
   2319  pixels2.resize(pixels.size());
   2320 
   2321  std::vector<uint8_t> jpeg_output(64);
   2322  size_t used_jpeg_output = 0;
   2323 
   2324  std::vector<std::vector<uint8_t>> codestreams(kCSBF_NUM_ENTRIES);
   2325  std::vector<std::vector<uint8_t>> jpeg_codestreams(kCSBF_NUM_ENTRIES);
   2326  for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2327    params.box_format = static_cast<CodeStreamBoxFormat>(i);
   2328    if (reconstructible_jpeg) {
   2329      params.jpeg_codestream = &jpeg_codestreams[i];
   2330    }
   2331    codestreams[i] =
   2332        jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   2333                                     xsize, ysize, channels, params);
   2334  }
   2335 
   2336  // Test multiple step sizes, to test different combinations of the streaming
   2337  // box parsing.
   2338  std::vector<size_t> increments = {1, 3, 17, 23, 120, 700, 1050};
   2339 
   2340  for (size_t increment : increments) {
   2341    for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   2342      if (reconstructible_jpeg && static_cast<CodeStreamBoxFormat>(i) ==
   2343                                      CodeStreamBoxFormat::kCSBF_None) {
   2344        continue;
   2345      }
   2346      const std::vector<uint8_t>& data = codestreams[i];
   2347      const uint8_t* next_in = data.data();
   2348      size_t avail_in = 0;
   2349 
   2350      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2351 
   2352      EXPECT_EQ(JXL_DEC_SUCCESS,
   2353                JxlDecoderSubscribeEvents(
   2354                    dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE |
   2355                             JXL_DEC_JPEG_RECONSTRUCTION));
   2356 
   2357      bool seen_basic_info = false;
   2358      bool seen_full_image = false;
   2359      bool seen_jpeg_recon = false;
   2360 
   2361      size_t total_size = 0;
   2362 
   2363      for (;;) {
   2364        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   2365        JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   2366        size_t remaining = JxlDecoderReleaseInput(dec);
   2367        EXPECT_LE(remaining, avail_in);
   2368        next_in += avail_in - remaining;
   2369        avail_in = remaining;
   2370        if (status == JXL_DEC_NEED_MORE_INPUT) {
   2371          if (total_size >= data.size()) {
   2372            // End of test data reached, it should have successfully decoded the
   2373            // image now.
   2374            FAIL();
   2375            break;
   2376          }
   2377 
   2378          // End of the file reached, should be the final test.
   2379          if (total_size + increment > data.size()) {
   2380            increment = data.size() - total_size;
   2381          }
   2382          total_size += increment;
   2383          avail_in += increment;
   2384        } else if (status == JXL_DEC_BASIC_INFO) {
   2385          // This event should happen exactly once
   2386          EXPECT_FALSE(seen_basic_info);
   2387          if (seen_basic_info) break;
   2388          seen_basic_info = true;
   2389          JxlBasicInfo info;
   2390          EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2391          EXPECT_EQ(info.xsize, xsize);
   2392          EXPECT_EQ(info.ysize, ysize);
   2393        } else if (status == JXL_DEC_JPEG_RECONSTRUCTION) {
   2394          EXPECT_FALSE(seen_basic_info);
   2395          EXPECT_FALSE(seen_full_image);
   2396          EXPECT_EQ(JXL_DEC_SUCCESS,
   2397                    JxlDecoderSetJPEGBuffer(dec, jpeg_output.data(),
   2398                                            jpeg_output.size()));
   2399          seen_jpeg_recon = true;
   2400        } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) {
   2401          EXPECT_TRUE(seen_jpeg_recon);
   2402          used_jpeg_output =
   2403              jpeg_output.size() - JxlDecoderReleaseJPEGBuffer(dec);
   2404          jpeg_output.resize(jpeg_output.size() * 2);
   2405          EXPECT_EQ(JXL_DEC_SUCCESS,
   2406                    JxlDecoderSetJPEGBuffer(
   2407                        dec, jpeg_output.data() + used_jpeg_output,
   2408                        jpeg_output.size() - used_jpeg_output));
   2409        } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
   2410          EXPECT_EQ(JXL_DEC_SUCCESS,
   2411                    JxlDecoderSetImageOutBuffer(
   2412                        dec, &format_orig, pixels2.data(), pixels2.size()));
   2413        } else if (status == JXL_DEC_FULL_IMAGE) {
   2414          // This event should happen exactly once
   2415          EXPECT_FALSE(seen_full_image);
   2416          if (seen_full_image) break;
   2417          // This event should happen after basic info
   2418          EXPECT_TRUE(seen_basic_info);
   2419          seen_full_image = true;
   2420          if (reconstructible_jpeg) {
   2421            used_jpeg_output =
   2422                jpeg_output.size() - JxlDecoderReleaseJPEGBuffer(dec);
   2423            EXPECT_EQ(used_jpeg_output, jpeg_codestreams[i].size());
   2424            EXPECT_EQ(0, memcmp(jpeg_output.data(), jpeg_codestreams[i].data(),
   2425                                used_jpeg_output));
   2426          } else {
   2427            EXPECT_EQ(pixels, pixels2);
   2428          }
   2429        } else if (status == JXL_DEC_SUCCESS) {
   2430          EXPECT_TRUE(seen_full_image);
   2431          break;
   2432        } else {
   2433          // We do not expect any other events or errors
   2434          FAIL();
   2435          break;
   2436        }
   2437      }
   2438 
   2439      // Ensure the decoder emitted the basic info and full image events
   2440      EXPECT_TRUE(seen_basic_info);
   2441      EXPECT_TRUE(seen_full_image);
   2442 
   2443      JxlDecoderDestroy(dec);
   2444    }
   2445  }
   2446 }
   2447 
   2448 // Tests the return status when trying to decode pixels on incomplete file: it
   2449 // should return JXL_DEC_NEED_MORE_INPUT, not error.
   2450 TEST(DecodeTest, PixelPartialTest) { TestPartialStream(false); }
   2451 
   2452 // Tests the return status when trying to decode JPEG bytes on incomplete file.
   2453 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGPartialTest) {
   2454  TEST_LIBJPEG_SUPPORT();
   2455  TestPartialStream(true);
   2456 }
   2457 
   2458 // The DC event still exists, but is no longer implemented, it is deprecated.
   2459 TEST(DecodeTest, DCNotGettableTest) {
   2460  // 1x1 pixel JXL image
   2461  std::string compressed(
   2462      "\377\n\0\20\260\23\0H\200("
   2463      "\0\334\0U\17\0\0\250P\31e\334\340\345\\\317\227\37:,"
   2464      "\246m\\gh\253m\vK\22E\306\261I\252C&pH\22\353 "
   2465      "\363\6\22\bp\0\200\237\34\231W2d\255$\1",
   2466      68);
   2467 
   2468  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2469 
   2470  EXPECT_EQ(JXL_DEC_SUCCESS,
   2471            JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO));
   2472  EXPECT_EQ(JXL_DEC_SUCCESS,
   2473            JxlDecoderSetInput(
   2474                dec, reinterpret_cast<const uint8_t*>(compressed.data()),
   2475                compressed.size()));
   2476 
   2477  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2478 
   2479  // Since the image is only 1x1 pixel, there is only 1 group, the decoder is
   2480  // unable to get DC size from this, and will not return the DC at all. Since
   2481  // no full image is requested either, it is expected to return success.
   2482  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   2483 
   2484  JxlDecoderDestroy(dec);
   2485 }
   2486 
   2487 TEST(DecodeTest, PreviewTest) {
   2488  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   2489  size_t xsize = 77;
   2490  size_t ysize = 120;
   2491  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2492  JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2493  for (jxl::PreviewMode mode : {jxl::kSmallPreview, jxl::kBigPreview}) {
   2494    jxl::TestCodestreamParams params;
   2495    params.preview_mode = mode;
   2496 
   2497    std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2498        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params);
   2499 
   2500    JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
   2501 
   2502    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2503    const uint8_t* next_in = compressed.data();
   2504    size_t avail_in = compressed.size();
   2505 
   2506    EXPECT_EQ(JXL_DEC_SUCCESS,
   2507              JxlDecoderSubscribeEvents(
   2508                  dec, JXL_DEC_BASIC_INFO | JXL_DEC_PREVIEW_IMAGE));
   2509    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   2510 
   2511    EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2512    JxlBasicInfo info;
   2513    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2514    size_t buffer_size;
   2515    EXPECT_EQ(JXL_DEC_SUCCESS,
   2516              JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size));
   2517 
   2518    jxl::ColorEncoding c_srgb = jxl::ColorEncoding::SRGB(false);
   2519    jxl::CodecInOut io0{memory_manager};
   2520    EXPECT_TRUE(jxl::ConvertFromExternal(
   2521        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, c_srgb,
   2522        /*bits_per_sample=*/16, format_orig, /*pool=*/nullptr, &io0.Main()));
   2523    GeneratePreview(params.preview_mode, &io0.Main());
   2524 
   2525    size_t xsize_preview = io0.Main().xsize();
   2526    size_t ysize_preview = io0.Main().ysize();
   2527    EXPECT_EQ(xsize_preview, info.preview.xsize);
   2528    EXPECT_EQ(ysize_preview, info.preview.ysize);
   2529    EXPECT_EQ(xsize_preview * ysize_preview * 3, buffer_size);
   2530 
   2531    EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec));
   2532 
   2533    std::vector<uint8_t> preview(buffer_size);
   2534    EXPECT_EQ(JXL_DEC_SUCCESS,
   2535              JxlDecoderSetPreviewOutBuffer(dec, &format, preview.data(),
   2536                                            preview.size()));
   2537 
   2538    EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec));
   2539 
   2540    jxl::CodecInOut io1{memory_manager};
   2541    EXPECT_TRUE(
   2542        jxl::ConvertFromExternal(jxl::Bytes(preview.data(), preview.size()),
   2543                                 xsize_preview, ysize_preview, c_srgb,
   2544                                 /*bits_per_sample=*/8, format,
   2545                                 /*pool=*/nullptr, &io1.Main()));
   2546 
   2547    jxl::ButteraugliParams butteraugli_params;
   2548    // TODO(lode): this ButteraugliDistance silently returns 0 (dangerous for
   2549    // tests) if xsize or ysize is < 8, no matter how different the images, a
   2550    // tiny size that could happen for a preview. ButteraugliDiffmap does
   2551    // support smaller than 8x8, but jxl's ButteraugliDistance does not. Perhaps
   2552    // move butteraugli's <8x8 handling from ButteraugliDiffmap to
   2553    // ButteraugliComparator::Diffmap in butteraugli.cc.
   2554    EXPECT_LE(ButteraugliDistance(io0.frames, io1.frames, butteraugli_params,
   2555                                  *JxlGetDefaultCms(),
   2556                                  /*distmap=*/nullptr, nullptr),
   2557              mode == jxl::kSmallPreview ? 0.7f : 1.2f);
   2558 
   2559    JxlDecoderDestroy(dec);
   2560  }
   2561 }
   2562 
   2563 TEST(DecodeTest, AlignTest) {
   2564  size_t xsize = 123;
   2565  size_t ysize = 77;
   2566  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   2567  JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2568 
   2569  jxl::TestCodestreamParams params;
   2570  // Lossless to verify pixels exactly after roundtrip.
   2571  params.cparams.SetLossless();
   2572  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   2573  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2574      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   2575 
   2576  size_t align = 17;
   2577  JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align};
   2578  // On purpose not using jxl::RoundUpTo to test it independently.
   2579  size_t expected_line_size_last = 1 * 3 * xsize;
   2580  size_t expected_line_size =
   2581      ((expected_line_size_last + align - 1) / align) * align;
   2582  size_t expected_pixels_size =
   2583      expected_line_size * (ysize - 1) + expected_line_size_last;
   2584 
   2585  for (bool use_callback : {false, true}) {
   2586    std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI(
   2587        jxl::Bytes(compressed.data(), compressed.size()), format, use_callback,
   2588        /*set_buffer_early=*/false,
   2589        /*use_resizable_runner=*/false, /*require_boxes=*/false,
   2590        /*expect_success=*/true);
   2591    EXPECT_EQ(expected_pixels_size, pixels2.size());
   2592    EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   2593                                           ysize, format_orig, format));
   2594  }
   2595 }
   2596 
   2597 TEST(DecodeTest, AnimationTest) {
   2598  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   2599  size_t xsize = 123;
   2600  size_t ysize = 77;
   2601  static const size_t num_frames = 2;
   2602  std::vector<uint8_t> frames[2];
   2603  frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2604  frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1);
   2605  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2606 
   2607  jxl::CodecInOut io{memory_manager};
   2608  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2609  io.metadata.m.SetUintSamples(16);
   2610  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   2611  io.metadata.m.have_animation = true;
   2612  io.frames.clear();
   2613  io.frames.reserve(num_frames);
   2614  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2615 
   2616  std::vector<uint32_t> frame_durations(num_frames);
   2617  for (size_t i = 0; i < num_frames; ++i) {
   2618    frame_durations[i] = 5 + i;
   2619  }
   2620 
   2621  for (size_t i = 0; i < num_frames; ++i) {
   2622    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   2623 
   2624    EXPECT_TRUE(ConvertFromExternal(
   2625        jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize,
   2626        jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   2627        /*bits_per_sample=*/16, format,
   2628        /*pool=*/nullptr, &bundle));
   2629    bundle.duration = frame_durations[i];
   2630    io.frames.push_back(std::move(bundle));
   2631  }
   2632 
   2633  jxl::CompressParams cparams;
   2634  cparams.SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   2635  cparams.speed_tier = jxl::SpeedTier::kThunder;
   2636  std::vector<uint8_t> compressed;
   2637  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   2638 
   2639  // Decode and test the animation frames
   2640 
   2641  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2642  const uint8_t* next_in = compressed.data();
   2643  size_t avail_in = compressed.size();
   2644 
   2645  void* runner = JxlThreadParallelRunnerCreate(
   2646      nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   2647  EXPECT_EQ(JXL_DEC_SUCCESS,
   2648            JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
   2649 
   2650  EXPECT_EQ(JXL_DEC_SUCCESS,
   2651            JxlDecoderSubscribeEvents(
   2652                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   2653  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   2654 
   2655  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2656  size_t buffer_size;
   2657  EXPECT_EQ(JXL_DEC_SUCCESS,
   2658            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   2659  JxlBasicInfo info;
   2660  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2661 
   2662  for (size_t i = 0; i < num_frames; ++i) {
   2663    std::vector<uint8_t> pixels(buffer_size);
   2664 
   2665    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   2666 
   2667    JxlFrameHeader frame_header;
   2668    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   2669    EXPECT_EQ(frame_durations[i], frame_header.duration);
   2670    EXPECT_EQ(0u, frame_header.name_length);
   2671    // For now, test with empty name, there's currently no easy way to encode
   2672    // a jxl file with a frame name because ImageBundle doesn't have a
   2673    // jxl::FrameHeader to set the name in. We can test the null termination
   2674    // character though.
   2675    char name;
   2676    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameName(dec, &name, 1));
   2677    EXPECT_EQ(0, name);
   2678 
   2679    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   2680 
   2681    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   2682 
   2683    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   2684                                   dec, &format, pixels.data(), pixels.size()));
   2685 
   2686    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   2687    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   2688                                           xsize, ysize, format, format));
   2689  }
   2690 
   2691  // After all frames were decoded, JxlDecoderProcessInput should return
   2692  // success to indicate all is done.
   2693  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   2694 
   2695  JxlThreadParallelRunnerDestroy(runner);
   2696  JxlDecoderDestroy(dec);
   2697 }
   2698 
   2699 TEST(DecodeTest, AnimationTestStreaming) {
   2700  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   2701  size_t xsize = 123;
   2702  size_t ysize = 77;
   2703  static const size_t num_frames = 2;
   2704  std::vector<uint8_t> frames[2];
   2705  frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
   2706  frames[1] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 1);
   2707  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2708 
   2709  jxl::CodecInOut io{memory_manager};
   2710  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2711  io.metadata.m.SetUintSamples(16);
   2712  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   2713  io.metadata.m.have_animation = true;
   2714  io.frames.clear();
   2715  io.frames.reserve(num_frames);
   2716  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2717 
   2718  std::vector<uint32_t> frame_durations(num_frames);
   2719  for (size_t i = 0; i < num_frames; ++i) {
   2720    frame_durations[i] = 5 + i;
   2721  }
   2722 
   2723  for (size_t i = 0; i < num_frames; ++i) {
   2724    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   2725 
   2726    EXPECT_TRUE(ConvertFromExternal(
   2727        jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize,
   2728        jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   2729        /*bits_per_sample=*/16, format,
   2730        /*pool=*/nullptr, &bundle));
   2731    bundle.duration = frame_durations[i];
   2732    io.frames.push_back(std::move(bundle));
   2733  }
   2734 
   2735  jxl::CompressParams cparams;
   2736  cparams.SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   2737  cparams.speed_tier = jxl::SpeedTier::kThunder;
   2738  std::vector<uint8_t> compressed;
   2739  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   2740 
   2741  // Decode and test the animation frames
   2742 
   2743  const size_t step_size = 16;
   2744 
   2745  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2746  const uint8_t* next_in = compressed.data();
   2747  size_t avail_in = 0;
   2748  size_t frame_headers_seen = 0;
   2749  size_t frames_seen = 0;
   2750  bool seen_basic_info = false;
   2751 
   2752  void* runner = JxlThreadParallelRunnerCreate(
   2753      nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   2754  EXPECT_EQ(JXL_DEC_SUCCESS,
   2755            JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
   2756 
   2757  EXPECT_EQ(JXL_DEC_SUCCESS,
   2758            JxlDecoderSubscribeEvents(
   2759                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   2760 
   2761  std::vector<uint8_t> frames2[2];
   2762  for (size_t i = 0; i < num_frames; ++i) {
   2763    frames2[i].resize(frames[i].size());
   2764  }
   2765 
   2766  size_t total_in = 0;
   2767  size_t loop_count = 0;
   2768 
   2769  for (;;) {
   2770    if (loop_count++ > compressed.size()) {
   2771      fprintf(stderr, "Too many loops\n");
   2772      FAIL();
   2773      break;
   2774    }
   2775 
   2776    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   2777    auto status = JxlDecoderProcessInput(dec);
   2778    size_t remaining = JxlDecoderReleaseInput(dec);
   2779    EXPECT_LE(remaining, avail_in);
   2780    next_in += avail_in - remaining;
   2781    avail_in = remaining;
   2782 
   2783    if (status == JXL_DEC_SUCCESS) {
   2784      break;
   2785    } else if (status == JXL_DEC_ERROR) {
   2786      FAIL();
   2787    } else if (status == JXL_DEC_NEED_MORE_INPUT) {
   2788      if (total_in >= compressed.size()) {
   2789        fprintf(stderr, "Already gave all input data\n");
   2790        FAIL();
   2791        break;
   2792      }
   2793      size_t amount = step_size;
   2794      if (total_in + amount > compressed.size()) {
   2795        amount = compressed.size() - total_in;
   2796      }
   2797      avail_in += amount;
   2798      total_in += amount;
   2799    } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
   2800      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   2801                                     dec, &format, frames2[frames_seen].data(),
   2802                                     frames2[frames_seen].size()));
   2803    } else if (status == JXL_DEC_BASIC_INFO) {
   2804      EXPECT_EQ(false, seen_basic_info);
   2805      seen_basic_info = true;
   2806      JxlBasicInfo info;
   2807      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2808      EXPECT_EQ(xsize, info.xsize);
   2809      EXPECT_EQ(ysize, info.ysize);
   2810    } else if (status == JXL_DEC_FRAME) {
   2811      EXPECT_EQ(true, seen_basic_info);
   2812      frame_headers_seen++;
   2813    } else if (status == JXL_DEC_FULL_IMAGE) {
   2814      frames_seen++;
   2815      EXPECT_EQ(frame_headers_seen, frames_seen);
   2816    } else {
   2817      fprintf(stderr, "Unexpected status: %d\n", static_cast<int>(status));
   2818      FAIL();
   2819    }
   2820  }
   2821 
   2822  EXPECT_EQ(true, seen_basic_info);
   2823  EXPECT_EQ(num_frames, frames_seen);
   2824  EXPECT_EQ(num_frames, frame_headers_seen);
   2825  for (size_t i = 0; i < num_frames; ++i) {
   2826    EXPECT_EQ(frames[i], frames2[i]);
   2827  }
   2828 
   2829  JxlThreadParallelRunnerDestroy(runner);
   2830  JxlDecoderDestroy(dec);
   2831 }
   2832 
   2833 TEST(DecodeTest, ExtraChannelTest) {
   2834  size_t xsize = 55;
   2835  size_t ysize = 257;
   2836  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   2837  JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2838 
   2839  jxl::TestCodestreamParams params;
   2840  // Lossless to verify pixels exactly after roundtrip.
   2841  params.cparams.SetLossless();
   2842  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   2843  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   2844      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   2845 
   2846  size_t align = 17;
   2847  JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align};
   2848 
   2849  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2850 
   2851  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(
   2852                                 dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
   2853 
   2854  EXPECT_EQ(JXL_DEC_SUCCESS,
   2855            JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   2856  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2857  JxlBasicInfo info;
   2858  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2859  EXPECT_EQ(1u, info.num_extra_channels);
   2860  EXPECT_EQ(JXL_FALSE, info.alpha_premultiplied);
   2861 
   2862  JxlExtraChannelInfo extra_info;
   2863  EXPECT_EQ(JXL_DEC_SUCCESS,
   2864            JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info));
   2865  EXPECT_EQ(0, extra_info.type);
   2866 
   2867  EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   2868  size_t buffer_size;
   2869  EXPECT_EQ(JXL_DEC_SUCCESS,
   2870            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   2871  size_t extra_size;
   2872  EXPECT_EQ(JXL_DEC_SUCCESS,
   2873            JxlDecoderExtraChannelBufferSize(dec, &format, &extra_size, 0));
   2874 
   2875  std::vector<uint8_t> image(buffer_size);
   2876  std::vector<uint8_t> extra(extra_size);
   2877 
   2878  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   2879                                 dec, &format, image.data(), image.size()));
   2880  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetExtraChannelBuffer(
   2881                                 dec, &format, extra.data(), extra.size(), 0));
   2882 
   2883  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   2884 
   2885  // After the full image was output, JxlDecoderProcessInput should return
   2886  // success to indicate all is done.
   2887  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   2888  JxlDecoderDestroy(dec);
   2889 
   2890  EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), image.data(), xsize,
   2891                                         ysize, format_orig, format));
   2892 
   2893  // Compare the extracted extra channel with the original alpha channel
   2894 
   2895  std::vector<uint8_t> alpha(pixels.size() / 4);
   2896  for (size_t i = 0; i < pixels.size(); i += 8) {
   2897    size_t index_alpha = i / 4;
   2898    alpha[index_alpha + 0] = pixels[i + 6];
   2899    alpha[index_alpha + 1] = pixels[i + 7];
   2900  }
   2901  JxlPixelFormat format_alpha = format;
   2902  format_alpha.num_channels = 1;
   2903  JxlPixelFormat format_orig_alpha = format_orig;
   2904  format_orig_alpha.num_channels = 1;
   2905 
   2906  EXPECT_EQ(0u,
   2907            jxl::test::ComparePixels(alpha.data(), extra.data(), xsize, ysize,
   2908                                     format_orig_alpha, format_alpha));
   2909 }
   2910 
   2911 TEST(DecodeTest, SkipCurrentFrameTest) {
   2912  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   2913  size_t xsize = 90;
   2914  size_t ysize = 120;
   2915  constexpr size_t num_frames = 7;
   2916  std::vector<uint8_t> frames[num_frames];
   2917  for (size_t i = 0; i < num_frames; i++) {
   2918    frames[i] = jxl::test::GetSomeTestImage(xsize, ysize, 3, i);
   2919  }
   2920  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   2921 
   2922  jxl::CodecInOut io{memory_manager};
   2923  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2924  io.metadata.m.SetUintSamples(16);
   2925  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   2926  io.metadata.m.have_animation = true;
   2927  io.frames.clear();
   2928  io.frames.reserve(num_frames);
   2929  ASSERT_TRUE(io.SetSize(xsize, ysize));
   2930 
   2931  std::vector<uint32_t> frame_durations(num_frames);
   2932  for (size_t i = 0; i < num_frames; ++i) {
   2933    frame_durations[i] = 5 + i;
   2934  }
   2935 
   2936  for (size_t i = 0; i < num_frames; ++i) {
   2937    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   2938    if (i & 1) {
   2939      // Mark some frames as referenceable, others not.
   2940      bundle.use_for_next_frame = true;
   2941    }
   2942 
   2943    EXPECT_TRUE(ConvertFromExternal(
   2944        jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize,
   2945        jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   2946        /*bits_per_sample=*/16, format,
   2947        /*pool=*/nullptr, &bundle));
   2948    bundle.duration = frame_durations[i];
   2949    io.frames.push_back(std::move(bundle));
   2950  }
   2951 
   2952  jxl::CompressParams cparams;
   2953  cparams.speed_tier = jxl::SpeedTier::kThunder;
   2954  std::vector<uint8_t> compressed;
   2955  jxl::PassDefinition passes[] = {{2, 0, 4}, {4, 0, 4}, {8, 2, 2}, {8, 0, 1}};
   2956  jxl::ProgressiveMode progressive_mode{passes};
   2957  cparams.custom_progressive_mode = &progressive_mode;
   2958  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   2959 
   2960  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   2961  const uint8_t* next_in = compressed.data();
   2962  size_t avail_in = compressed.size();
   2963 
   2964  EXPECT_EQ(JXL_DEC_SUCCESS,
   2965            JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME |
   2966                                               JXL_DEC_FRAME_PROGRESSION |
   2967                                               JXL_DEC_FULL_IMAGE));
   2968  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kLastPasses));
   2969  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   2970 
   2971  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   2972  size_t buffer_size;
   2973  EXPECT_EQ(JXL_DEC_SUCCESS,
   2974            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   2975  JxlBasicInfo info;
   2976  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   2977 
   2978  for (size_t i = 0; i < num_frames; ++i) {
   2979    printf("Decoding frame %d\n", static_cast<int>(i));
   2980    EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec));
   2981    std::vector<uint8_t> pixels(buffer_size);
   2982    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   2983    EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec));
   2984    JxlFrameHeader frame_header;
   2985    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   2986    EXPECT_EQ(frame_durations[i], frame_header.duration);
   2987    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   2988    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   2989    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   2990                                   dec, &format, pixels.data(), pixels.size()));
   2991    if (i == 2) {
   2992      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec));
   2993      continue;
   2994    }
   2995    EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec));
   2996    EXPECT_EQ(8, JxlDecoderGetIntendedDownsamplingRatio(dec));
   2997    if (i == 3) {
   2998      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec));
   2999      continue;
   3000    }
   3001    EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec));
   3002    EXPECT_EQ(4, JxlDecoderGetIntendedDownsamplingRatio(dec));
   3003    if (i == 4) {
   3004      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec));
   3005      continue;
   3006    }
   3007    EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec));
   3008    EXPECT_EQ(2, JxlDecoderGetIntendedDownsamplingRatio(dec));
   3009    if (i == 5) {
   3010      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSkipCurrentFrame(dec));
   3011      continue;
   3012    }
   3013    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3014    EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec));
   3015  }
   3016 
   3017  // After all frames were decoded, JxlDecoderProcessInput should return
   3018  // success to indicate all is done.
   3019  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3020 
   3021  JxlDecoderDestroy(dec);
   3022 }
   3023 
   3024 TEST(DecodeTest, SkipFrameTest) {
   3025  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   3026  size_t xsize = 90;
   3027  size_t ysize = 120;
   3028  constexpr size_t num_frames = 16;
   3029  std::vector<uint8_t> frames[num_frames];
   3030  for (size_t i = 0; i < num_frames; i++) {
   3031    frames[i] = jxl::test::GetSomeTestImage(xsize, ysize, 3, i);
   3032  }
   3033  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   3034 
   3035  jxl::CodecInOut io{memory_manager};
   3036  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3037  io.metadata.m.SetUintSamples(16);
   3038  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   3039  io.metadata.m.have_animation = true;
   3040  io.frames.clear();
   3041  io.frames.reserve(num_frames);
   3042  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3043 
   3044  std::vector<uint32_t> frame_durations(num_frames);
   3045  for (size_t i = 0; i < num_frames; ++i) {
   3046    frame_durations[i] = 5 + i;
   3047  }
   3048 
   3049  for (size_t i = 0; i < num_frames; ++i) {
   3050    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   3051    if (i & 1) {
   3052      // Mark some frames as referenceable, others not.
   3053      bundle.use_for_next_frame = true;
   3054    }
   3055 
   3056    EXPECT_TRUE(ConvertFromExternal(
   3057        jxl::Bytes(frames[i].data(), frames[i].size()), xsize, ysize,
   3058        jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3059        /*bits_per_sample=*/16, format,
   3060        /*pool=*/nullptr, &bundle));
   3061    bundle.duration = frame_durations[i];
   3062    io.frames.push_back(std::move(bundle));
   3063  }
   3064 
   3065  jxl::CompressParams cparams;
   3066  cparams.SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   3067  cparams.speed_tier = jxl::SpeedTier::kThunder;
   3068  std::vector<uint8_t> compressed;
   3069  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   3070 
   3071  // Decode and test the animation frames
   3072 
   3073  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3074  const uint8_t* next_in = compressed.data();
   3075  size_t avail_in = compressed.size();
   3076 
   3077  void* runner = JxlThreadParallelRunnerCreate(
   3078      nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3079  EXPECT_EQ(JXL_DEC_SUCCESS,
   3080            JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
   3081 
   3082  EXPECT_EQ(JXL_DEC_SUCCESS,
   3083            JxlDecoderSubscribeEvents(
   3084                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3085  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3086 
   3087  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   3088  size_t buffer_size;
   3089  EXPECT_EQ(JXL_DEC_SUCCESS,
   3090            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3091  JxlBasicInfo info;
   3092  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   3093 
   3094  for (size_t i = 0; i < num_frames; ++i) {
   3095    if (i == 3) {
   3096      JxlDecoderSkipFrames(dec, 5);
   3097      i += 5;
   3098    }
   3099    std::vector<uint8_t> pixels(buffer_size);
   3100 
   3101    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3102 
   3103    JxlFrameHeader frame_header;
   3104    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3105    EXPECT_EQ(frame_durations[i], frame_header.duration);
   3106 
   3107    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3108 
   3109    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3110 
   3111    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3112                                   dec, &format, pixels.data(), pixels.size()));
   3113 
   3114    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3115    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3116                                           xsize, ysize, format, format));
   3117  }
   3118 
   3119  // After all frames were decoded, JxlDecoderProcessInput should return
   3120  // success to indicate all is done.
   3121  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3122 
   3123  // Test rewinding the decoder and skipping different frames
   3124 
   3125  JxlDecoderRewind(dec);
   3126  EXPECT_EQ(JXL_DEC_SUCCESS,
   3127            JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3128  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3129 
   3130  for (size_t i = 0; i < num_frames; ++i) {
   3131    int test_skipping = (i == 9) ? 3 : 0;
   3132    std::vector<uint8_t> pixels(buffer_size);
   3133 
   3134    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3135 
   3136    // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this
   3137    // should only skip the next frame, not the currently processed one.
   3138    if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping);
   3139 
   3140    JxlFrameHeader frame_header;
   3141    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3142    EXPECT_EQ(frame_durations[i], frame_header.duration);
   3143 
   3144    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3145 
   3146    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3147 
   3148    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3149                                   dec, &format, pixels.data(), pixels.size()));
   3150 
   3151    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3152    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3153                                           xsize, ysize, format, format));
   3154 
   3155    if (test_skipping) i += test_skipping;
   3156  }
   3157 
   3158  JxlThreadParallelRunnerDestroy(runner);
   3159  JxlDecoderDestroy(dec);
   3160 }
   3161 
   3162 TEST(DecodeTest, SkipFrameWithBlendingTest) {
   3163  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   3164  size_t xsize = 90;
   3165  size_t ysize = 120;
   3166  constexpr size_t num_frames = 16;
   3167  std::vector<uint8_t> frames[num_frames];
   3168  JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   3169 
   3170  jxl::CodecInOut io{memory_manager};
   3171  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3172  io.metadata.m.SetUintSamples(16);
   3173  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   3174  io.metadata.m.have_animation = true;
   3175  io.frames.clear();
   3176  io.frames.reserve(num_frames);
   3177  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3178 
   3179  std::vector<uint32_t> frame_durations(num_frames);
   3180 
   3181  for (size_t i = 0; i < num_frames; ++i) {
   3182    if (i < 5) {
   3183      std::vector<uint8_t> frame_internal =
   3184          jxl::test::GetSomeTestImage(xsize, ysize, 3, i * 2 + 1);
   3185      // An internal frame with 0 duration, and use_for_next_frame, this is a
   3186      // frame that is not rendered and not output by the API, but on which the
   3187      // rendered frames depend
   3188      jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m);
   3189      EXPECT_TRUE(ConvertFromExternal(
   3190          jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize,
   3191          ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3192          /*bits_per_sample=*/16, format,
   3193          /*pool=*/nullptr, &bundle_internal));
   3194      bundle_internal.duration = 0;
   3195      bundle_internal.use_for_next_frame = true;
   3196      io.frames.push_back(std::move(bundle_internal));
   3197    }
   3198 
   3199    std::vector<uint8_t> frame =
   3200        jxl::test::GetSomeTestImage(xsize, ysize, 3, i * 2);
   3201    // Actual rendered frame
   3202    frame_durations[i] = 5 + i;
   3203    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   3204    EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()),
   3205                                    xsize, ysize,
   3206                                    jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3207                                    /*bits_per_sample=*/16, format,
   3208                                    /*pool=*/nullptr, &bundle));
   3209    bundle.duration = frame_durations[i];
   3210    // Create some variation in which frames depend on which.
   3211    if (i != 3 && i != 9 && i != 10) {
   3212      bundle.use_for_next_frame = true;
   3213    }
   3214    if (i != 12) {
   3215      bundle.blend = true;
   3216      // Choose a blend mode that depends on the pixels of the saved frame and
   3217      // doesn't use alpha
   3218      bundle.blendmode = jxl::BlendMode::kMul;
   3219    }
   3220    io.frames.push_back(std::move(bundle));
   3221  }
   3222 
   3223  jxl::CompressParams cparams;
   3224  cparams.SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   3225  cparams.speed_tier = jxl::SpeedTier::kThunder;
   3226  std::vector<uint8_t> compressed;
   3227  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   3228 
   3229  // Independently decode all frames without any skipping, to create the
   3230  // expected blended frames, for the actual tests below to compare with.
   3231  {
   3232    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3233    const uint8_t* next_in = compressed.data();
   3234    size_t avail_in = compressed.size();
   3235 
   3236    void* runner = JxlThreadParallelRunnerCreate(
   3237        nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3238    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
   3239                                   dec, JxlThreadParallelRunner, runner));
   3240    EXPECT_EQ(JXL_DEC_SUCCESS,
   3241              JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
   3242    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3243    for (auto& frame : frames) {
   3244      EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3245      frame.resize(xsize * ysize * 6);
   3246      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3247                                     dec, &format, frame.data(), frame.size()));
   3248      EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3249    }
   3250 
   3251    // After all frames were decoded, JxlDecoderProcessInput should return
   3252    // success to indicate all is done.
   3253    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3254    JxlThreadParallelRunnerDestroy(runner);
   3255    JxlDecoderDestroy(dec);
   3256  }
   3257 
   3258  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3259  const uint8_t* next_in = compressed.data();
   3260  size_t avail_in = compressed.size();
   3261 
   3262  void* runner = JxlThreadParallelRunnerCreate(
   3263      nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3264  EXPECT_EQ(JXL_DEC_SUCCESS,
   3265            JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner));
   3266 
   3267  EXPECT_EQ(JXL_DEC_SUCCESS,
   3268            JxlDecoderSubscribeEvents(
   3269                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3270  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3271  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   3272  size_t buffer_size;
   3273  EXPECT_EQ(JXL_DEC_SUCCESS,
   3274            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3275  JxlBasicInfo info;
   3276  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   3277 
   3278  for (size_t i = 0; i < num_frames; ++i) {
   3279    std::vector<uint8_t> pixels(buffer_size);
   3280 
   3281    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3282 
   3283    JxlFrameHeader frame_header;
   3284    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3285    EXPECT_EQ(frame_durations[i], frame_header.duration);
   3286 
   3287    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3288 
   3289    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3290 
   3291    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3292                                   dec, &format, pixels.data(), pixels.size()));
   3293 
   3294    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3295    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3296                                           xsize, ysize, format, format));
   3297 
   3298    // Test rewinding mid-way, not decoding all frames.
   3299    if (i == 8) {
   3300      break;
   3301    }
   3302  }
   3303 
   3304  JxlDecoderRewind(dec);
   3305  EXPECT_EQ(JXL_DEC_SUCCESS,
   3306            JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3307  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3308 
   3309  for (size_t i = 0; i < num_frames; ++i) {
   3310    if (i == 3) {
   3311      JxlDecoderSkipFrames(dec, 5);
   3312      i += 5;
   3313    }
   3314    std::vector<uint8_t> pixels(buffer_size);
   3315 
   3316    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3317 
   3318    JxlFrameHeader frame_header;
   3319    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3320    EXPECT_EQ(frame_durations[i], frame_header.duration);
   3321 
   3322    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3323 
   3324    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3325 
   3326    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3327                                   dec, &format, pixels.data(), pixels.size()));
   3328 
   3329    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3330    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3331                                           xsize, ysize, format, format));
   3332  }
   3333 
   3334  // After all frames were decoded, JxlDecoderProcessInput should return
   3335  // success to indicate all is done.
   3336  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3337 
   3338  // Test rewinding the decoder and skipping different frames
   3339 
   3340  JxlDecoderRewind(dec);
   3341  EXPECT_EQ(JXL_DEC_SUCCESS,
   3342            JxlDecoderSubscribeEvents(dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3343  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3344 
   3345  for (size_t i = 0; i < num_frames; ++i) {
   3346    int test_skipping = (i == 9) ? 3 : 0;
   3347    std::vector<uint8_t> pixels(buffer_size);
   3348 
   3349    EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3350 
   3351    // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this
   3352    // should only skip the next frame, not the currently processed one.
   3353    if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping);
   3354 
   3355    JxlFrameHeader frame_header;
   3356    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3357    EXPECT_EQ(frame_durations[i], frame_header.duration);
   3358 
   3359    EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3360 
   3361    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3362 
   3363    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   3364                                   dec, &format, pixels.data(), pixels.size()));
   3365 
   3366    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3367    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3368                                           xsize, ysize, format, format));
   3369 
   3370    if (test_skipping) i += test_skipping;
   3371  }
   3372 
   3373  JxlThreadParallelRunnerDestroy(runner);
   3374  JxlDecoderDestroy(dec);
   3375 }
   3376 
   3377 TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
   3378  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   3379  size_t xsize = 90;
   3380  size_t ysize = 120;
   3381  constexpr size_t num_frames = 16;
   3382  std::vector<uint8_t> frames[num_frames + 5];
   3383  JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   3384 
   3385  jxl::CodecInOut io{memory_manager};
   3386  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3387  io.metadata.m.SetUintSamples(16);
   3388  io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   3389  io.metadata.m.have_animation = true;
   3390  io.frames.clear();
   3391  io.frames.reserve(num_frames + 5);
   3392  ASSERT_TRUE(io.SetSize(xsize, ysize));
   3393 
   3394  std::vector<uint32_t> frame_durations_c;
   3395  std::vector<uint32_t> frame_durations_nc;
   3396  std::vector<uint32_t> frame_xsize;
   3397  std::vector<uint32_t> frame_ysize;
   3398  std::vector<uint32_t> frame_x0;
   3399  std::vector<uint32_t> frame_y0;
   3400 
   3401  for (size_t i = 0; i < num_frames; ++i) {
   3402    size_t cropxsize = 1 + xsize * 2 / (i + 1);
   3403    size_t cropysize = 1 + ysize * 3 / (i + 2);
   3404    int cropx0 = i * 3 - 8;
   3405    int cropy0 = i * 4 - 7;
   3406    if (i < 5) {
   3407      std::vector<uint8_t> frame_internal =
   3408          jxl::test::GetSomeTestImage(xsize / 2, ysize / 2, 4, i * 2 + 1);
   3409      // An internal frame with 0 duration, and use_for_next_frame, this is a
   3410      // frame that is not rendered and not output by default by the API, but on
   3411      // which the rendered frames depend
   3412      jxl::ImageBundle bundle_internal(memory_manager, &io.metadata.m);
   3413      EXPECT_TRUE(ConvertFromExternal(
   3414          jxl::Bytes(frame_internal.data(), frame_internal.size()), xsize / 2,
   3415          ysize / 2, jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3416          /*bits_per_sample=*/16, format,
   3417          /*pool=*/nullptr, &bundle_internal));
   3418      bundle_internal.duration = 0;
   3419      bundle_internal.use_for_next_frame = true;
   3420      bundle_internal.origin = {13, 17};
   3421      io.frames.push_back(std::move(bundle_internal));
   3422      frame_durations_nc.push_back(0);
   3423      frame_xsize.push_back(xsize / 2);
   3424      frame_ysize.push_back(ysize / 2);
   3425      frame_x0.push_back(13);
   3426      frame_y0.push_back(17);
   3427    }
   3428 
   3429    std::vector<uint8_t> frame =
   3430        jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2);
   3431    // Actual rendered frame
   3432    jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   3433    EXPECT_TRUE(ConvertFromExternal(jxl::Bytes(frame.data(), frame.size()),
   3434                                    cropxsize, cropysize,
   3435                                    jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3436                                    /*bits_per_sample=*/16, format,
   3437                                    /*pool=*/nullptr, &bundle));
   3438    bundle.duration = 5 + i;
   3439    frame_durations_nc.push_back(5 + i);
   3440    frame_durations_c.push_back(5 + i);
   3441    frame_xsize.push_back(cropxsize);
   3442    frame_ysize.push_back(cropysize);
   3443    frame_x0.push_back(cropx0);
   3444    frame_y0.push_back(cropy0);
   3445    bundle.origin = {cropx0, cropy0};
   3446    // Create some variation in which frames depend on which.
   3447    if (i != 3 && i != 9 && i != 10) {
   3448      bundle.use_for_next_frame = true;
   3449    }
   3450    if (i != 12) {
   3451      bundle.blend = true;
   3452      bundle.blendmode = jxl::BlendMode::kBlend;
   3453    }
   3454    io.frames.push_back(std::move(bundle));
   3455  }
   3456 
   3457  jxl::CompressParams cparams;
   3458  cparams.SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   3459  cparams.speed_tier = jxl::SpeedTier::kThunder;
   3460  std::vector<uint8_t> compressed;
   3461  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   3462  // try both with and without coalescing
   3463  for (auto coalescing : {JXL_TRUE, JXL_FALSE}) {
   3464    // Independently decode all frames without any skipping, to create the
   3465    // expected blended frames, for the actual tests below to compare with.
   3466    {
   3467      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3468      const uint8_t* next_in = compressed.data();
   3469      size_t avail_in = compressed.size();
   3470      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
   3471      void* runner = JxlThreadParallelRunnerCreate(
   3472          nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3473      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
   3474                                     dec, JxlThreadParallelRunner, runner));
   3475      EXPECT_EQ(JXL_DEC_SUCCESS,
   3476                JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
   3477      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3478      for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) {
   3479        EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3480        size_t buffer_size;
   3481        EXPECT_EQ(JXL_DEC_SUCCESS,
   3482                  JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3483        if (coalescing) {
   3484          EXPECT_EQ(xsize * ysize * 8, buffer_size);
   3485        } else {
   3486          EXPECT_EQ(frame_xsize[i] * frame_ysize[i] * 8, buffer_size);
   3487        }
   3488        frames[i].resize(buffer_size);
   3489        EXPECT_EQ(JXL_DEC_SUCCESS,
   3490                  JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(),
   3491                                              frames[i].size()));
   3492        EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3493      }
   3494 
   3495      // After all frames were decoded, JxlDecoderProcessInput should return
   3496      // success to indicate all is done.
   3497      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3498      JxlThreadParallelRunnerDestroy(runner);
   3499      JxlDecoderDestroy(dec);
   3500    }
   3501 
   3502    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3503    const uint8_t* next_in = compressed.data();
   3504    size_t avail_in = compressed.size();
   3505 
   3506    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
   3507    void* runner = JxlThreadParallelRunnerCreate(
   3508        nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3509    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
   3510                                   dec, JxlThreadParallelRunner, runner));
   3511 
   3512    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(
   3513                                   dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME |
   3514                                            JXL_DEC_FULL_IMAGE));
   3515    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3516    EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   3517    JxlBasicInfo info;
   3518    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   3519 
   3520    for (size_t i = 0; i < num_frames; ++i) {
   3521      EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3522 
   3523      size_t buffer_size;
   3524      EXPECT_EQ(JXL_DEC_SUCCESS,
   3525                JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3526      std::vector<uint8_t> pixels(buffer_size);
   3527 
   3528      JxlFrameHeader frame_header;
   3529      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3530      EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]),
   3531                frame_header.duration);
   3532 
   3533      EXPECT_EQ(i + 1 == num_frames, frame_header.is_last);
   3534 
   3535      EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3536 
   3537      EXPECT_EQ(JXL_DEC_SUCCESS,
   3538                JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(),
   3539                                            pixels.size()));
   3540 
   3541      EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3542      if (coalescing) {
   3543        EXPECT_EQ(frame_header.layer_info.xsize, xsize);
   3544      } else {
   3545        EXPECT_EQ(frame_header.layer_info.xsize, frame_xsize[i]);
   3546      }
   3547      if (coalescing) {
   3548        EXPECT_EQ(frame_header.layer_info.ysize, ysize);
   3549      } else {
   3550        EXPECT_EQ(frame_header.layer_info.ysize, frame_ysize[i]);
   3551      }
   3552      EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3553                                             frame_header.layer_info.xsize,
   3554                                             frame_header.layer_info.ysize,
   3555                                             format, format));
   3556 
   3557      // Test rewinding mid-way, not decoding all frames.
   3558      if (i == 8) {
   3559        break;
   3560      }
   3561    }
   3562 
   3563    JxlDecoderRewind(dec);
   3564    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(
   3565                                   dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3566    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3567 
   3568    for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) {
   3569      if (i == 3) {
   3570        JxlDecoderSkipFrames(dec, 5);
   3571        i += 5;
   3572      }
   3573 
   3574      EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3575      size_t buffer_size;
   3576      EXPECT_EQ(JXL_DEC_SUCCESS,
   3577                JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3578      std::vector<uint8_t> pixels(buffer_size);
   3579 
   3580      JxlFrameHeader frame_header;
   3581      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3582      EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]),
   3583                frame_header.duration);
   3584 
   3585      EXPECT_EQ(i + 1 == num_frames + (coalescing ? 0 : 5),
   3586                frame_header.is_last);
   3587 
   3588      EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3589 
   3590      EXPECT_EQ(JXL_DEC_SUCCESS,
   3591                JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(),
   3592                                            pixels.size()));
   3593 
   3594      EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3595      if (coalescing) {
   3596        EXPECT_EQ(frame_header.layer_info.xsize, xsize);
   3597        EXPECT_EQ(frame_header.layer_info.ysize, ysize);
   3598        EXPECT_EQ(frame_header.layer_info.crop_x0, 0);
   3599        EXPECT_EQ(frame_header.layer_info.crop_y0, 0);
   3600      } else {
   3601        EXPECT_EQ(frame_header.layer_info.xsize, frame_xsize[i]);
   3602        EXPECT_EQ(frame_header.layer_info.ysize, frame_ysize[i]);
   3603        EXPECT_EQ(frame_header.layer_info.crop_x0, frame_x0[i]);
   3604        EXPECT_EQ(frame_header.layer_info.crop_y0, frame_y0[i]);
   3605        EXPECT_EQ(frame_header.layer_info.blend_info.blendmode,
   3606                  i != 12 + 5 && frame_header.duration != 0
   3607                      ? 2
   3608                      : 0);  // kBlend or the default kReplace
   3609      }
   3610      EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3611                                             frame_header.layer_info.xsize,
   3612                                             frame_header.layer_info.ysize,
   3613                                             format, format));
   3614    }
   3615 
   3616    // After all frames were decoded, JxlDecoderProcessInput should return
   3617    // success to indicate all is done.
   3618    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3619 
   3620    // Test rewinding the decoder and skipping different frames
   3621 
   3622    JxlDecoderRewind(dec);
   3623    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(
   3624                                   dec, JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3625    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3626 
   3627    for (size_t i = 0; i < num_frames + (coalescing ? 0 : 5); ++i) {
   3628      int test_skipping = (i == 9) ? 3 : 0;
   3629 
   3630      EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   3631      size_t buffer_size;
   3632      EXPECT_EQ(JXL_DEC_SUCCESS,
   3633                JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3634      std::vector<uint8_t> pixels(buffer_size);
   3635 
   3636      // Since this is after JXL_DEC_FRAME but before JXL_DEC_FULL_IMAGE, this
   3637      // should only skip the next frame, not the currently processed one.
   3638      if (test_skipping) JxlDecoderSkipFrames(dec, test_skipping);
   3639 
   3640      JxlFrameHeader frame_header;
   3641      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetFrameHeader(dec, &frame_header));
   3642      EXPECT_EQ((coalescing ? frame_durations_c[i] : frame_durations_nc[i]),
   3643                frame_header.duration);
   3644 
   3645      EXPECT_EQ(i + 1 == num_frames + (coalescing ? 0 : 5),
   3646                frame_header.is_last);
   3647 
   3648      EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3649 
   3650      EXPECT_EQ(JXL_DEC_SUCCESS,
   3651                JxlDecoderSetImageOutBuffer(dec, &format, pixels.data(),
   3652                                            pixels.size()));
   3653 
   3654      EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3655      EXPECT_EQ(0u, jxl::test::ComparePixels(frames[i].data(), pixels.data(),
   3656                                             frame_header.layer_info.xsize,
   3657                                             frame_header.layer_info.ysize,
   3658                                             format, format));
   3659 
   3660      if (test_skipping) i += test_skipping;
   3661    }
   3662 
   3663    JxlThreadParallelRunnerDestroy(runner);
   3664    JxlDecoderDestroy(dec);
   3665  }
   3666 }
   3667 
   3668 TEST(DecodeTest, OrientedCroppedFrameTest) {
   3669  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   3670  const auto test = [&](bool keep_orientation, uint32_t orientation,
   3671                        uint32_t resampling) {
   3672    size_t xsize = 90;
   3673    size_t ysize = 120;
   3674    JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   3675    size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize);
   3676    size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize);
   3677    jxl::CodecInOut io{memory_manager};
   3678    EXPECT_TRUE(io.SetSize(xsize, ysize));
   3679    io.metadata.m.SetUintSamples(16);
   3680    io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
   3681    io.metadata.m.orientation = orientation;
   3682    io.frames.clear();
   3683    EXPECT_TRUE(io.SetSize(xsize, ysize));
   3684 
   3685    for (size_t i = 0; i < 3; ++i) {
   3686      size_t cropxsize = 1 + xsize * 2 / (i + 1);
   3687      size_t cropysize = 1 + ysize * 3 / (i + 2);
   3688      int cropx0 = i * 3 - 8;
   3689      int cropy0 = i * 4 - 7;
   3690 
   3691      std::vector<uint8_t> frame =
   3692          jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2);
   3693      jxl::ImageBundle bundle(memory_manager, &io.metadata.m);
   3694      EXPECT_TRUE(ConvertFromExternal(
   3695          jxl::Bytes(frame.data(), frame.size()), cropxsize, cropysize,
   3696          jxl::ColorEncoding::SRGB(/*is_gray=*/false),
   3697          /*bits_per_sample=*/16, format,
   3698          /*pool=*/nullptr, &bundle));
   3699      bundle.origin = {cropx0, cropy0};
   3700      bundle.use_for_next_frame = true;
   3701      io.frames.push_back(std::move(bundle));
   3702    }
   3703 
   3704    jxl::CompressParams cparams;
   3705    cparams
   3706        .SetLossless();  // Lossless to verify pixels exactly after roundtrip.
   3707    cparams.speed_tier = jxl::SpeedTier::kThunder;
   3708    cparams.resampling = resampling;
   3709    std::vector<uint8_t> compressed;
   3710    EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   3711 
   3712    // 0 is merged frame as decoded with coalescing enabled (default)
   3713    // 1-3 are non-coalesced frames as decoded with coalescing disabled
   3714    // 4 is the manually merged frame
   3715    std::vector<uint8_t> frames[5];
   3716    frames[4].resize(xsize * ysize * 8, 0);
   3717 
   3718    // try both with and without coalescing
   3719    for (auto coalescing : {JXL_TRUE, JXL_FALSE}) {
   3720      // Independently decode all frames without any skipping, to create the
   3721      // expected blended frames, for the actual tests below to compare with.
   3722      {
   3723        JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3724        const uint8_t* next_in = compressed.data();
   3725        size_t avail_in = compressed.size();
   3726        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
   3727        EXPECT_EQ(JXL_DEC_SUCCESS,
   3728                  JxlDecoderSetKeepOrientation(dec, keep_orientation));
   3729        void* runner = JxlThreadParallelRunnerCreate(
   3730            nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
   3731        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
   3732                                       dec, JxlThreadParallelRunner, runner));
   3733        EXPECT_EQ(JXL_DEC_SUCCESS,
   3734                  JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
   3735        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3736        for (size_t i = (coalescing ? 0 : 1); i < (coalescing ? 1 : 4); ++i) {
   3737          EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   3738          JxlFrameHeader frame_header;
   3739          EXPECT_EQ(JXL_DEC_SUCCESS,
   3740                    JxlDecoderGetFrameHeader(dec, &frame_header));
   3741          size_t buffer_size;
   3742          EXPECT_EQ(JXL_DEC_SUCCESS,
   3743                    JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3744          if (coalescing) {
   3745            EXPECT_EQ(xsize * ysize * 8, buffer_size);
   3746          } else {
   3747            EXPECT_EQ(frame_header.layer_info.xsize *
   3748                          frame_header.layer_info.ysize * 8,
   3749                      buffer_size);
   3750          }
   3751          frames[i].resize(buffer_size);
   3752          EXPECT_EQ(JXL_DEC_SUCCESS,
   3753                    JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(),
   3754                                                frames[i].size()));
   3755          EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   3756          EXPECT_EQ(frame_header.layer_info.blend_info.blendmode,
   3757                    JXL_BLEND_REPLACE);
   3758          if (coalescing) {
   3759            EXPECT_EQ(frame_header.layer_info.xsize, oxsize);
   3760            EXPECT_EQ(frame_header.layer_info.ysize, oysize);
   3761            EXPECT_EQ(frame_header.layer_info.crop_x0, 0);
   3762            EXPECT_EQ(frame_header.layer_info.crop_y0, 0);
   3763          } else {
   3764            // manually merge this layer
   3765            int x0 = frame_header.layer_info.crop_x0;
   3766            int y0 = frame_header.layer_info.crop_y0;
   3767            int w = frame_header.layer_info.xsize;
   3768            int h = frame_header.layer_info.ysize;
   3769            for (int y = 0; y < static_cast<int>(oysize); y++) {
   3770              if (y < y0 || y >= y0 + h) continue;
   3771              // pointers do whole 16-bit RGBA pixels at a time
   3772              uint64_t* row_merged = reinterpret_cast<uint64_t*>(
   3773                  frames[4].data() + y * oxsize * 8);
   3774              uint64_t* row_layer = reinterpret_cast<uint64_t*>(
   3775                  frames[i].data() + (y - y0) * w * 8);
   3776              for (int x = 0; x < static_cast<int>(oxsize); x++) {
   3777                if (x < x0 || x >= x0 + w) continue;
   3778                row_merged[x] = row_layer[x - x0];
   3779              }
   3780            }
   3781          }
   3782        }
   3783 
   3784        // After all frames were decoded, JxlDecoderProcessInput should return
   3785        // success to indicate all is done.
   3786        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   3787        JxlThreadParallelRunnerDestroy(runner);
   3788        JxlDecoderDestroy(dec);
   3789      }
   3790    }
   3791 
   3792    EXPECT_EQ(0u, jxl::test::ComparePixels(frames[0].data(), frames[4].data(),
   3793                                           oxsize, oysize, format, format));
   3794  };
   3795 
   3796  for (bool keep_orientation : {true, false}) {
   3797    for (uint32_t orientation = 1; orientation <= 8; orientation++) {
   3798      for (uint32_t resampling : {1, 2, 4, 8}) {
   3799        SCOPED_TRACE(testing::Message()
   3800                     << "keep_orientation: " << keep_orientation << ", "
   3801                     << "orientation: " << orientation << ", "
   3802                     << "resampling: " << resampling);
   3803        test(keep_orientation, orientation, resampling);
   3804      }
   3805    }
   3806  }
   3807 }
   3808 
   3809 struct FramePositions {
   3810  size_t frame_start;
   3811  size_t header_end;
   3812  size_t toc_end;
   3813  std::vector<size_t> section_end;
   3814 };
   3815 
   3816 struct StreamPositions {
   3817  size_t codestream_start;
   3818  size_t codestream_end;
   3819  size_t basic_info;
   3820  size_t jbrd_end = 0;
   3821  std::vector<size_t> box_start;
   3822  std::vector<FramePositions> frames;
   3823 };
   3824 
   3825 void AnalyzeCodestream(const std::vector<uint8_t>& data,
   3826                       StreamPositions* streampos) {
   3827  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   3828  // Unbox data to codestream and mark where it is broken up by boxes.
   3829  std::vector<uint8_t> codestream;
   3830  std::vector<std::pair<size_t, size_t>> breakpoints;
   3831  bool codestream_end = false;
   3832  ASSERT_LE(2, data.size());
   3833  if (data[0] == 0xff && data[1] == 0x0a) {
   3834    codestream = std::vector<uint8_t>(data.begin(), data.end());
   3835    streampos->codestream_start = 0;
   3836  } else {
   3837    const uint8_t* in = data.data();
   3838    size_t pos = 0;
   3839    while (pos < data.size()) {
   3840      ASSERT_LE(pos + 8, data.size());
   3841      streampos->box_start.push_back(pos);
   3842      size_t box_size = LoadBE32(in + pos);
   3843      if (box_size == 0) box_size = data.size() - pos;
   3844      ASSERT_LE(pos + box_size, data.size());
   3845      if (memcmp(in + pos + 4, "jxlc", 4) == 0) {
   3846        EXPECT_TRUE(codestream.empty());
   3847        streampos->codestream_start = pos + 8;
   3848        codestream.insert(codestream.end(), in + pos + 8, in + pos + box_size);
   3849        codestream_end = true;
   3850      } else if (memcmp(in + pos + 4, "jxlp", 4) == 0) {
   3851        codestream_end = ((LoadBE32(in + pos + 8) & 0x80000000) != 0);
   3852        if (codestream.empty()) {
   3853          streampos->codestream_start = pos + 12;
   3854        } else if (box_size > 12 || !codestream_end) {
   3855          breakpoints.emplace_back(codestream.size(), 12);
   3856        }
   3857        codestream.insert(codestream.end(), in + pos + 12, in + pos + box_size);
   3858      } else if (memcmp(in + pos + 4, "jbrd", 4) == 0) {
   3859        EXPECT_TRUE(codestream.empty());
   3860        streampos->jbrd_end = pos + box_size;
   3861      } else if (!codestream.empty() && !codestream_end) {
   3862        breakpoints.emplace_back(codestream.size(), box_size);
   3863      }
   3864      pos += box_size;
   3865    }
   3866    ASSERT_EQ(pos, data.size());
   3867  }
   3868  // Translate codestream positions to boxed stream positions.
   3869  size_t offset = streampos->codestream_start;
   3870  size_t bp = 0;
   3871  auto add_offset = [&](size_t pos) {
   3872    while (bp < breakpoints.size() && pos >= breakpoints[bp].first) {
   3873      offset += breakpoints[bp++].second;
   3874    }
   3875    return pos + offset;
   3876  };
   3877  // Analyze the unboxed codestream.
   3878  jxl::BitReader br(jxl::Bytes(codestream.data(), codestream.size()));
   3879  ASSERT_EQ(br.ReadFixedBits<16>(), 0x0AFF);
   3880  jxl::CodecMetadata metadata;
   3881  ASSERT_TRUE(ReadSizeHeader(&br, &metadata.size));
   3882  ASSERT_TRUE(ReadImageMetadata(&br, &metadata.m));
   3883  streampos->basic_info =
   3884      add_offset(br.TotalBitsConsumed() / jxl::kBitsPerByte);
   3885  metadata.transform_data.nonserialized_xyb_encoded = metadata.m.xyb_encoded;
   3886  ASSERT_TRUE(jxl::Bundle::Read(&br, &metadata.transform_data));
   3887  if (metadata.m.color_encoding.WantICC()) {
   3888    std::vector<uint8_t> icc;
   3889    ASSERT_TRUE(jxl::test::ReadICC(&br, &icc));
   3890    ASSERT_TRUE(!icc.empty());
   3891    metadata.m.color_encoding.SetICCRaw(std::move(icc));
   3892  }
   3893  ASSERT_TRUE(br.JumpToByteBoundary());
   3894  bool has_preview = metadata.m.have_preview;
   3895  while (br.TotalBitsConsumed() < br.TotalBytes() * jxl::kBitsPerByte) {
   3896    FramePositions p;
   3897    p.frame_start = add_offset(br.TotalBitsConsumed() / jxl::kBitsPerByte);
   3898    jxl::FrameHeader frame_header(&metadata);
   3899    if (has_preview) {
   3900      frame_header.nonserialized_is_preview = true;
   3901      has_preview = false;
   3902    }
   3903    ASSERT_TRUE(ReadFrameHeader(&br, &frame_header));
   3904    p.header_end =
   3905        add_offset(jxl::DivCeil(br.TotalBitsConsumed(), jxl::kBitsPerByte));
   3906    jxl::FrameDimensions frame_dim = frame_header.ToFrameDimensions();
   3907    uint64_t groups_total_size;
   3908    const size_t toc_entries =
   3909        jxl::NumTocEntries(frame_dim.num_groups, frame_dim.num_dc_groups,
   3910                           frame_header.passes.num_passes);
   3911    std::vector<uint64_t> section_offsets;
   3912    std::vector<uint32_t> section_sizes;
   3913    ASSERT_TRUE(ReadGroupOffsets(memory_manager, toc_entries, &br,
   3914                                 &section_offsets, &section_sizes,
   3915                                 &groups_total_size));
   3916    EXPECT_EQ(br.TotalBitsConsumed() % jxl::kBitsPerByte, 0);
   3917    size_t sections_start = br.TotalBitsConsumed() / jxl::kBitsPerByte;
   3918    p.toc_end = add_offset(sections_start);
   3919    for (size_t i = 0; i < toc_entries; ++i) {
   3920      size_t end = sections_start + section_offsets[i] + section_sizes[i];
   3921      p.section_end.push_back(add_offset(end));
   3922    }
   3923    br.SkipBits(groups_total_size * jxl::kBitsPerByte);
   3924    streampos->frames.push_back(p);
   3925  }
   3926  streampos->codestream_end = add_offset(codestream.size());
   3927  EXPECT_EQ(br.TotalBitsConsumed(), br.TotalBytes() * jxl::kBitsPerByte);
   3928  EXPECT_TRUE(br.Close());
   3929 }
   3930 
   3931 enum ExpectedFlushState { NO_FLUSH, SAME_FLUSH, NEW_FLUSH };
   3932 struct Breakpoint {
   3933  size_t file_pos;
   3934  ExpectedFlushState expect_flush;
   3935 };
   3936 
   3937 void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels,
   3938                       const std::vector<uint8_t>& pixels,
   3939                       const std::vector<uint8_t>& data,
   3940                       std::vector<Breakpoint> breakpoints) {
   3941  // Size large enough for multiple groups, required to have progressive stages.
   3942  ASSERT_LT(256, xsize);
   3943  ASSERT_LT(256, ysize);
   3944  std::vector<uint8_t> pixels2;
   3945  pixels2.resize(pixels.size());
   3946  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   3947  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   3948  EXPECT_EQ(JXL_DEC_SUCCESS,
   3949            JxlDecoderSubscribeEvents(
   3950                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   3951  int bp = 0;
   3952  const uint8_t* next_in = data.data();
   3953  size_t avail_in = breakpoints[bp].file_pos;
   3954  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   3955  double prev_dist = 1.0;
   3956  for (;;) {
   3957    JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   3958    printf("bp: %d  status: 0x%x\n", bp, static_cast<int>(status));
   3959    if (status == JXL_DEC_BASIC_INFO) {
   3960      JxlBasicInfo info;
   3961      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   3962      EXPECT_EQ(info.xsize, xsize);
   3963      EXPECT_EQ(info.ysize, ysize);
   3964      // Output buffer/callback not yet set
   3965      EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   3966      size_t buffer_size;
   3967      EXPECT_EQ(JXL_DEC_SUCCESS,
   3968                JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   3969      EXPECT_EQ(pixels2.size(), buffer_size);
   3970      EXPECT_EQ(JXL_DEC_SUCCESS,
   3971                JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(),
   3972                                            pixels2.size()));
   3973    } else if (status == JXL_DEC_FRAME) {
   3974      // Nothing to do.
   3975    } else if (status == JXL_DEC_SUCCESS) {
   3976      EXPECT_EQ(bp + 1, breakpoints.size());
   3977      break;
   3978    } else if (status == JXL_DEC_NEED_MORE_INPUT ||
   3979               status == JXL_DEC_FULL_IMAGE) {
   3980      if (breakpoints[bp].expect_flush == NO_FLUSH) {
   3981        EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   3982      } else {
   3983        if (status != JXL_DEC_FULL_IMAGE) {
   3984          EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   3985        }
   3986        double dist = jxl::test::DistanceRMS(pixels2.data(), pixels.data(),
   3987                                             xsize, ysize, format);
   3988        if (breakpoints[bp].expect_flush == NEW_FLUSH) {
   3989          EXPECT_LT(dist, prev_dist);
   3990          prev_dist = dist;
   3991        } else {
   3992          EXPECT_EQ(dist, prev_dist);
   3993        }
   3994      }
   3995      if (status == JXL_DEC_FULL_IMAGE) {
   3996        EXPECT_EQ(bp + 1, breakpoints.size());
   3997        continue;
   3998      }
   3999      ASSERT_LT(++bp, breakpoints.size());
   4000      next_in += avail_in - JxlDecoderReleaseInput(dec);
   4001      avail_in = breakpoints[bp].file_pos - (next_in - data.data());
   4002      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   4003    } else {
   4004      printf("Unexpected status: 0x%x\n", static_cast<int>(status));
   4005      FAIL();  // unexpected returned status
   4006    }
   4007  }
   4008  JxlDecoderDestroy(dec);
   4009 }
   4010 
   4011 TEST(DecodeTest, ProgressionTest) {
   4012  size_t xsize = 508;
   4013  size_t ysize = 470;
   4014  uint32_t num_channels = 3;
   4015  std::vector<uint8_t> pixels =
   4016      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4017  jxl::TestCodestreamParams params;
   4018  params.cparams.progressive_dc = 1;
   4019  params.preview_mode = jxl::kSmallPreview;
   4020  std::vector<uint8_t> data =
   4021      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4022                                   xsize, ysize, num_channels, params);
   4023  StreamPositions streampos;
   4024  AnalyzeCodestream(data, &streampos);
   4025  const std::vector<FramePositions>& fp = streampos.frames;
   4026  // We have preview, dc frame and regular frame.
   4027  EXPECT_EQ(3, fp.size());
   4028  EXPECT_EQ(7, fp[2].section_end.size());
   4029  EXPECT_EQ(data.size(), fp[2].section_end[6]);
   4030  std::vector<Breakpoint> breakpoints{
   4031      {fp[0].frame_start, NO_FLUSH},           // headers
   4032      {fp[1].frame_start, NO_FLUSH},           // preview
   4033      {fp[2].frame_start, NO_FLUSH},           // dc frame
   4034      {fp[2].section_end[0], NO_FLUSH},        // DC global
   4035      {fp[2].section_end[1] - 1, NO_FLUSH},    // partial DC group
   4036      {fp[2].section_end[1], NEW_FLUSH},       // DC group
   4037      {fp[2].section_end[2], SAME_FLUSH},      // AC global
   4038      {fp[2].section_end[3], NEW_FLUSH},       // AC group 0
   4039      {fp[2].section_end[4] - 1, SAME_FLUSH},  // partial AC group 1
   4040      {fp[2].section_end[4], NEW_FLUSH},       // AC group 1
   4041      {fp[2].section_end[5], NEW_FLUSH},       // AC group 2
   4042      {data.size() - 1, SAME_FLUSH},           // partial AC group 3
   4043      {data.size(), NEW_FLUSH}};               // full image
   4044  VerifyProgression(xsize, ysize, num_channels, pixels, data, breakpoints);
   4045 }
   4046 
   4047 TEST(DecodeTest, ProgressionTestLosslessAlpha) {
   4048  size_t xsize = 508;
   4049  size_t ysize = 470;
   4050  uint32_t num_channels = 4;
   4051  std::vector<uint8_t> pixels =
   4052      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4053  jxl::TestCodestreamParams params;
   4054  params.cparams.SetLossless();
   4055  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   4056  params.cparams.responsive = 1;
   4057  std::vector<uint8_t> data =
   4058      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4059                                   xsize, ysize, num_channels, params);
   4060  StreamPositions streampos;
   4061  AnalyzeCodestream(data, &streampos);
   4062  const std::vector<FramePositions>& fp = streampos.frames;
   4063  // We have preview, dc frame and regular frame.
   4064  EXPECT_EQ(1, fp.size());
   4065  EXPECT_EQ(7, fp[0].section_end.size());
   4066  EXPECT_EQ(data.size(), fp[0].section_end[6]);
   4067  std::vector<Breakpoint> breakpoints{
   4068      {fp[0].frame_start, NO_FLUSH},           // headers
   4069      {fp[0].section_end[0] - 1, NO_FLUSH},    // partial DC global
   4070      {fp[0].section_end[0], NEW_FLUSH},       // DC global
   4071      {fp[0].section_end[1], SAME_FLUSH},      // DC group
   4072      {fp[0].section_end[2], SAME_FLUSH},      // AC global
   4073      {fp[0].section_end[3], NEW_FLUSH},       // AC group 0
   4074      {fp[0].section_end[4] - 1, SAME_FLUSH},  // partial AC group 1
   4075      {fp[0].section_end[4], NEW_FLUSH},       // AC group 1
   4076      {fp[0].section_end[5], NEW_FLUSH},       // AC group 2
   4077      {data.size() - 1, SAME_FLUSH},           // partial AC group 3
   4078      {data.size(), NEW_FLUSH}};               // full image
   4079  VerifyProgression(xsize, ysize, num_channels, pixels, data, breakpoints);
   4080 }
   4081 
   4082 void VerifyFilePosition(size_t expected_pos, const std::vector<uint8_t>& data,
   4083                        JxlDecoder* dec) {
   4084  size_t remaining = JxlDecoderReleaseInput(dec);
   4085  size_t pos = data.size() - remaining;
   4086  EXPECT_EQ(expected_pos, pos);
   4087  EXPECT_EQ(JXL_DEC_SUCCESS,
   4088            JxlDecoderSetInput(dec, data.data() + pos, remaining));
   4089 }
   4090 
   4091 TEST(DecodeTest, InputHandlingTestOneShot) {
   4092  size_t xsize = 508;
   4093  size_t ysize = 470;
   4094  uint32_t num_channels = 3;
   4095  std::vector<uint8_t> pixels =
   4096      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4097  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   4098    printf("Testing with box format %d\n", i);
   4099    jxl::TestCodestreamParams params;
   4100    params.cparams.progressive_dc = 1;
   4101    params.preview_mode = jxl::kSmallPreview;
   4102    params.box_format = static_cast<CodeStreamBoxFormat>(i);
   4103    std::vector<uint8_t> data =
   4104        jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4105                                     xsize, ysize, num_channels, params);
   4106    JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4107    StreamPositions streampos;
   4108    AnalyzeCodestream(data, &streampos);
   4109    const std::vector<FramePositions>& fp = streampos.frames;
   4110    // We have preview, dc frame and regular frame.
   4111    EXPECT_EQ(3, fp.size());
   4112 
   4113    std::vector<uint8_t> pixels2;
   4114    pixels2.resize(pixels.size());
   4115 
   4116    int kNumEvents = 6;
   4117    int events[] = {
   4118        JXL_DEC_BASIC_INFO, JXL_DEC_COLOR_ENCODING, JXL_DEC_PREVIEW_IMAGE,
   4119        JXL_DEC_FRAME,      JXL_DEC_FULL_IMAGE,     JXL_DEC_FRAME_PROGRESSION,
   4120    };
   4121    size_t end_positions[] = {
   4122        streampos.basic_info,     fp[0].frame_start,
   4123        fp[1].frame_start,        fp[2].toc_end,
   4124        streampos.codestream_end, streampos.codestream_end};
   4125    int events_wanted = 0;
   4126    for (int j = 0; j < kNumEvents; ++j) {
   4127      events_wanted |= events[j];
   4128      size_t end_pos = end_positions[j];
   4129      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4130      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted));
   4131      EXPECT_EQ(JXL_DEC_SUCCESS,
   4132                JxlDecoderSetInput(dec, data.data(), data.size()));
   4133      EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4134      VerifyFilePosition(streampos.basic_info, data, dec);
   4135      if (j >= 1) {
   4136        EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   4137        VerifyFilePosition(fp[0].frame_start, data, dec);
   4138      }
   4139      if (j >= 2) {
   4140        EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec));
   4141        VerifyFilePosition(fp[0].toc_end, data, dec);
   4142        size_t buffer_size;
   4143        EXPECT_EQ(JXL_DEC_SUCCESS,
   4144                  JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size));
   4145        EXPECT_GE(pixels2.size(), buffer_size);
   4146        EXPECT_EQ(JXL_DEC_SUCCESS,
   4147                  JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(),
   4148                                                buffer_size));
   4149        EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec));
   4150        VerifyFilePosition(fp[1].frame_start, data, dec);
   4151      }
   4152      if (j >= 3) {
   4153        EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4154        VerifyFilePosition(fp[2].toc_end, data, dec);
   4155        if (j >= 5) {
   4156          EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kDC));
   4157        }
   4158      }
   4159      if (j >= 4) {
   4160        EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   4161        VerifyFilePosition(fp[2].toc_end, data, dec);
   4162        size_t buffer_size;
   4163        EXPECT_EQ(JXL_DEC_SUCCESS,
   4164                  JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4165        EXPECT_EQ(pixels2.size(), buffer_size);
   4166        EXPECT_EQ(JXL_DEC_SUCCESS,
   4167                  JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(),
   4168                                              pixels2.size()));
   4169        if (j >= 5) {
   4170          EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, JxlDecoderProcessInput(dec));
   4171          VerifyFilePosition(fp[2].section_end[1], data, dec);
   4172        }
   4173        EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4174        VerifyFilePosition(streampos.codestream_end, data, dec);
   4175      }
   4176      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   4177      VerifyFilePosition(end_pos, data, dec);
   4178      JxlDecoderDestroy(dec);
   4179    }
   4180  }
   4181 }
   4182 
   4183 JXL_TRANSCODE_JPEG_TEST(DecodeTest, InputHandlingTestJPEGOneshot) {
   4184  TEST_LIBJPEG_SUPPORT();
   4185  size_t xsize = 123;
   4186  size_t ysize = 77;
   4187  size_t channels = 3;
   4188  std::vector<uint8_t> pixels =
   4189      jxl::test::GetSomeTestImage(xsize, ysize, channels, /*seed=*/0);
   4190  for (int i = 1; i < kCSBF_NUM_ENTRIES; ++i) {
   4191    printf("Testing with box format %d\n", i);
   4192    std::vector<uint8_t> jpeg_codestream;
   4193    jxl::TestCodestreamParams params;
   4194    params.cparams.color_transform = jxl::ColorTransform::kNone;
   4195    params.jpeg_codestream = &jpeg_codestream;
   4196    params.preview_mode = jxl::kSmallPreview;
   4197    params.box_format = static_cast<CodeStreamBoxFormat>(i);
   4198    std::vector<uint8_t> data =
   4199        jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4200                                     xsize, ysize, channels, params);
   4201    JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4202    StreamPositions streampos;
   4203    AnalyzeCodestream(data, &streampos);
   4204    const std::vector<FramePositions>& fp = streampos.frames;
   4205    // We have preview and regular frame.
   4206    EXPECT_EQ(2, fp.size());
   4207    EXPECT_LT(0, streampos.jbrd_end);
   4208 
   4209    std::vector<uint8_t> pixels2;
   4210    pixels2.resize(pixels.size());
   4211 
   4212    int kNumEvents = 6;
   4213    int events[] = {JXL_DEC_BASIC_INFO,     JXL_DEC_JPEG_RECONSTRUCTION,
   4214                    JXL_DEC_COLOR_ENCODING, JXL_DEC_PREVIEW_IMAGE,
   4215                    JXL_DEC_FRAME,          JXL_DEC_FULL_IMAGE};
   4216    size_t end_positions[] = {streampos.basic_info, streampos.basic_info,
   4217                              fp[0].frame_start,    fp[1].frame_start,
   4218                              fp[1].toc_end,        streampos.codestream_end};
   4219    int events_wanted = 0;
   4220    for (int j = 0; j < kNumEvents; ++j) {
   4221      printf("j = %d\n", j);
   4222      events_wanted |= events[j];
   4223      size_t end_pos = end_positions[j];
   4224      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4225      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted));
   4226      EXPECT_EQ(JXL_DEC_SUCCESS,
   4227                JxlDecoderSetInput(dec, data.data(), data.size()));
   4228      if (j >= 1) {
   4229        EXPECT_EQ(JXL_DEC_JPEG_RECONSTRUCTION, JxlDecoderProcessInput(dec));
   4230        VerifyFilePosition(streampos.jbrd_end, data, dec);
   4231      }
   4232      EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4233      VerifyFilePosition(streampos.basic_info, data, dec);
   4234      if (j >= 2) {
   4235        EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
   4236        VerifyFilePosition(fp[0].frame_start, data, dec);
   4237      }
   4238      if (j >= 3) {
   4239        EXPECT_EQ(JXL_DEC_NEED_PREVIEW_OUT_BUFFER, JxlDecoderProcessInput(dec));
   4240        VerifyFilePosition(fp[0].toc_end, data, dec);
   4241        size_t buffer_size;
   4242        EXPECT_EQ(JXL_DEC_SUCCESS,
   4243                  JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size));
   4244        EXPECT_GE(pixels2.size(), buffer_size);
   4245        EXPECT_EQ(JXL_DEC_SUCCESS,
   4246                  JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(),
   4247                                                buffer_size));
   4248        EXPECT_EQ(JXL_DEC_PREVIEW_IMAGE, JxlDecoderProcessInput(dec));
   4249        VerifyFilePosition(fp[1].frame_start, data, dec);
   4250      }
   4251      if (j >= 4) {
   4252        EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4253        VerifyFilePosition(fp[1].toc_end, data, dec);
   4254      }
   4255      if (j >= 5) {
   4256        EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   4257        VerifyFilePosition(fp[1].toc_end, data, dec);
   4258        size_t buffer_size;
   4259        EXPECT_EQ(JXL_DEC_SUCCESS,
   4260                  JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4261        EXPECT_EQ(pixels2.size(), buffer_size);
   4262        EXPECT_EQ(JXL_DEC_SUCCESS,
   4263                  JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(),
   4264                                              pixels2.size()));
   4265        EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4266        VerifyFilePosition(streampos.codestream_end, data, dec);
   4267      }
   4268      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   4269      VerifyFilePosition(end_pos, data, dec);
   4270      JxlDecoderDestroy(dec);
   4271    }
   4272  }
   4273 }
   4274 
   4275 TEST(DecodeTest, InputHandlingTestStreaming) {
   4276  size_t xsize = 508;
   4277  size_t ysize = 470;
   4278  uint32_t num_channels = 3;
   4279  std::vector<uint8_t> pixels =
   4280      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4281  for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
   4282    printf("Testing with box format %d\n", i);
   4283    fflush(stdout);
   4284    jxl::TestCodestreamParams params;
   4285    params.cparams.progressive_dc = 1;
   4286    params.box_format = static_cast<CodeStreamBoxFormat>(i);
   4287    params.preview_mode = jxl::kSmallPreview;
   4288    std::vector<uint8_t> data =
   4289        jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4290                                     xsize, ysize, num_channels, params);
   4291    JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4292    StreamPositions streampos;
   4293    AnalyzeCodestream(data, &streampos);
   4294    const std::vector<FramePositions>& fp = streampos.frames;
   4295    // We have preview, dc frame and regular frame.
   4296    EXPECT_EQ(3, fp.size());
   4297    std::vector<uint8_t> pixels2;
   4298    pixels2.resize(pixels.size());
   4299    int events_wanted =
   4300        (JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_PREVIEW_IMAGE |
   4301         JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION |
   4302         JXL_DEC_BOX);
   4303    for (size_t increment : {1, 7, 27, 1024}) {
   4304      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4305      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, events_wanted));
   4306      size_t file_pos = 0;
   4307      size_t box_index = 0;
   4308      size_t avail_in = 0;
   4309      for (;;) {
   4310        const uint8_t* next_in = data.data() + file_pos;
   4311        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   4312        JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   4313        size_t remaining = JxlDecoderReleaseInput(dec);
   4314        size_t consumed = avail_in - remaining;
   4315        file_pos += consumed;
   4316        avail_in += increment;
   4317        avail_in = std::min<size_t>(avail_in, data.size() - file_pos);
   4318        if (status == JXL_DEC_BASIC_INFO) {
   4319          EXPECT_EQ(file_pos, streampos.basic_info);
   4320        } else if (status == JXL_DEC_COLOR_ENCODING) {
   4321          EXPECT_EQ(file_pos, streampos.frames[0].frame_start);
   4322        } else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) {
   4323          EXPECT_EQ(file_pos, streampos.frames[0].toc_end);
   4324          size_t buffer_size;
   4325          EXPECT_EQ(JXL_DEC_SUCCESS,
   4326                    JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size));
   4327          EXPECT_GE(pixels2.size(), buffer_size);
   4328          EXPECT_EQ(JXL_DEC_SUCCESS,
   4329                    JxlDecoderSetPreviewOutBuffer(dec, &format, pixels2.data(),
   4330                                                  buffer_size));
   4331        } else if (status == JXL_DEC_PREVIEW_IMAGE) {
   4332          EXPECT_EQ(file_pos, streampos.frames[1].frame_start);
   4333        } else if (status == JXL_DEC_FRAME) {
   4334          EXPECT_EQ(file_pos, streampos.frames[2].toc_end);
   4335          EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetProgressiveDetail(dec, kDC));
   4336        } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
   4337          EXPECT_EQ(file_pos, streampos.frames[2].toc_end);
   4338          size_t buffer_size;
   4339          EXPECT_EQ(JXL_DEC_SUCCESS,
   4340                    JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4341          EXPECT_EQ(pixels2.size(), buffer_size);
   4342          EXPECT_EQ(JXL_DEC_SUCCESS,
   4343                    JxlDecoderSetImageOutBuffer(dec, &format, pixels2.data(),
   4344                                                pixels2.size()));
   4345        } else if (status == JXL_DEC_FRAME_PROGRESSION) {
   4346          EXPECT_EQ(file_pos, streampos.frames[2].section_end[1]);
   4347        } else if (status == JXL_DEC_FULL_IMAGE) {
   4348          EXPECT_EQ(file_pos, streampos.codestream_end);
   4349        } else if (status == JXL_DEC_SUCCESS) {
   4350          EXPECT_EQ(file_pos, streampos.codestream_end);
   4351          break;
   4352        } else if (status == JXL_DEC_NEED_MORE_INPUT) {
   4353          EXPECT_LT(remaining, 12);
   4354          if ((i == kCSBF_None && file_pos >= 2) ||
   4355              (box_index > 0 && box_index < streampos.box_start.size() &&
   4356               file_pos >= streampos.box_start[box_index - 1] + 12 &&
   4357               file_pos < streampos.box_start[box_index])) {
   4358            EXPECT_EQ(remaining, 0);
   4359          }
   4360          if (file_pos == data.size()) break;
   4361        } else if (status == JXL_DEC_BOX) {
   4362          ASSERT_LT(box_index, streampos.box_start.size());
   4363          EXPECT_EQ(file_pos, streampos.box_start[box_index++]);
   4364        } else {
   4365          printf("Unexpected status: 0x%x\n", static_cast<int>(status));
   4366          FAIL();
   4367        }
   4368      }
   4369      JxlDecoderDestroy(dec);
   4370    }
   4371  }
   4372 }
   4373 
   4374 TEST(DecodeTest, FlushTest) {
   4375  // Size large enough for multiple groups, required to have progressive
   4376  // stages
   4377  size_t xsize = 333;
   4378  size_t ysize = 300;
   4379  uint32_t num_channels = 3;
   4380  std::vector<uint8_t> pixels =
   4381      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4382  jxl::TestCodestreamParams params;
   4383  params.preview_mode = jxl::kSmallPreview;
   4384  std::vector<uint8_t> data =
   4385      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4386                                   xsize, ysize, num_channels, params);
   4387  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4388 
   4389  std::vector<uint8_t> pixels2;
   4390  pixels2.resize(pixels.size());
   4391 
   4392  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4393 
   4394  EXPECT_EQ(JXL_DEC_SUCCESS,
   4395            JxlDecoderSubscribeEvents(
   4396                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   4397 
   4398  // Ensure that the first part contains at least the full DC of the image,
   4399  // otherwise flush does not work.
   4400  size_t first_part = data.size() - 1;
   4401 
   4402  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part));
   4403 
   4404  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4405  JxlBasicInfo info;
   4406  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4407  EXPECT_EQ(info.xsize, xsize);
   4408  EXPECT_EQ(info.ysize, ysize);
   4409 
   4410  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4411 
   4412  // Output buffer not yet set
   4413  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   4414 
   4415  size_t buffer_size;
   4416  EXPECT_EQ(JXL_DEC_SUCCESS,
   4417            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4418  EXPECT_EQ(pixels2.size(), buffer_size);
   4419  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   4420                                 dec, &format, pixels2.data(), pixels2.size()));
   4421 
   4422  // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if
   4423  // data was already input before, since the processing of the frame only
   4424  // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME.
   4425  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4426 
   4427  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4428 
   4429  // Crude test of actual pixel data: pixel threshold of about 4% (2560/65535).
   4430  // 29000 pixels can be above the threshold
   4431  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4432                                     ysize, format, format, 2560.0),
   4433            29000u);
   4434 
   4435  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4436 
   4437  size_t consumed = first_part - JxlDecoderReleaseInput(dec);
   4438 
   4439  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed,
   4440                                                data.size() - consumed));
   4441  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4442  // Lower threshold for the final (still lossy) image
   4443  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4444                                     ysize, format, format, 2560.0),
   4445            11000u);
   4446 
   4447  JxlDecoderDestroy(dec);
   4448 }
   4449 
   4450 TEST(DecodeTest, FlushTestImageOutCallback) {
   4451  // Size large enough for multiple groups, required to have progressive
   4452  // stages
   4453  size_t xsize = 333;
   4454  size_t ysize = 300;
   4455  uint32_t num_channels = 3;
   4456  std::vector<uint8_t> pixels =
   4457      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4458  jxl::TestCodestreamParams params;
   4459  params.preview_mode = jxl::kSmallPreview;
   4460  std::vector<uint8_t> data =
   4461      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4462                                   xsize, ysize, num_channels, params);
   4463  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4464 
   4465  std::vector<uint8_t> pixels2;
   4466  pixels2.resize(pixels.size());
   4467 
   4468  size_t bytes_per_pixel = format.num_channels * 2;
   4469  size_t stride = bytes_per_pixel * xsize;
   4470  auto callback = [&](size_t x, size_t y, size_t num_pixels,
   4471                      const void* pixels_row) {
   4472    memcpy(pixels2.data() + stride * y + bytes_per_pixel * x, pixels_row,
   4473           num_pixels * bytes_per_pixel);
   4474  };
   4475 
   4476  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4477 
   4478  EXPECT_EQ(JXL_DEC_SUCCESS,
   4479            JxlDecoderSubscribeEvents(
   4480                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   4481 
   4482  // Ensure that the first part contains at least the full DC of the image,
   4483  // otherwise flush does not work.
   4484  size_t first_part = data.size() - 1;
   4485 
   4486  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part));
   4487 
   4488  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4489  JxlBasicInfo info;
   4490  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4491  EXPECT_EQ(info.xsize, xsize);
   4492  EXPECT_EQ(info.ysize, ysize);
   4493 
   4494  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4495 
   4496  // Output callback not yet set
   4497  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   4498 
   4499  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutCallback(
   4500                                 dec, &format,
   4501                                 [](void* opaque, size_t x, size_t y,
   4502                                    size_t xsize, const void* pixels_row) {
   4503                                   auto cb =
   4504                                       static_cast<decltype(&callback)>(opaque);
   4505                                   (*cb)(x, y, xsize, pixels_row);
   4506                                 },
   4507                                 /*opaque=*/&callback));
   4508 
   4509  // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if
   4510  // data was already input before, since the processing of the frame only
   4511  // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME.
   4512  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4513 
   4514  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4515 
   4516  // Crude test of actual pixel data: pixel threshold of about 4% (2560/65535).
   4517  // 29000 pixels can be above the threshold
   4518  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4519                                     ysize, format, format, 2560.0),
   4520            29000u);
   4521 
   4522  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4523 
   4524  size_t consumed = first_part - JxlDecoderReleaseInput(dec);
   4525 
   4526  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed,
   4527                                                data.size() - consumed));
   4528  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4529  // Lower threshold for the final (still lossy) image
   4530  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4531                                     ysize, format, format, 2560.0),
   4532            11000u);
   4533 
   4534  JxlDecoderDestroy(dec);
   4535 }
   4536 
   4537 TEST(DecodeTest, FlushTestLossyProgressiveAlpha) {
   4538  // Size large enough for multiple groups, required to have progressive
   4539  // stages
   4540  size_t xsize = 333;
   4541  size_t ysize = 300;
   4542  uint32_t num_channels = 4;
   4543  std::vector<uint8_t> pixels =
   4544      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4545  jxl::TestCodestreamParams params;
   4546  params.preview_mode = jxl::kSmallPreview;
   4547  std::vector<uint8_t> data =
   4548      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4549                                   xsize, ysize, num_channels, params);
   4550  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4551 
   4552  std::vector<uint8_t> pixels2;
   4553  pixels2.resize(pixels.size());
   4554 
   4555  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4556 
   4557  EXPECT_EQ(JXL_DEC_SUCCESS,
   4558            JxlDecoderSubscribeEvents(
   4559                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   4560 
   4561  // Ensure that the first part contains at least the full DC of the image,
   4562  // otherwise flush does not work.
   4563  size_t first_part = data.size() - 1;
   4564 
   4565  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part));
   4566 
   4567  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4568  JxlBasicInfo info;
   4569  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4570  EXPECT_EQ(info.xsize, xsize);
   4571  EXPECT_EQ(info.ysize, ysize);
   4572 
   4573  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4574 
   4575  // Output buffer not yet set
   4576  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   4577 
   4578  size_t buffer_size;
   4579  EXPECT_EQ(JXL_DEC_SUCCESS,
   4580            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4581  EXPECT_EQ(pixels2.size(), buffer_size);
   4582  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   4583                                 dec, &format, pixels2.data(), pixels2.size()));
   4584 
   4585  // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if
   4586  // data was already input before, since the processing of the frame only
   4587  // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME.
   4588  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4589 
   4590  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4591 
   4592  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4593                                     ysize, format, format, 2560.0),
   4594            30000u);
   4595 
   4596  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4597 
   4598  size_t consumed = first_part - JxlDecoderReleaseInput(dec);
   4599 
   4600  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed,
   4601                                                data.size() - consumed));
   4602 
   4603  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4604  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4605                                     ysize, format, format, 2560.0),
   4606            11000u);
   4607 
   4608  JxlDecoderDestroy(dec);
   4609 }
   4610 TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) {
   4611  size_t xsize = 533;
   4612  size_t ysize = 401;
   4613  uint32_t num_channels = 4;
   4614  std::vector<uint8_t> pixels =
   4615      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4616  jxl::TestCodestreamParams params;
   4617  params.cparams.resampling = 2;
   4618  params.cparams.ec_resampling = 4;
   4619  params.preview_mode = jxl::kSmallPreview;
   4620  std::vector<uint8_t> data =
   4621      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4622                                   xsize, ysize, num_channels, params);
   4623  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4624 
   4625  std::vector<uint8_t> pixels2;
   4626  pixels2.resize(pixels.size());
   4627 
   4628  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4629 
   4630  EXPECT_EQ(JXL_DEC_SUCCESS,
   4631            JxlDecoderSubscribeEvents(
   4632                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   4633 
   4634  // Ensure that the first part contains at least the full DC of the image,
   4635  // otherwise flush does not work.
   4636  size_t first_part = data.size() * 2 / 3;
   4637 
   4638  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part));
   4639 
   4640  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4641  JxlBasicInfo info;
   4642  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4643  EXPECT_EQ(info.xsize, xsize);
   4644  EXPECT_EQ(info.ysize, ysize);
   4645 
   4646  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4647 
   4648  // Output buffer not yet set
   4649  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   4650 
   4651  size_t buffer_size;
   4652  EXPECT_EQ(JXL_DEC_SUCCESS,
   4653            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4654  EXPECT_EQ(pixels2.size(), buffer_size);
   4655  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   4656                                 dec, &format, pixels2.data(), pixels2.size()));
   4657 
   4658  // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if
   4659  // data was already input before, since the processing of the frame only
   4660  // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME.
   4661  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4662 
   4663  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4664 
   4665  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4666                                     ysize, format, format, 2560.0),
   4667            125000u);
   4668 
   4669  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4670 
   4671  size_t consumed = first_part - JxlDecoderReleaseInput(dec);
   4672 
   4673  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed,
   4674                                                data.size() - consumed));
   4675 
   4676  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4677  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4678                                     ysize, format, format, 2560.0),
   4679            70000u);
   4680 
   4681  JxlDecoderDestroy(dec);
   4682 }
   4683 TEST(DecodeTest, FlushTestLosslessProgressiveAlpha) {
   4684  // Size large enough for multiple groups, required to have progressive
   4685  // stages
   4686  size_t xsize = 333;
   4687  size_t ysize = 300;
   4688  uint32_t num_channels = 4;
   4689  std::vector<uint8_t> pixels =
   4690      jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4691  jxl::TestCodestreamParams params;
   4692  params.cparams.SetLossless();
   4693  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   4694  params.cparams.responsive = 1;
   4695  params.cparams.modular_group_size_shift = 1;
   4696  params.preview_mode = jxl::kSmallPreview;
   4697  std::vector<uint8_t> data =
   4698      jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4699                                   xsize, ysize, num_channels, params);
   4700  JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4701 
   4702  std::vector<uint8_t> pixels2;
   4703  pixels2.resize(pixels.size());
   4704 
   4705  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4706 
   4707  EXPECT_EQ(JXL_DEC_SUCCESS,
   4708            JxlDecoderSubscribeEvents(
   4709                dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME | JXL_DEC_FULL_IMAGE));
   4710 
   4711  // Ensure that the first part contains at least the full DC of the image,
   4712  // otherwise flush does not work.
   4713  size_t first_part = data.size() / 2;
   4714 
   4715  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data(), first_part));
   4716 
   4717  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   4718  JxlBasicInfo info;
   4719  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4720  EXPECT_EQ(info.xsize, xsize);
   4721  EXPECT_EQ(info.ysize, ysize);
   4722 
   4723  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   4724 
   4725  // Output buffer not yet set
   4726  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderFlushImage(dec));
   4727 
   4728  size_t buffer_size;
   4729  EXPECT_EQ(JXL_DEC_SUCCESS,
   4730            JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4731  EXPECT_EQ(pixels2.size(), buffer_size);
   4732  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   4733                                 dec, &format, pixels2.data(), pixels2.size()));
   4734 
   4735  // Must process input further until we get JXL_DEC_NEED_MORE_INPUT, even if
   4736  // data was already input before, since the processing of the frame only
   4737  // happens at the JxlDecoderProcessInput call after JXL_DEC_FRAME.
   4738  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4739 
   4740  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4741 
   4742  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4743                                     ysize, format, format, 2560.0),
   4744            2700u);
   4745 
   4746  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   4747 
   4748  size_t consumed = first_part - JxlDecoderReleaseInput(dec);
   4749 
   4750  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, data.data() + consumed,
   4751                                                data.size() - consumed));
   4752 
   4753  EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   4754  EXPECT_LE(jxl::test::ComparePixels(pixels2.data(), pixels.data(), xsize,
   4755                                     ysize, format, format),
   4756            0u);
   4757 
   4758  JxlDecoderDestroy(dec);
   4759 }
   4760 
   4761 class DecodeProgressiveTest : public ::testing::TestWithParam<int> {};
   4762 JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeProgressiveTestInstantiation,
   4763                                   DecodeProgressiveTest,
   4764                                   ::testing::Range(0, 8));
   4765 TEST_P(DecodeProgressiveTest, ProgressiveEventTest) {
   4766  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   4767  const int params = GetParam();
   4768  bool single_group = ((params & 1) != 0);
   4769  bool lossless = (((params >> 1) & 1) != 0);
   4770  uint32_t num_channels = 3 + ((params >> 2) & 1);
   4771  bool has_alpha = ((num_channels & 1) == 0);
   4772  std::set<JxlProgressiveDetail> progressive_details = {kDC, kLastPasses,
   4773                                                        kPasses};
   4774  for (auto prog_detail : progressive_details) {
   4775    // Only few combinations are expected to support outputting
   4776    // intermediate flushes for complete DC and complete passes.
   4777    // The test can be updated if more cases are expected to support it.
   4778    bool expect_flush = !has_alpha && !lossless;
   4779    size_t xsize;
   4780    size_t ysize;
   4781    if (single_group) {
   4782      // An image smaller than 256x256 ensures it contains only 1 group.
   4783      xsize = 99;
   4784      ysize = 100;
   4785    } else {
   4786      xsize = 277;
   4787      ysize = 280;
   4788    }
   4789    std::vector<uint8_t> pixels =
   4790        jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
   4791    JxlPixelFormat format = {num_channels, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   4792    jxl::ColorEncoding color_encoding = jxl::ColorEncoding::SRGB(false);
   4793    jxl::CodecInOut io{memory_manager};
   4794    EXPECT_TRUE(jxl::ConvertFromExternal(
   4795        jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, color_encoding,
   4796        /*bits_per_sample=*/16, format,
   4797        /*pool=*/nullptr, &io.Main()));
   4798    jxl::TestCodestreamParams params;
   4799    if (lossless) {
   4800      params.cparams.SetLossless();
   4801    } else {
   4802      params.cparams.butteraugli_distance = 0.5f;
   4803    }
   4804    jxl::PassDefinition passes[] = {
   4805        {2, 0, 4}, {4, 0, 4}, {8, 2, 2}, {8, 1, 2}, {8, 0, 1}};
   4806    const int kNumPasses = 5;
   4807    jxl::ProgressiveMode progressive_mode{passes};
   4808    params.cparams.custom_progressive_mode = &progressive_mode;
   4809    std::vector<uint8_t> data =
   4810        jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()),
   4811                                     xsize, ysize, num_channels, params);
   4812 
   4813    for (size_t increment : {static_cast<size_t>(1), data.size()}) {
   4814      printf(
   4815          "Testing with single_group=%s, lossless=%s, "
   4816          "num_channels=%d, prog_detail=%d, increment=%d\n",
   4817          BoolToCStr(single_group), BoolToCStr(lossless),
   4818          static_cast<int>(num_channels), static_cast<int>(prog_detail),
   4819          static_cast<int>(increment));
   4820      std::vector<std::vector<uint8_t>> passes(kNumPasses + 1);
   4821      for (int i = 0; i <= kNumPasses; ++i) {
   4822        passes[i].resize(pixels.size());
   4823      }
   4824 
   4825      JxlDecoder* dec = JxlDecoderCreate(nullptr);
   4826 
   4827      EXPECT_EQ(JXL_DEC_SUCCESS,
   4828                JxlDecoderSubscribeEvents(
   4829                    dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME |
   4830                             JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME_PROGRESSION));
   4831      EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kFrames));
   4832      EXPECT_EQ(JXL_DEC_ERROR,
   4833                JxlDecoderSetProgressiveDetail(dec, kDCProgressive));
   4834      EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kDCGroups));
   4835      EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSetProgressiveDetail(dec, kGroups));
   4836      EXPECT_EQ(JXL_DEC_SUCCESS,
   4837                JxlDecoderSetProgressiveDetail(dec, prog_detail));
   4838 
   4839      uint8_t* next_in = data.data();
   4840      size_t avail_in = 0;
   4841      size_t pos = 0;
   4842 
   4843      auto process_input = [&]() {
   4844        for (;;) {
   4845          EXPECT_EQ(JXL_DEC_SUCCESS,
   4846                    JxlDecoderSetInput(dec, next_in, avail_in));
   4847          JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   4848          size_t remaining = JxlDecoderReleaseInput(dec);
   4849          EXPECT_LE(remaining, avail_in);
   4850          next_in += avail_in - remaining;
   4851          avail_in = remaining;
   4852          if (status == JXL_DEC_NEED_MORE_INPUT && pos < data.size()) {
   4853            size_t chunk = std::min<size_t>(increment, data.size() - pos);
   4854            pos += chunk;
   4855            avail_in += chunk;
   4856            continue;
   4857          }
   4858          return status;
   4859        }
   4860      };
   4861 
   4862      EXPECT_EQ(JXL_DEC_BASIC_INFO, process_input());
   4863      JxlBasicInfo info;
   4864      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   4865      EXPECT_EQ(info.xsize, xsize);
   4866      EXPECT_EQ(info.ysize, ysize);
   4867 
   4868      EXPECT_EQ(JXL_DEC_FRAME, process_input());
   4869 
   4870      size_t buffer_size;
   4871      EXPECT_EQ(JXL_DEC_SUCCESS,
   4872                JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   4873      EXPECT_EQ(pixels.size(), buffer_size);
   4874      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   4875                                     dec, &format, passes[kNumPasses].data(),
   4876                                     passes[kNumPasses].size()));
   4877 
   4878      auto next_pass = [&](int pass) {
   4879        if (prog_detail <= kDC) return kNumPasses;
   4880        if (prog_detail <= kLastPasses) {
   4881          return std::min(pass + 2, kNumPasses);
   4882        }
   4883        return pass + 1;
   4884      };
   4885 
   4886      if (expect_flush) {
   4887        // Return a particular downsampling ratio only after the last
   4888        // pass for that downsampling was processed.
   4889        int expected_downsampling_ratios[] = {8, 8, 4, 4, 2};
   4890        for (int p = 0; p < kNumPasses; p = next_pass(p)) {
   4891          EXPECT_EQ(JXL_DEC_FRAME_PROGRESSION, process_input());
   4892          EXPECT_EQ(expected_downsampling_ratios[p],
   4893                    JxlDecoderGetIntendedDownsamplingRatio(dec));
   4894          EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderFlushImage(dec));
   4895          passes[p] = passes[kNumPasses];
   4896        }
   4897      }
   4898 
   4899      EXPECT_EQ(JXL_DEC_FULL_IMAGE, process_input());
   4900      EXPECT_EQ(JXL_DEC_SUCCESS, process_input());
   4901 
   4902      JxlDecoderDestroy(dec);
   4903 
   4904      if (!expect_flush) {
   4905        continue;
   4906      }
   4907      jxl::ButteraugliParams butteraugli_params;
   4908      std::vector<float> distances(kNumPasses + 1);
   4909      for (int p = 0;; p = next_pass(p)) {
   4910        jxl::CodecInOut io1{memory_manager};
   4911        EXPECT_TRUE(jxl::ConvertFromExternal(
   4912            jxl::Bytes(passes[p].data(), passes[p].size()), xsize, ysize,
   4913            color_encoding,
   4914            /*bits_per_sample=*/16, format,
   4915            /*pool=*/nullptr, &io1.Main()));
   4916        distances[p] =
   4917            ButteraugliDistance(io.frames, io1.frames, butteraugli_params,
   4918                                *JxlGetDefaultCms(), nullptr, nullptr);
   4919        if (p == kNumPasses) break;
   4920      }
   4921      const float kMaxDistance[kNumPasses + 1] = {30.0f, 20.0f, 10.0f,
   4922                                                  5.0f,  3.0f,  2.0f};
   4923      EXPECT_LT(distances[kNumPasses], kMaxDistance[kNumPasses]);
   4924      for (int p = 0; p < kNumPasses;) {
   4925        int next_p = next_pass(p);
   4926        EXPECT_LT(distances[p], kMaxDistance[p]);
   4927        // Verify that the returned pass image is actually not the
   4928        // same as the next pass image, by checking that it has a bit
   4929        // worse butteraugli score.
   4930        EXPECT_LT(distances[next_p] * 1.1f, distances[p]);
   4931        p = next_p;
   4932      }
   4933    }
   4934  }
   4935 }
   4936 
   4937 void VerifyJPEGReconstruction(jxl::Span<const uint8_t> container,
   4938                              jxl::Span<const uint8_t> jpeg_bytes) {
   4939  JxlDecoderPtr dec = JxlDecoderMake(nullptr);
   4940  EXPECT_EQ(JXL_DEC_SUCCESS,
   4941            JxlDecoderSubscribeEvents(
   4942                dec.get(), JXL_DEC_JPEG_RECONSTRUCTION | JXL_DEC_FULL_IMAGE));
   4943  JxlDecoderSetInput(dec.get(), container.data(), container.size());
   4944  EXPECT_EQ(JXL_DEC_JPEG_RECONSTRUCTION, JxlDecoderProcessInput(dec.get()));
   4945  std::vector<uint8_t> reconstructed_buffer(128);
   4946  EXPECT_EQ(JXL_DEC_SUCCESS,
   4947            JxlDecoderSetJPEGBuffer(dec.get(), reconstructed_buffer.data(),
   4948                                    reconstructed_buffer.size()));
   4949  size_t used = 0;
   4950  JxlDecoderStatus process_result = JXL_DEC_JPEG_NEED_MORE_OUTPUT;
   4951  while (process_result == JXL_DEC_JPEG_NEED_MORE_OUTPUT) {
   4952    used = reconstructed_buffer.size() - JxlDecoderReleaseJPEGBuffer(dec.get());
   4953    reconstructed_buffer.resize(reconstructed_buffer.size() * 2);
   4954    EXPECT_EQ(
   4955        JXL_DEC_SUCCESS,
   4956        JxlDecoderSetJPEGBuffer(dec.get(), reconstructed_buffer.data() + used,
   4957                                reconstructed_buffer.size() - used));
   4958    process_result = JxlDecoderProcessInput(dec.get());
   4959  }
   4960  ASSERT_EQ(JXL_DEC_FULL_IMAGE, process_result);
   4961  used = reconstructed_buffer.size() - JxlDecoderReleaseJPEGBuffer(dec.get());
   4962  ASSERT_EQ(used, jpeg_bytes.size());
   4963  EXPECT_EQ(0, memcmp(reconstructed_buffer.data(), jpeg_bytes.data(), used));
   4964 }
   4965 
   4966 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructTestCodestream) {
   4967  TEST_LIBJPEG_SUPPORT();
   4968  size_t xsize = 123;
   4969  size_t ysize = 77;
   4970  size_t channels = 3;
   4971  std::vector<uint8_t> pixels =
   4972      jxl::test::GetSomeTestImage(xsize, ysize, channels, /*seed=*/0);
   4973  std::vector<uint8_t> jpeg_codestream;
   4974  jxl::TestCodestreamParams params;
   4975  params.cparams.color_transform = jxl::ColorTransform::kNone;
   4976  params.box_format = kCSBF_Single;
   4977  params.jpeg_codestream = &jpeg_codestream;
   4978  params.preview_mode = jxl::kSmallPreview;
   4979  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   4980      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, channels, params);
   4981  VerifyJPEGReconstruction(jxl::Bytes(compressed), jxl::Bytes(jpeg_codestream));
   4982 }
   4983 
   4984 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructionTest) {
   4985  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   4986  const std::string jpeg_path = "jxl/flower/flower.png.im_q85_420.jpg";
   4987  const std::vector<uint8_t> orig = jxl::test::ReadTestData(jpeg_path);
   4988  jxl::CodecInOut orig_io{memory_manager};
   4989  ASSERT_TRUE(jxl::jpeg::DecodeImageJPG(jxl::Bytes(orig), &orig_io));
   4990  jxl::jpeg::JPEGData jpeg_data_copy = *orig_io.Main().jpeg_data;
   4991  orig_io.metadata.m.xyb_encoded = false;
   4992  jxl::BitWriter writer{memory_manager};
   4993  ASSERT_TRUE(WriteCodestreamHeaders(&orig_io.metadata, &writer, nullptr));
   4994  writer.ZeroPadToByte();
   4995  jxl::CompressParams cparams;
   4996  cparams.color_transform = jxl::ColorTransform::kNone;
   4997  ASSERT_TRUE(jxl::EncodeFrame(memory_manager, cparams, jxl::FrameInfo{},
   4998                               &orig_io.metadata, orig_io.Main(),
   4999                               *JxlGetDefaultCms(),
   5000                               /*pool=*/nullptr, &writer,
   5001                               /*aux_out=*/nullptr));
   5002 
   5003  std::vector<uint8_t> jpeg_data;
   5004  ASSERT_TRUE(
   5005      EncodeJPEGData(memory_manager, jpeg_data_copy, &jpeg_data, cparams));
   5006  std::vector<uint8_t> container;
   5007  jxl::Bytes(jxl::kContainerHeader).AppendTo(container);
   5008  jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false,
   5009                       &container);
   5010  jxl::Bytes(jpeg_data).AppendTo(container);
   5011  jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), 0, true, &container);
   5012  jxl::PaddedBytes codestream = std::move(writer).TakeBytes();
   5013  jxl::Bytes(codestream).AppendTo(container);
   5014  VerifyJPEGReconstruction(jxl::Bytes(container), jxl::Bytes(orig));
   5015 }
   5016 
   5017 JXL_TRANSCODE_JPEG_TEST(DecodeTest, JPEGReconstructionMetadataTest) {
   5018  const std::string jpeg_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jpg";
   5019  const std::string jxl_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jxl";
   5020  const std::vector<uint8_t> jpeg = jxl::test::ReadTestData(jpeg_path);
   5021  const std::vector<uint8_t> jxl = jxl::test::ReadTestData(jxl_path);
   5022  VerifyJPEGReconstruction(jxl::Bytes(jxl), jxl::Bytes(jpeg));
   5023 }
   5024 
   5025 TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) {
   5026  size_t xsize = 80;
   5027  size_t ysize = 90;
   5028  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   5029  jxl::TestCodestreamParams params;
   5030  params.box_format = kCSBF_Multi_Other_Terminated;
   5031  params.add_icc_profile = true;
   5032  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   5033      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   5034  StreamPositions streampos;
   5035  AnalyzeCodestream(compressed, &streampos);
   5036 
   5037  // The non-essential final box size including 8-byte header
   5038  size_t final_box_size = unk3_box_size + 8;
   5039  size_t last_box_begin = compressed.size() - final_box_size;
   5040  // Verify that the test is indeed setup correctly to be at the beginning of
   5041  // the 'unkn' box header.
   5042  ASSERT_EQ(compressed[last_box_begin + 3], final_box_size);
   5043  ASSERT_EQ(compressed[last_box_begin + 4], 'u');
   5044  ASSERT_EQ(compressed[last_box_begin + 5], 'n');
   5045  ASSERT_EQ(compressed[last_box_begin + 6], 'k');
   5046  ASSERT_EQ(compressed[last_box_begin + 7], '3');
   5047 
   5048  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5049 
   5050  EXPECT_EQ(JXL_DEC_SUCCESS,
   5051            JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO | JXL_DEC_FRAME));
   5052 
   5053  EXPECT_EQ(JXL_DEC_SUCCESS,
   5054            JxlDecoderSetInput(dec, compressed.data(), last_box_begin));
   5055 
   5056  EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   5057  EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec));
   5058  // The decoder returns success despite not having seen the final unknown box
   5059  // yet. This is because calling JxlDecoderCloseInput is not mandatory for
   5060  // backwards compatibility, so it doesn't know more bytes follow, the current
   5061  // bytes ended at a perfectly valid place.
   5062  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   5063 
   5064  size_t remaining = JxlDecoderReleaseInput(dec);
   5065  // Since the test was set up to end exactly at the boundary of the final
   5066  // codestream box, and the decoder returned success, all bytes are expected to
   5067  // be consumed until the end of the  frame header.
   5068  EXPECT_EQ(remaining, last_box_begin - streampos.frames[0].toc_end);
   5069 
   5070  // Now set the remaining non-codestream box as input.
   5071  EXPECT_EQ(JXL_DEC_SUCCESS,
   5072            JxlDecoderSetInput(dec, compressed.data() + last_box_begin,
   5073                               compressed.size() - last_box_begin));
   5074  // Even though JxlDecoderProcessInput already returned JXL_DEC_SUCCESS before,
   5075  // when calling it again now after setting more input, success is expected, no
   5076  // event occurs but the box has been successfully skipped.
   5077  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   5078 
   5079  JxlDecoderDestroy(dec);
   5080 }
   5081 
   5082 namespace {
   5083 bool BoxTypeEquals(const std::string& type_string, const JxlBoxType type) {
   5084  return type_string.size() == 4 && type_string[0] == type[0] &&
   5085         type_string[1] == type[1] && type_string[2] == type[2] &&
   5086         type_string[3] == type[3];
   5087 }
   5088 }  // namespace
   5089 
   5090 TEST(DecodeTest, ExtendedBoxSizeTest) {
   5091  const std::string jxl_path = "jxl/boxes/square-extended-size-container.jxl";
   5092  const std::vector<uint8_t> orig = jxl::test::ReadTestData(jxl_path);
   5093  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5094 
   5095  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX));
   5096 
   5097  JxlBoxType type;
   5098  uint64_t box_size;
   5099  uint64_t contents_size;
   5100  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, orig.data(), orig.size()));
   5101  EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
   5102  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5103  EXPECT_TRUE(BoxTypeEquals("JXL ", type));
   5104  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
   5105  EXPECT_EQ(12, box_size);
   5106  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
   5107  EXPECT_EQ(contents_size + 8, box_size);
   5108  EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
   5109  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5110  EXPECT_TRUE(BoxTypeEquals("ftyp", type));
   5111  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
   5112  EXPECT_EQ(20, box_size);
   5113  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
   5114  EXPECT_EQ(contents_size + 8, box_size);
   5115  EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
   5116  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5117  EXPECT_TRUE(BoxTypeEquals("jxlc", type));
   5118  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
   5119  EXPECT_EQ(72, box_size);
   5120  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size));
   5121  // This is an extended box, hence the difference between `box_size` and
   5122  // `contents_size` is 16.
   5123  EXPECT_EQ(contents_size + 8 + 8, box_size);
   5124 
   5125  JxlDecoderDestroy(dec);
   5126 }
   5127 
   5128 JXL_BOXES_TEST(DecodeTest, BoxTest) {
   5129  size_t xsize = 1;
   5130  size_t ysize = 1;
   5131  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   5132  jxl::TestCodestreamParams params;
   5133  params.box_format = kCSBF_Multi_Other_Terminated;
   5134  params.add_icc_profile = true;
   5135  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   5136      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   5137 
   5138  JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5139 
   5140  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX));
   5141 
   5142  std::vector<std::string> expected_box_types = {
   5143      "JXL ", "ftyp", "jxlp", "unk1", "unk2", "jxlp", "jxlp", "jxlp", "unk3"};
   5144 
   5145  // Value 0 means to not test the size: codestream is not required to be a
   5146  // particular exact size.
   5147  std::vector<size_t> expected_box_sizes = {12, 20, 0, 34, 18, 0, 0, 0, 20};
   5148 
   5149  JxlBoxType type;
   5150  uint64_t box_size;
   5151  uint64_t contents_size;
   5152  std::vector<uint8_t> contents(50);
   5153  size_t expected_release_size = 0;
   5154 
   5155  // Cannot get these when decoding didn't start yet
   5156  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5157  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderGetBoxSizeRaw(dec, &box_size));
   5158 
   5159  uint8_t* next_in = compressed.data();
   5160  size_t avail_in = compressed.size();
   5161  for (size_t i = 0; i < expected_box_types.size(); i++) {
   5162    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   5163    EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec));
   5164    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5165    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size));
   5166    EXPECT_TRUE(BoxTypeEquals(expected_box_types[i], type));
   5167    if (expected_box_sizes[i]) {
   5168      EXPECT_EQ(expected_box_sizes[i], box_size);
   5169      EXPECT_EQ(JXL_DEC_SUCCESS,
   5170                JxlDecoderGetBoxSizeContents(dec, &contents_size));
   5171      EXPECT_EQ(contents_size + 8, box_size);
   5172    }
   5173 
   5174    if (expected_release_size > 0) {
   5175      EXPECT_EQ(expected_release_size, JxlDecoderReleaseBoxBuffer(dec));
   5176      expected_release_size = 0;
   5177    }
   5178 
   5179    if (type[0] == 'u' && type[1] == 'n' && type[2] == 'k') {
   5180      JxlDecoderSetBoxBuffer(dec, contents.data(), contents.size());
   5181      size_t expected_box_contents_size =
   5182          type[3] == '1' ? unk1_box_size
   5183                         : (type[3] == '2' ? unk2_box_size : unk3_box_size);
   5184      expected_release_size = contents.size() - expected_box_contents_size;
   5185    }
   5186    size_t consumed = avail_in - JxlDecoderReleaseInput(dec);
   5187    next_in += consumed;
   5188    avail_in -= consumed;
   5189  }
   5190 
   5191  // After the last DEC_BOX event, check that the input position is exactly at
   5192  // the stat of the box header.
   5193  EXPECT_EQ(avail_in, expected_box_sizes.back());
   5194 
   5195  // Even though all input is given, the decoder cannot assume there aren't
   5196  // more boxes if the input was not closed.
   5197  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
   5198  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
   5199  JxlDecoderCloseInput(dec);
   5200  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   5201 
   5202  JxlDecoderDestroy(dec);
   5203 }
   5204 
   5205 JXL_BOXES_TEST(DecodeTest, ExifBrobBoxTest) {
   5206  size_t xsize = 1;
   5207  size_t ysize = 1;
   5208  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   5209  jxl::TestCodestreamParams params;
   5210  // Lossless to verify pixels exactly after roundtrip.
   5211  params.cparams.SetLossless();
   5212  params.box_format = kCSBF_Brob_Exif;
   5213  params.add_icc_profile = true;
   5214  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   5215      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   5216 
   5217  // Test raw brob box, not brotli-decompressing
   5218  for (int streaming = 0; streaming < 2; ++streaming) {
   5219    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5220 
   5221    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX));
   5222    if (!streaming) {
   5223      EXPECT_EQ(JXL_DEC_SUCCESS,
   5224                JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   5225      JxlDecoderCloseInput(dec);
   5226    }
   5227    // for streaming input case
   5228    const uint8_t* next_in = compressed.data();
   5229    size_t avail_in = 0;
   5230    size_t total_in = 0;
   5231    size_t step_size = 64;
   5232 
   5233    std::vector<uint8_t> box_buffer;
   5234    size_t box_num_output;
   5235    bool seen_brob_begin = false;
   5236    bool seen_brob_end = false;
   5237 
   5238    for (;;) {
   5239      JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   5240      if (status == JXL_DEC_NEED_MORE_INPUT) {
   5241        if (streaming) {
   5242          size_t remaining = JxlDecoderReleaseInput(dec);
   5243          EXPECT_LE(remaining, avail_in);
   5244          next_in += avail_in - remaining;
   5245          avail_in = remaining;
   5246          size_t amount = step_size;
   5247          if (total_in + amount > compressed.size()) {
   5248            amount = compressed.size() - total_in;
   5249          }
   5250          avail_in += amount;
   5251          total_in += amount;
   5252          EXPECT_EQ(JXL_DEC_SUCCESS,
   5253                    JxlDecoderSetInput(dec, next_in, avail_in));
   5254          if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
   5255        } else {
   5256          FAIL();
   5257          break;
   5258        }
   5259      } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) {
   5260        if (!box_buffer.empty()) {
   5261          EXPECT_EQ(false, seen_brob_end);
   5262          seen_brob_end = true;
   5263          size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5264          box_num_output = box_buffer.size() - remaining;
   5265          EXPECT_EQ(box_num_output, box_brob_exif_size - 8);
   5266          EXPECT_EQ(
   5267              0, memcmp(box_buffer.data(), box_brob_exif + 8, box_num_output));
   5268          box_buffer.clear();
   5269        }
   5270        if (status == JXL_DEC_SUCCESS) break;
   5271        JxlBoxType type;
   5272        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5273        if (BoxTypeEquals("brob", type)) {
   5274          EXPECT_EQ(false, seen_brob_begin);
   5275          seen_brob_begin = true;
   5276          box_buffer.resize(8);
   5277          JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size());
   5278        }
   5279      } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
   5280        size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5281        box_num_output = box_buffer.size() - remaining;
   5282        box_buffer.resize(box_buffer.size() * 2);
   5283        JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output,
   5284                               box_buffer.size() - box_num_output);
   5285      } else {
   5286        // We do not expect any other events or errors
   5287        FAIL();
   5288        break;
   5289      }
   5290    }
   5291 
   5292    EXPECT_EQ(true, seen_brob_begin);
   5293    EXPECT_EQ(true, seen_brob_end);
   5294 
   5295    JxlDecoderDestroy(dec);
   5296  }
   5297 
   5298  // Test decompressed brob box
   5299  for (int streaming = 0; streaming < 2; ++streaming) {
   5300    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5301 
   5302    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BOX));
   5303    if (!streaming) {
   5304      EXPECT_EQ(JXL_DEC_SUCCESS,
   5305                JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   5306      JxlDecoderCloseInput(dec);
   5307    }
   5308    // for streaming input case
   5309    const uint8_t* next_in = compressed.data();
   5310    size_t avail_in = 0;
   5311    size_t total_in = 0;
   5312    size_t step_size = 64;
   5313 
   5314    std::vector<uint8_t> box_buffer;
   5315    size_t box_num_output;
   5316    bool seen_exif_begin = false;
   5317    bool seen_exif_end = false;
   5318 
   5319    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetDecompressBoxes(dec, JXL_TRUE));
   5320 
   5321    for (;;) {
   5322      JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   5323      if (status == JXL_DEC_NEED_MORE_INPUT) {
   5324        if (streaming) {
   5325          size_t remaining = JxlDecoderReleaseInput(dec);
   5326          EXPECT_LE(remaining, avail_in);
   5327          next_in += avail_in - remaining;
   5328          avail_in = remaining;
   5329          size_t amount = step_size;
   5330          if (total_in + amount > compressed.size()) {
   5331            amount = compressed.size() - total_in;
   5332          }
   5333          avail_in += amount;
   5334          total_in += amount;
   5335          EXPECT_EQ(JXL_DEC_SUCCESS,
   5336                    JxlDecoderSetInput(dec, next_in, avail_in));
   5337          if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
   5338        } else {
   5339          FAIL();
   5340          break;
   5341        }
   5342      } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) {
   5343        if (!box_buffer.empty()) {
   5344          EXPECT_EQ(false, seen_exif_end);
   5345          seen_exif_end = true;
   5346          size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5347          box_num_output = box_buffer.size() - remaining;
   5348          // Expect that the output has the same size and contents as the
   5349          // uncompressed exif data. Only check contents if the sizes match to
   5350          // avoid comparing uninitialized memory in the test.
   5351          EXPECT_EQ(box_num_output, exif_uncompressed_size);
   5352          if (box_num_output == exif_uncompressed_size) {
   5353            EXPECT_EQ(0, memcmp(box_buffer.data(), exif_uncompressed,
   5354                                exif_uncompressed_size));
   5355          }
   5356          box_buffer.clear();
   5357        }
   5358        if (status == JXL_DEC_SUCCESS) break;
   5359        JxlBoxType type;
   5360        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_TRUE));
   5361        if (BoxTypeEquals("Exif", type)) {
   5362          EXPECT_EQ(false, seen_exif_begin);
   5363          seen_exif_begin = true;
   5364          box_buffer.resize(8);
   5365          JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size());
   5366        }
   5367      } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
   5368        size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5369        box_num_output = box_buffer.size() - remaining;
   5370        box_buffer.resize(box_buffer.size() * 2);
   5371        JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output,
   5372                               box_buffer.size() - box_num_output);
   5373      } else {
   5374        // We do not expect any other events or errors
   5375        FAIL();
   5376        break;
   5377      }
   5378    }
   5379 
   5380    EXPECT_EQ(true, seen_exif_begin);
   5381    EXPECT_EQ(true, seen_exif_end);
   5382 
   5383    JxlDecoderDestroy(dec);
   5384  }
   5385 }
   5386 
   5387 JXL_BOXES_TEST(DecodeTest, PartialCodestreamBoxTest) {
   5388  size_t xsize = 23;
   5389  size_t ysize = 81;
   5390  std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
   5391  JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
   5392  // Lossless to verify pixels exactly after roundtrip.
   5393  jxl::TestCodestreamParams params;
   5394  params.cparams.SetLossless();
   5395  params.cparams.speed_tier = jxl::SpeedTier::kThunder;
   5396  params.box_format = kCSBF_Multi;
   5397  params.add_icc_profile = true;
   5398  std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream(
   5399      jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 4, params);
   5400 
   5401  std::vector<uint8_t> extracted_codestream;
   5402 
   5403  {
   5404    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5405 
   5406    EXPECT_EQ(JXL_DEC_SUCCESS,
   5407              JxlDecoderSubscribeEvents(
   5408                  dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX));
   5409    EXPECT_EQ(JXL_DEC_SUCCESS,
   5410              JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   5411    JxlDecoderCloseInput(dec);
   5412 
   5413    size_t num_jxlp = 0;
   5414 
   5415    std::vector<uint8_t> pixels2;
   5416    pixels2.resize(pixels.size());
   5417 
   5418    std::vector<uint8_t> box_buffer;
   5419    size_t box_num_output;
   5420 
   5421    for (;;) {
   5422      JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   5423      if (status == JXL_DEC_NEED_MORE_INPUT) {
   5424        FAIL();
   5425        break;
   5426      } else if (status == JXL_DEC_BASIC_INFO) {
   5427        JxlBasicInfo info;
   5428        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   5429        EXPECT_EQ(info.xsize, xsize);
   5430        EXPECT_EQ(info.ysize, ysize);
   5431      } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
   5432        EXPECT_EQ(JXL_DEC_SUCCESS,
   5433                  JxlDecoderSetImageOutBuffer(dec, &format_orig, pixels2.data(),
   5434                                              pixels2.size()));
   5435      } else if (status == JXL_DEC_FULL_IMAGE) {
   5436        continue;
   5437      } else if (status == JXL_DEC_BOX || status == JXL_DEC_SUCCESS) {
   5438        if (!box_buffer.empty()) {
   5439          size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5440          box_num_output = box_buffer.size() - remaining;
   5441          EXPECT_GE(box_num_output, 4);
   5442          // Do not insert the first 4 bytes, which are not part of the
   5443          // codestream, but the partial codestream box index
   5444          extracted_codestream.insert(extracted_codestream.end(),
   5445                                      box_buffer.begin() + 4,
   5446                                      box_buffer.begin() + box_num_output);
   5447          box_buffer.clear();
   5448        }
   5449        if (status == JXL_DEC_SUCCESS) break;
   5450        JxlBoxType type;
   5451        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE));
   5452        if (BoxTypeEquals("jxlp", type)) {
   5453          num_jxlp++;
   5454          box_buffer.resize(8);
   5455          JxlDecoderSetBoxBuffer(dec, box_buffer.data(), box_buffer.size());
   5456        }
   5457      } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
   5458        size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5459        box_num_output = box_buffer.size() - remaining;
   5460        box_buffer.resize(box_buffer.size() * 2);
   5461        JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output,
   5462                               box_buffer.size() - box_num_output);
   5463      } else {
   5464        // We do not expect any other events or errors
   5465        FAIL();
   5466        break;
   5467      }
   5468    }
   5469 
   5470    // The test file created with kCSBF_Multi is expected to have 4 jxlp boxes.
   5471    EXPECT_EQ(4, num_jxlp);
   5472 
   5473    EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   5474                                           ysize, format_orig, format_orig));
   5475 
   5476    JxlDecoderDestroy(dec);
   5477  }
   5478 
   5479  // Now test whether the codestream extracted from the jxlp boxes can itself
   5480  // also be decoded and gives the same pixels
   5481  {
   5482    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5483 
   5484    EXPECT_EQ(JXL_DEC_SUCCESS,
   5485              JxlDecoderSubscribeEvents(
   5486                  dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX));
   5487    EXPECT_EQ(JXL_DEC_SUCCESS,
   5488              JxlDecoderSetInput(dec, extracted_codestream.data(),
   5489                                 extracted_codestream.size()));
   5490    JxlDecoderCloseInput(dec);
   5491 
   5492    size_t num_boxes = 0;
   5493 
   5494    std::vector<uint8_t> pixels2;
   5495    pixels2.resize(pixels.size());
   5496 
   5497    std::vector<uint8_t> box_buffer;
   5498    size_t box_num_output;
   5499 
   5500    for (;;) {
   5501      JxlDecoderStatus status = JxlDecoderProcessInput(dec);
   5502      if (status == JXL_DEC_NEED_MORE_INPUT) {
   5503        FAIL();
   5504        break;
   5505      } else if (status == JXL_DEC_BASIC_INFO) {
   5506        JxlBasicInfo info;
   5507        EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
   5508        EXPECT_EQ(info.xsize, xsize);
   5509        EXPECT_EQ(info.ysize, ysize);
   5510      } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
   5511        EXPECT_EQ(JXL_DEC_SUCCESS,
   5512                  JxlDecoderSetImageOutBuffer(dec, &format_orig, pixels2.data(),
   5513                                              pixels2.size()));
   5514      } else if (status == JXL_DEC_FULL_IMAGE) {
   5515        continue;
   5516      } else if (status == JXL_DEC_BOX) {
   5517        num_boxes++;
   5518      } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
   5519        size_t remaining = JxlDecoderReleaseBoxBuffer(dec);
   5520        box_num_output = box_buffer.size() - remaining;
   5521        box_buffer.resize(box_buffer.size() * 2);
   5522        JxlDecoderSetBoxBuffer(dec, box_buffer.data() + box_num_output,
   5523                               box_buffer.size() - box_num_output);
   5524      } else if (status == JXL_DEC_SUCCESS) {
   5525        break;
   5526      } else {
   5527        // We do not expect any other events or errors
   5528        FAIL();
   5529        break;
   5530      }
   5531    }
   5532 
   5533    EXPECT_EQ(0, num_boxes);  // The data does not use the container format.
   5534    EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
   5535                                           ysize, format_orig, format_orig));
   5536 
   5537    JxlDecoderDestroy(dec);
   5538  }
   5539 }
   5540 
   5541 TEST(DecodeTest, SpotColorTest) {
   5542  JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
   5543  jxl::CodecInOut io{memory_manager};
   5544  size_t xsize = 55;
   5545  size_t ysize = 257;
   5546  io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB();
   5547  JXL_TEST_ASSIGN_OR_DIE(Image3F main,
   5548                         Image3F::Create(memory_manager, xsize, ysize));
   5549  JXL_TEST_ASSIGN_OR_DIE(ImageF spot,
   5550                         ImageF::Create(memory_manager, xsize, ysize));
   5551  jxl::ZeroFillImage(&main);
   5552  jxl::ZeroFillImage(&spot);
   5553 
   5554  for (size_t y = 0; y < ysize; y++) {
   5555    float* JXL_RESTRICT rowm = main.PlaneRow(1, y);
   5556    float* JXL_RESTRICT rows = spot.Row(y);
   5557    for (size_t x = 0; x < xsize; x++) {
   5558      rowm[x] = (x + y) * (1.f / 255.f);
   5559      rows[x] = ((x ^ y) & 255) * (1.f / 255.f);
   5560    }
   5561  }
   5562  ASSERT_TRUE(
   5563      io.SetFromImage(std::move(main), jxl::ColorEncoding::LinearSRGB()));
   5564  jxl::ExtraChannelInfo info;
   5565  info.bit_depth.bits_per_sample = 8;
   5566  info.dim_shift = 0;
   5567  info.type = jxl::ExtraChannel::kSpotColor;
   5568  info.spot_color[0] = 0.5f;
   5569  info.spot_color[1] = 0.2f;
   5570  info.spot_color[2] = 1.f;
   5571  info.spot_color[3] = 0.5f;
   5572 
   5573  io.metadata.m.extra_channel_info.push_back(info);
   5574  std::vector<ImageF> ec;
   5575  ec.push_back(std::move(spot));
   5576  ASSERT_TRUE(io.frames[0].SetExtraChannels(std::move(ec)));
   5577 
   5578  jxl::CompressParams cparams;
   5579  cparams.speed_tier = jxl::SpeedTier::kLightning;
   5580  cparams.modular_mode = true;
   5581  cparams.color_transform = jxl::ColorTransform::kNone;
   5582  cparams.butteraugli_distance = 0.f;
   5583 
   5584  std::vector<uint8_t> compressed;
   5585  EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed));
   5586 
   5587  for (size_t render_spot = 0; render_spot < 2; render_spot++) {
   5588    JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
   5589 
   5590    JxlDecoder* dec = JxlDecoderCreate(nullptr);
   5591 
   5592    EXPECT_EQ(JXL_DEC_SUCCESS,
   5593              JxlDecoderSubscribeEvents(
   5594                  dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
   5595    if (!render_spot) {
   5596      EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetRenderSpotcolors(dec, JXL_FALSE));
   5597    }
   5598 
   5599    EXPECT_EQ(JXL_DEC_SUCCESS,
   5600              JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
   5601    EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
   5602    JxlBasicInfo binfo;
   5603    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &binfo));
   5604    EXPECT_EQ(1u, binfo.num_extra_channels);
   5605    EXPECT_EQ(xsize, binfo.xsize);
   5606    EXPECT_EQ(ysize, binfo.ysize);
   5607 
   5608    JxlExtraChannelInfo extra_info;
   5609    EXPECT_EQ(JXL_DEC_SUCCESS,
   5610              JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info));
   5611    EXPECT_EQ(static_cast<unsigned int>(jxl::ExtraChannel::kSpotColor),
   5612              extra_info.type);
   5613 
   5614    EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
   5615    size_t buffer_size;
   5616    EXPECT_EQ(JXL_DEC_SUCCESS,
   5617              JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
   5618    size_t extra_size;
   5619    EXPECT_EQ(JXL_DEC_SUCCESS,
   5620              JxlDecoderExtraChannelBufferSize(dec, &format, &extra_size, 0));
   5621 
   5622    std::vector<uint8_t> image(buffer_size);
   5623    std::vector<uint8_t> extra(extra_size);
   5624    size_t bytes_per_pixel = format.num_channels *
   5625                             jxl::test::GetDataBits(format.data_type) /
   5626                             jxl::kBitsPerByte;
   5627    size_t stride = bytes_per_pixel * binfo.xsize;
   5628 
   5629    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
   5630                                   dec, &format, image.data(), image.size()));
   5631    EXPECT_EQ(JXL_DEC_SUCCESS,
   5632              JxlDecoderSetExtraChannelBuffer(dec, &format, extra.data(),
   5633                                              extra.size(), 0));
   5634 
   5635    EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
   5636 
   5637    // After the full image was output, JxlDecoderProcessInput should return
   5638    // success to indicate all is done.
   5639    EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
   5640    JxlDecoderDestroy(dec);
   5641 
   5642    for (size_t y = 0; y < ysize; y++) {
   5643      uint8_t* JXL_RESTRICT rowm = image.data() + stride * y;
   5644      uint8_t* JXL_RESTRICT rows = extra.data() + xsize * y;
   5645      for (size_t x = 0; x < xsize; x++) {
   5646        if (!render_spot) {
   5647          // if spot color isn't rendered, main image should be as we made it
   5648          // (red and blue are all zeroes)
   5649 
   5650          EXPECT_EQ(rowm[x * 3 + 0], 0);
   5651          EXPECT_EQ(rowm[x * 3 + 1], (x + y > 255 ? 255 : x + y));
   5652          EXPECT_EQ(rowm[x * 3 + 2], 0);
   5653        }
   5654        if (render_spot) {
   5655          // if spot color is rendered, expect red and blue to look like the
   5656          // spot color channel
   5657          EXPECT_LT(abs(rowm[x * 3 + 0] - (rows[x] * 0.25f)), 1);
   5658          EXPECT_LT(abs(rowm[x * 3 + 2] - (rows[x] * 0.5f)), 1);
   5659        }
   5660        EXPECT_EQ(rows[x], ((x ^ y) & 255));
   5661      }
   5662    }
   5663  }
   5664 }
   5665 
   5666 TEST(DecodeTest, CloseInput) {
   5667  std::vector<uint8_t> partial_file = {0xff};
   5668 
   5669  JxlDecoderPtr dec = JxlDecoderMake(nullptr);
   5670  EXPECT_EQ(JXL_DEC_SUCCESS,
   5671            JxlDecoderSubscribeEvents(dec.get(),
   5672                                      JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
   5673  EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec.get(), partial_file.data(),
   5674                                                partial_file.size()));
   5675  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get()));
   5676  EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get()));
   5677  JxlDecoderCloseInput(dec.get());
   5678  EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderProcessInput(dec.get()));
   5679 }