tor-browser

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

encode_api_test.cc (29432B)


      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 <algorithm>
      7 #include <cstddef>
      8 #include <cstdint>
      9 #include <cstdio>
     10 #include <cstdlib>
     11 #include <cstring>
     12 #include <ostream>
     13 #include <sstream>
     14 #include <string>
     15 #include <vector>
     16 
     17 #include "lib/jpegli/encode.h"
     18 #include "lib/jpegli/libjpeg_test_util.h"
     19 #include "lib/jpegli/test_params.h"
     20 #include "lib/jpegli/test_utils.h"
     21 #include "lib/jpegli/testing.h"
     22 #include "lib/jpegli/types.h"
     23 
     24 namespace jpegli {
     25 namespace {
     26 
     27 struct TestConfig {
     28  TestImage input;
     29  CompressParams jparams;
     30  JpegIOMode input_mode = PIXELS;
     31  double max_bpp;
     32  double max_dist;
     33 };
     34 
     35 class EncodeAPITestParam : public ::testing::TestWithParam<TestConfig> {};
     36 
     37 void GenerateInput(JpegIOMode input_mode, const CompressParams& jparams,
     38                   TestImage* input) {
     39  GeneratePixels(input);
     40  if (input_mode == RAW_DATA) {
     41    GenerateRawData(jparams, input);
     42  } else if (input_mode == COEFFICIENTS) {
     43    GenerateCoeffs(jparams, input);
     44  }
     45 }
     46 
     47 TEST_P(EncodeAPITestParam, TestAPI) {
     48  TestConfig config = GetParam();
     49  GenerateInput(config.input_mode, config.jparams, &config.input);
     50  std::vector<uint8_t> compressed;
     51  ASSERT_TRUE(EncodeWithJpegli(config.input, config.jparams, &compressed));
     52  if (config.jparams.icc.empty()) {
     53    double bpp =
     54        compressed.size() * 8.0 / (config.input.xsize * config.input.ysize);
     55    printf("bpp: %f\n", bpp);
     56    EXPECT_LT(bpp, config.max_bpp);
     57  }
     58  DecompressParams dparams;
     59  dparams.output_mode =
     60      config.input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS;
     61  if (config.jparams.set_jpeg_colorspace &&
     62      config.jparams.jpeg_color_space == JCS_GRAYSCALE) {
     63    ConvertToGrayscale(&config.input);
     64  } else {
     65    dparams.set_out_color_space = true;
     66    dparams.out_color_space = config.input.color_space;
     67  }
     68  TestImage output;
     69  DecodeWithLibjpeg(config.jparams, dparams, compressed, &output);
     70  VerifyOutputImage(config.input, output, config.max_dist);
     71 }
     72 
     73 TEST(EncodeAPITest, ReuseCinfoSameImageTwice) {
     74  TestImage input;
     75  input.xsize = 129;
     76  input.ysize = 73;
     77  CompressParams jparams;
     78  GenerateInput(PIXELS, jparams, &input);
     79  uint8_t* buffer = nullptr;
     80  unsigned long buffer_size = 0;  // NOLINT
     81  std::vector<uint8_t> compressed0;
     82  std::vector<uint8_t> compressed1;
     83  jpeg_compress_struct cinfo;
     84  const auto try_catch_block = [&]() -> bool {
     85    ERROR_HANDLER_SETUP(jpegli);
     86    jpegli_create_compress(&cinfo);
     87    jpegli_mem_dest(&cinfo, &buffer, &buffer_size);
     88    EncodeWithJpegli(input, jparams, &cinfo);
     89    compressed0.assign(buffer, buffer + buffer_size);
     90    jpegli_mem_dest(&cinfo, &buffer, &buffer_size);
     91    EncodeWithJpegli(input, jparams, &cinfo);
     92    compressed1.assign(buffer, buffer + buffer_size);
     93    return true;
     94  };
     95  EXPECT_TRUE(try_catch_block());
     96  jpegli_destroy_compress(&cinfo);
     97  if (buffer) free(buffer);
     98  ASSERT_EQ(compressed0.size(), compressed1.size());
     99  EXPECT_EQ(0,
    100            memcmp(compressed0.data(), compressed1.data(), compressed0.size()));
    101 }
    102 
    103 std::vector<TestConfig> GenerateBasicConfigs() {
    104  std::vector<TestConfig> all_configs;
    105  for (int samp : {1, 2}) {
    106    for (int progr : {0, 2}) {
    107      for (int optimize : {0, 1}) {
    108        if (progr && optimize) continue;
    109        TestConfig config;
    110        config.input.xsize = 257 + samp * 37;
    111        config.input.ysize = 265 + optimize * 17;
    112        config.jparams.h_sampling = {samp, 1, 1};
    113        config.jparams.v_sampling = {samp, 1, 1};
    114        config.jparams.progressive_mode = progr;
    115        config.jparams.optimize_coding = optimize;
    116        config.max_dist = 2.4f;
    117        GeneratePixels(&config.input);
    118        all_configs.push_back(config);
    119      }
    120    }
    121  }
    122  return all_configs;
    123 }
    124 
    125 TEST(EncodeAPITest, ReuseCinfoSameMemOutput) {
    126  std::vector<TestConfig> all_configs = GenerateBasicConfigs();
    127  uint8_t* buffer = nullptr;
    128  unsigned long buffer_size = 0;  // NOLINT
    129  {
    130    jpeg_compress_struct cinfo;
    131    const auto try_catch_block = [&]() -> bool {
    132      ERROR_HANDLER_SETUP(jpegli);
    133      jpegli_create_compress(&cinfo);
    134      jpegli_mem_dest(&cinfo, &buffer, &buffer_size);
    135      for (const TestConfig& config : all_configs) {
    136        EncodeWithJpegli(config.input, config.jparams, &cinfo);
    137      }
    138      return true;
    139    };
    140    EXPECT_TRUE(try_catch_block());
    141    jpegli_destroy_compress(&cinfo);
    142  }
    143  size_t pos = 0;
    144  for (auto& config : all_configs) {
    145    TestImage output;
    146    pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0,
    147                             buffer + pos, buffer_size - pos, &output);
    148    VerifyOutputImage(config.input, output, config.max_dist);
    149  }
    150  if (buffer) free(buffer);
    151 }
    152 
    153 TEST(EncodeAPITest, ReuseCinfoSameStdOutput) {
    154  std::vector<TestConfig> all_configs = GenerateBasicConfigs();
    155  FILE* tmpf = tmpfile();
    156  ASSERT_TRUE(tmpf);
    157  {
    158    jpeg_compress_struct cinfo;
    159    const auto try_catch_block = [&]() -> bool {
    160      ERROR_HANDLER_SETUP(jpegli);
    161      jpegli_create_compress(&cinfo);
    162      jpegli_stdio_dest(&cinfo, tmpf);
    163      for (const TestConfig& config : all_configs) {
    164        EncodeWithJpegli(config.input, config.jparams, &cinfo);
    165      }
    166      return true;
    167    };
    168    EXPECT_TRUE(try_catch_block());
    169    jpegli_destroy_compress(&cinfo);
    170  }
    171  size_t total_size = ftell(tmpf);
    172  fseek(tmpf, 0, SEEK_SET);
    173  std::vector<uint8_t> compressed(total_size);
    174  ASSERT_TRUE(total_size == fread(compressed.data(), 1, total_size, tmpf));
    175  fclose(tmpf);
    176  size_t pos = 0;
    177  for (auto& config : all_configs) {
    178    TestImage output;
    179    pos +=
    180        DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0,
    181                          &compressed[pos], compressed.size() - pos, &output);
    182    VerifyOutputImage(config.input, output, config.max_dist);
    183  }
    184 }
    185 
    186 TEST(EncodeAPITest, ReuseCinfoChangeParams) {
    187  TestImage input;
    188  TestImage output;
    189  CompressParams jparams;
    190  DecompressParams dparams;
    191  uint8_t* buffer = nullptr;
    192  unsigned long buffer_size = 0;  // NOLINT
    193  std::vector<uint8_t> compressed;
    194  jpeg_compress_struct cinfo;
    195  const auto max_rms = [](int q, int hs, int vs) {
    196    if (hs == 1 && vs == 1) return q == 90 ? 2.2 : 0.6;
    197    if (hs == 2 && vs == 2) return q == 90 ? 2.8 : 1.2;
    198    return q == 90 ? 2.4 : 1.0;
    199  };
    200  const auto try_catch_block = [&]() -> bool {
    201    ERROR_HANDLER_SETUP(jpegli);
    202    jpegli_create_compress(&cinfo);
    203    input.xsize = 129;
    204    input.ysize = 73;
    205    dparams.set_out_color_space = true;
    206    for (JpegIOMode input_mode : {PIXELS, RAW_DATA, PIXELS, COEFFICIENTS}) {
    207      for (int h_samp : {2, 1}) {
    208        for (int v_samp : {2, 1}) {
    209          for (int progr : {0, 2}) {
    210            for (int quality : {90, 100}) {
    211              input.Clear();
    212              input.color_space =
    213                  (input_mode == RAW_DATA ? JCS_YCbCr : JCS_RGB);
    214              jparams.quality = quality;
    215              jparams.h_sampling = {h_samp, 1, 1};
    216              jparams.v_sampling = {v_samp, 1, 1};
    217              jparams.progressive_mode = progr;
    218              printf(
    219                  "Generating input with quality %d chroma subsampling %dx%d "
    220                  "input mode %d progressive_mode %d\n",
    221                  quality, h_samp, v_samp, input_mode, progr);
    222              GenerateInput(input_mode, jparams, &input);
    223              jpegli_mem_dest(&cinfo, &buffer, &buffer_size);
    224              if (input_mode != COEFFICIENTS) {
    225                cinfo.image_width = input.xsize;
    226                cinfo.image_height = input.ysize;
    227                cinfo.input_components = input.components;
    228                jpegli_set_defaults(&cinfo);
    229                jpegli_start_compress(&cinfo, TRUE);
    230                jpegli_abort_compress(&cinfo);
    231                jpegli_mem_dest(&cinfo, &buffer, &buffer_size);
    232              }
    233              EncodeWithJpegli(input, jparams, &cinfo);
    234              compressed.resize(buffer_size);
    235              std::copy_n(buffer, buffer_size, compressed.data());
    236              dparams.output_mode =
    237                  input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS;
    238              dparams.out_color_space = input.color_space;
    239              output.Clear();
    240              DecodeWithLibjpeg(jparams, dparams, compressed, &output);
    241              VerifyOutputImage(input, output,
    242                                max_rms(quality, h_samp, v_samp));
    243            }
    244          }
    245        }
    246      }
    247    }
    248    return true;
    249  };
    250  EXPECT_TRUE(try_catch_block());
    251  jpegli_destroy_compress(&cinfo);
    252  if (buffer) free(buffer);
    253 }
    254 
    255 TEST(EncodeAPITest, AbbreviatedStreams) {
    256  uint8_t* table_stream = nullptr;
    257  unsigned long table_stream_size = 0;  // NOLINT
    258  uint8_t* data_stream = nullptr;
    259  unsigned long data_stream_size = 0;  // NOLINT
    260  {
    261    jpeg_compress_struct cinfo;
    262    const auto try_catch_block = [&]() -> bool {
    263      ERROR_HANDLER_SETUP(jpegli);
    264      jpegli_create_compress(&cinfo);
    265      jpegli_mem_dest(&cinfo, &table_stream, &table_stream_size);
    266      cinfo.input_components = 3;
    267      cinfo.in_color_space = JCS_RGB;
    268      jpegli_set_defaults(&cinfo);
    269      jpegli_write_tables(&cinfo);
    270      jpegli_mem_dest(&cinfo, &data_stream, &data_stream_size);
    271      cinfo.image_width = 1;
    272      cinfo.image_height = 1;
    273      cinfo.optimize_coding = FALSE;
    274      jpegli_set_progressive_level(&cinfo, 0);
    275      jpegli_start_compress(&cinfo, FALSE);
    276      JSAMPLE image[3] = {0};
    277      JSAMPROW row[] = {image};
    278      jpegli_write_scanlines(&cinfo, row, 1);
    279      jpegli_finish_compress(&cinfo);
    280      return true;
    281    };
    282    EXPECT_TRUE(try_catch_block());
    283    EXPECT_LT(data_stream_size, 50);
    284    jpegli_destroy_compress(&cinfo);
    285  }
    286  TestImage output;
    287  DecodeWithLibjpeg(CompressParams(), DecompressParams(), table_stream,
    288                    table_stream_size, data_stream, data_stream_size, &output);
    289  EXPECT_EQ(1, output.xsize);
    290  EXPECT_EQ(1, output.ysize);
    291  EXPECT_EQ(3, output.components);
    292  EXPECT_EQ(0, output.pixels[0]);
    293  EXPECT_EQ(0, output.pixels[1]);
    294  EXPECT_EQ(0, output.pixels[2]);
    295  if (table_stream) free(table_stream);
    296  if (data_stream) free(data_stream);
    297 }
    298 
    299 void CopyQuantTables(j_compress_ptr cinfo, uint16_t* quant_tables) {
    300  for (int c = 0; c < cinfo->num_components; ++c) {
    301    int quant_idx = cinfo->comp_info[c].quant_tbl_no;
    302    JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_idx];
    303    for (int k = 0; k < DCTSIZE2; ++k) {
    304      quant_tables[c * DCTSIZE2 + k] = quant_table->quantval[k];
    305    }
    306  }
    307 }
    308 
    309 TEST(EncodeAPITest, QualitySettings) {
    310  // Test that jpegli_set_quality, jpegli_set_linear_quality and
    311  // jpegli_quality_scaling are consistent with each other.
    312  uint16_t quant_tables0[3 * DCTSIZE2];
    313  uint16_t quant_tables1[3 * DCTSIZE2];
    314  jpeg_compress_struct cinfo;
    315  const auto try_catch_block = [&]() -> bool {
    316    ERROR_HANDLER_SETUP(jpegli);
    317    jpegli_create_compress(&cinfo);
    318    cinfo.input_components = 3;
    319    cinfo.in_color_space = JCS_RGB;
    320    jpegli_set_defaults(&cinfo);
    321    for (boolean baseline : {FALSE, TRUE}) {
    322      for (int q = 1; q <= 100; ++q) {
    323        jpegli_set_quality(&cinfo, q, baseline);
    324        CopyQuantTables(&cinfo, quant_tables0);
    325        jpegli_set_linear_quality(&cinfo, jpegli_quality_scaling(q), baseline);
    326        CopyQuantTables(&cinfo, quant_tables1);
    327        EXPECT_EQ(0,
    328                  memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0)));
    329 #if JPEG_LIB_VERSION >= 70
    330        for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
    331          cinfo.q_scale_factor[i] = jpegli_quality_scaling(q);
    332        }
    333        jpegli_default_qtables(&cinfo, baseline);
    334        CopyQuantTables(&cinfo, quant_tables1);
    335        EXPECT_EQ(0,
    336                  memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0)));
    337 #endif
    338      }
    339    }
    340    return true;
    341  };
    342  EXPECT_TRUE(try_catch_block());
    343  jpegli_destroy_compress(&cinfo);
    344  // Test jpegli_quality_scaling for some specific values .
    345  EXPECT_EQ(5000, jpegli_quality_scaling(-1));
    346  EXPECT_EQ(5000, jpegli_quality_scaling(0));
    347  EXPECT_EQ(5000, jpegli_quality_scaling(1));
    348  EXPECT_EQ(100, jpegli_quality_scaling(50));
    349  EXPECT_EQ(50, jpegli_quality_scaling(75));
    350  EXPECT_EQ(20, jpegli_quality_scaling(90));
    351  EXPECT_EQ(0, jpegli_quality_scaling(100));
    352  EXPECT_EQ(0, jpegli_quality_scaling(101));
    353 }
    354 
    355 std::vector<TestConfig> GenerateTests() {
    356  std::vector<TestConfig> all_tests;
    357  for (int h_samp : {1, 2}) {
    358    for (int v_samp : {1, 2}) {
    359      for (int progr : {0, 2}) {
    360        for (int optimize : {0, 1}) {
    361          if (progr && optimize) continue;
    362          TestConfig config;
    363          config.jparams.h_sampling = {h_samp, 1, 1};
    364          config.jparams.v_sampling = {v_samp, 1, 1};
    365          config.jparams.progressive_mode = progr;
    366          if (!progr) {
    367            config.jparams.optimize_coding = optimize;
    368          }
    369          const float kMaxBpp[4] = {1.55, 1.4, 1.4, 1.32};
    370          const float kMaxDist[4] = {1.95, 2.2, 2.2, 2.0};
    371          const int idx = v_samp * 2 + h_samp - 3;
    372          config.max_bpp =
    373              kMaxBpp[idx] * (optimize ? 0.97 : 1.0) * (progr ? 0.97 : 1.0);
    374          config.max_dist = kMaxDist[idx];
    375          all_tests.push_back(config);
    376        }
    377      }
    378    }
    379  }
    380  {
    381    TestConfig config;
    382    config.jparams.quality = 100;
    383    config.jparams.h_sampling = {1, 1, 1};
    384    config.jparams.v_sampling = {1, 1, 1};
    385    config.max_bpp = 6.6;
    386    config.max_dist = 0.6;
    387    all_tests.push_back(config);
    388  }
    389  {
    390    TestConfig config;
    391    config.jparams.quality = 80;
    392    config.max_bpp = 1.05;
    393    config.max_dist = 2.7;
    394    all_tests.push_back(config);
    395  }
    396  for (int samp : {1, 2}) {
    397    for (int progr : {0, 2}) {
    398      for (int optimize : {0, 1}) {
    399        if (progr && optimize) continue;
    400        TestConfig config;
    401        config.input.xsize = 257;
    402        config.input.ysize = 265;
    403        config.jparams.h_sampling = {samp, 1, 1};
    404        config.jparams.v_sampling = {samp, 1, 1};
    405        config.jparams.progressive_mode = progr;
    406        if (!progr) {
    407          config.jparams.optimize_coding = optimize;
    408        }
    409        config.jparams.use_adaptive_quantization = false;
    410        config.max_bpp = 2.05f;
    411        config.max_dist = 2.3f;
    412        all_tests.push_back(config);
    413      }
    414    }
    415  }
    416  for (int h0_samp : {1, 2, 4}) {
    417    for (int v0_samp : {1, 2, 4}) {
    418      for (int h2_samp : {1, 2, 4}) {
    419        for (int v2_samp : {1, 2, 4}) {
    420          TestConfig config;
    421          config.input.xsize = 137;
    422          config.input.ysize = 75;
    423          config.jparams.progressive_mode = 2;
    424          config.jparams.h_sampling = {h0_samp, 1, h2_samp};
    425          config.jparams.v_sampling = {v0_samp, 1, v2_samp};
    426          config.max_bpp = 2.5;
    427          config.max_dist = 12.0;
    428          all_tests.push_back(config);
    429        }
    430      }
    431    }
    432  }
    433  for (int h0_samp : {1, 3}) {
    434    for (int v0_samp : {1, 3}) {
    435      for (int h2_samp : {1, 3}) {
    436        for (int v2_samp : {1, 3}) {
    437          TestConfig config;
    438          config.input.xsize = 205;
    439          config.input.ysize = 99;
    440          config.jparams.progressive_mode = 2;
    441          config.jparams.h_sampling = {h0_samp, 1, h2_samp};
    442          config.jparams.v_sampling = {v0_samp, 1, v2_samp};
    443          config.max_bpp = 2.5;
    444          config.max_dist = 10.0;
    445          all_tests.push_back(config);
    446        }
    447      }
    448    }
    449  }
    450  for (int h0_samp : {1, 2, 3, 4}) {
    451    for (int v0_samp : {1, 2, 3, 4}) {
    452      TestConfig config;
    453      config.input.xsize = 217;
    454      config.input.ysize = 129;
    455      config.jparams.progressive_mode = 2;
    456      config.jparams.h_sampling = {h0_samp, 1, 1};
    457      config.jparams.v_sampling = {v0_samp, 1, 1};
    458      config.max_bpp = 2.0;
    459      config.max_dist = 5.5;
    460      all_tests.push_back(config);
    461    }
    462  }
    463  for (int p = 0; p < 3 + NumTestScanScripts(); ++p) {
    464    for (int samp : {1, 2}) {
    465      for (int quality : {100, 90, 1}) {
    466        for (int r : {0, 1024, 1}) {
    467          for (int optimize : {0, 1}) {
    468            bool progressive = p == 1 || p == 2 || p > 4;
    469            if (progressive && !optimize) continue;
    470            TestConfig config;
    471            config.input.xsize = 273;
    472            config.input.ysize = 265;
    473            config.jparams.progressive_mode = p;
    474            if (!progressive) {
    475              config.jparams.optimize_coding = optimize;
    476            }
    477            config.jparams.h_sampling = {samp, 1, 1};
    478            config.jparams.v_sampling = {samp, 1, 1};
    479            config.jparams.quality = quality;
    480            config.jparams.restart_interval = r;
    481            config.max_bpp = quality == 100 ? 8.0 : 1.9;
    482            if (r == 1) {
    483              config.max_bpp += 10.0;
    484            }
    485            config.max_dist = quality == 1 ? 20.0 : 2.1;
    486            all_tests.push_back(config);
    487          }
    488        }
    489      }
    490    }
    491  }
    492  {
    493    TestConfig config;
    494    config.jparams.simple_progression = true;
    495    config.max_bpp = 1.48;
    496    config.max_dist = 2.0;
    497    all_tests.push_back(config);
    498  }
    499  {
    500    TestConfig config;
    501    config.input_mode = COEFFICIENTS;
    502    config.jparams.h_sampling = {2, 1, 1};
    503    config.jparams.v_sampling = {2, 1, 1};
    504    config.jparams.progressive_mode = 0;
    505    config.jparams.optimize_coding = 0;
    506    config.max_bpp = 16;
    507    config.max_dist = 0.0;
    508    all_tests.push_back(config);
    509  }
    510  {
    511    TestConfig config;
    512    config.jparams.xyb_mode = true;
    513    config.jparams.progressive_mode = 2;
    514    config.max_bpp = 1.5;
    515    config.max_dist = 3.5;
    516    all_tests.push_back(config);
    517  }
    518  {
    519    TestConfig config;
    520    config.jparams.libjpeg_mode = true;
    521    config.max_bpp = 2.1;
    522    config.max_dist = 1.7;
    523    config.jparams.h_sampling = {1, 1, 1};
    524    config.jparams.v_sampling = {1, 1, 1};
    525    all_tests.push_back(config);
    526  }
    527 
    528  for (J_COLOR_SPACE in_color_space :
    529       {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE, JCS_EXT_RGB, JCS_EXT_BGR,
    530        JCS_EXT_RGBA, JCS_EXT_BGRA, JCS_EXT_ARGB, JCS_EXT_ABGR}) {
    531    for (J_COLOR_SPACE jpeg_color_space : {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE}) {
    532      if (jpeg_color_space == JCS_RGB && in_color_space >= JCS_YCbCr) continue;
    533      TestConfig config;
    534      config.input.xsize = config.input.ysize = 256;
    535      config.input.color_space = in_color_space;
    536      config.jparams.set_jpeg_colorspace = true;
    537      config.jparams.jpeg_color_space = jpeg_color_space;
    538      config.jparams.h_sampling = {1, 1, 1};
    539      config.jparams.v_sampling = {1, 1, 1};
    540      config.max_bpp = jpeg_color_space == JCS_RGB ? 4.5 : 1.85;
    541      config.max_dist = jpeg_color_space == JCS_RGB ? 1.4 : 2.05;
    542      all_tests.push_back(config);
    543    }
    544  }
    545  for (J_COLOR_SPACE in_color_space : {JCS_CMYK, JCS_YCCK}) {
    546    for (J_COLOR_SPACE jpeg_color_space : {JCS_CMYK, JCS_YCCK}) {
    547      if (jpeg_color_space == JCS_CMYK && in_color_space == JCS_YCCK) continue;
    548      TestConfig config;
    549      config.input.xsize = config.input.ysize = 256;
    550      config.input.color_space = in_color_space;
    551      if (in_color_space != jpeg_color_space) {
    552        config.jparams.set_jpeg_colorspace = true;
    553        config.jparams.jpeg_color_space = jpeg_color_space;
    554      }
    555      config.jparams.h_sampling = {1, 1, 1, 1};
    556      config.jparams.v_sampling = {1, 1, 1, 1};
    557      config.max_bpp = jpeg_color_space == JCS_CMYK ? 4.0 : 3.6;
    558      config.max_dist = jpeg_color_space == JCS_CMYK ? 1.2 : 1.5;
    559      all_tests.push_back(config);
    560    }
    561  }
    562  {
    563    TestConfig config;
    564    config.input.color_space = JCS_YCbCr;
    565    config.max_bpp = 1.6;
    566    config.max_dist = 1.35;
    567    config.jparams.h_sampling = {1, 1, 1};
    568    config.jparams.v_sampling = {1, 1, 1};
    569    all_tests.push_back(config);
    570  }
    571  for (bool xyb : {false, true}) {
    572    TestConfig config;
    573    config.input.color_space = JCS_GRAYSCALE;
    574    config.jparams.xyb_mode = xyb;
    575    config.max_bpp = 1.35;
    576    config.max_dist = 1.4;
    577    all_tests.push_back(config);
    578  }
    579  for (int channels = 1; channels <= 4; ++channels) {
    580    TestConfig config;
    581    config.input.color_space = JCS_UNKNOWN;
    582    config.input.components = channels;
    583    config.max_bpp = 1.35 * channels;
    584    config.max_dist = 1.4;
    585    all_tests.push_back(config);
    586  }
    587  for (size_t r : {1, 3, 17, 1024}) {
    588    for (int progr : {0, 2}) {
    589      TestConfig config;
    590      config.jparams.restart_interval = r;
    591      config.jparams.progressive_mode = progr;
    592      config.max_bpp = 1.58 + 5.5 / r;
    593      config.max_dist = 2.2;
    594      all_tests.push_back(config);
    595    }
    596  }
    597  for (size_t rr : {1, 3, 8, 100}) {
    598    TestConfig config;
    599    config.jparams.restart_in_rows = rr;
    600    config.max_bpp = 1.6;
    601    config.max_dist = 2.2;
    602    all_tests.push_back(config);
    603  }
    604  for (int type : {0, 1, 10, 100, 10000}) {
    605    for (int scale : {1, 50, 100, 200, 500}) {
    606      for (bool add_raw : {false, true}) {
    607        for (bool baseline : {true, false}) {
    608          if (!baseline && (add_raw || type * scale < 25500)) continue;
    609          TestConfig config;
    610          config.input.xsize = 64;
    611          config.input.ysize = 64;
    612          CustomQuantTable table;
    613          table.table_type = type;
    614          table.scale_factor = scale;
    615          table.force_baseline = baseline;
    616          table.add_raw = add_raw;
    617          table.Generate();
    618          config.jparams.optimize_coding = 1;
    619          config.jparams.h_sampling = {1, 1, 1};
    620          config.jparams.v_sampling = {1, 1, 1};
    621          config.jparams.quant_tables.push_back(table);
    622          config.jparams.quant_indexes = {0, 0, 0};
    623          float q = (type == 0 ? 16 : type) * scale * 0.01f;
    624          if (baseline && !add_raw) q = std::max(1.0f, std::min(255.0f, q));
    625          config.max_bpp = 1.5f + 25.0f / q;
    626          config.max_dist = 0.6f + 0.25f * q;
    627          all_tests.push_back(config);
    628        }
    629      }
    630    }
    631  }
    632  for (int qidx = 0; qidx < 8; ++qidx) {
    633    if (qidx == 3) continue;
    634    TestConfig config;
    635    config.input.xsize = 256;
    636    config.input.ysize = 256;
    637    config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1,
    638                                    (qidx >> 0) & 1};
    639    config.jparams.h_sampling = {1, 1, 1};
    640    config.jparams.v_sampling = {1, 1, 1};
    641    config.max_bpp = 2.25;
    642    config.max_dist = 2.8;
    643    all_tests.push_back(config);
    644  }
    645  for (int qidx = 0; qidx < 8; ++qidx) {
    646    for (int slot_idx = 0; slot_idx < 2; ++slot_idx) {
    647      if (qidx == 0 && slot_idx == 0) continue;
    648      TestConfig config;
    649      config.input.xsize = 256;
    650      config.input.ysize = 256;
    651      config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1,
    652                                      (qidx >> 0) & 1};
    653      config.jparams.h_sampling = {1, 1, 1};
    654      config.jparams.v_sampling = {1, 1, 1};
    655      CustomQuantTable table;
    656      table.slot_idx = slot_idx;
    657      table.Generate();
    658      config.jparams.quant_tables.push_back(table);
    659      config.max_bpp = 2.3;
    660      config.max_dist = 2.9;
    661      all_tests.push_back(config);
    662    }
    663  }
    664  for (int qidx = 0; qidx < 8; ++qidx) {
    665    for (bool xyb : {false, true}) {
    666      TestConfig config;
    667      config.input.xsize = 256;
    668      config.input.ysize = 256;
    669      config.jparams.xyb_mode = xyb;
    670      config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1,
    671                                      (qidx >> 0) & 1};
    672      if (!xyb) {
    673        config.jparams.h_sampling = {1, 1, 1};
    674        config.jparams.v_sampling = {1, 1, 1};
    675      }
    676      {
    677        CustomQuantTable table;
    678        table.slot_idx = 0;
    679        table.Generate();
    680        config.jparams.quant_tables.push_back(table);
    681      }
    682      {
    683        CustomQuantTable table;
    684        table.slot_idx = 1;
    685        table.table_type = 20;
    686        table.Generate();
    687        config.jparams.quant_tables.push_back(table);
    688      }
    689      config.max_bpp = 2.0;
    690      config.max_dist = 3.85;
    691      all_tests.push_back(config);
    692    }
    693  }
    694  for (bool xyb : {false, true}) {
    695    TestConfig config;
    696    config.input.xsize = 256;
    697    config.input.ysize = 256;
    698    config.jparams.xyb_mode = xyb;
    699    config.jparams.quant_indexes = {0, 1, 2};
    700    if (!xyb) {
    701      config.jparams.h_sampling = {1, 1, 1};
    702      config.jparams.v_sampling = {1, 1, 1};
    703    }
    704    {
    705      CustomQuantTable table;
    706      table.slot_idx = 0;
    707      table.Generate();
    708      config.jparams.quant_tables.push_back(table);
    709    }
    710    {
    711      CustomQuantTable table;
    712      table.slot_idx = 1;
    713      table.table_type = 20;
    714      table.Generate();
    715      config.jparams.quant_tables.push_back(table);
    716    }
    717    {
    718      CustomQuantTable table;
    719      table.slot_idx = 2;
    720      table.table_type = 30;
    721      table.Generate();
    722      config.jparams.quant_tables.push_back(table);
    723    }
    724    config.max_bpp = 1.5;
    725    config.max_dist = 3.75;
    726    all_tests.push_back(config);
    727  }
    728  {
    729    TestConfig config;
    730    config.jparams.comp_ids = {7, 17, 177};
    731    config.input.xsize = config.input.ysize = 128;
    732    config.max_bpp = 2.25;
    733    config.max_dist = 2.4;
    734    all_tests.push_back(config);
    735  }
    736  for (int override_JFIF : {-1, 0, 1}) {
    737    for (int override_Adobe : {-1, 0, 1}) {
    738      if (override_JFIF == -1 && override_Adobe == -1) continue;
    739      TestConfig config;
    740      config.input.xsize = config.input.ysize = 128;
    741      config.jparams.override_JFIF = override_JFIF;
    742      config.jparams.override_Adobe = override_Adobe;
    743      config.max_bpp = 2.25;
    744      config.max_dist = 2.4;
    745      all_tests.push_back(config);
    746    }
    747  }
    748  {
    749    TestConfig config;
    750    config.input.xsize = config.input.ysize = 256;
    751    config.max_bpp = 1.85;
    752    config.max_dist = 2.05;
    753    config.jparams.add_marker = true;
    754    all_tests.push_back(config);
    755  }
    756  for (size_t icc_size : {728, 70000, 1000000}) {
    757    TestConfig config;
    758    config.input.xsize = config.input.ysize = 256;
    759    config.max_dist = 2.05;
    760    config.jparams.icc.resize(icc_size);
    761    for (size_t i = 0; i < icc_size; ++i) {
    762      config.jparams.icc[i] = (i * 17) & 0xff;
    763    }
    764    all_tests.push_back(config);
    765  }
    766  for (JpegIOMode input_mode : {PIXELS, RAW_DATA, COEFFICIENTS}) {
    767    TestConfig config;
    768    config.input.xsize = config.input.ysize = 256;
    769    config.input_mode = input_mode;
    770    if (input_mode == RAW_DATA) {
    771      config.input.color_space = JCS_YCbCr;
    772    }
    773    config.jparams.progressive_mode = 0;
    774    config.jparams.optimize_coding = 0;
    775    config.jparams.h_sampling = {1, 1, 1};
    776    config.jparams.v_sampling = {1, 1, 1};
    777    config.max_bpp = 1.85;
    778    config.max_dist = 2.05;
    779    if (input_mode == COEFFICIENTS) {
    780      config.max_bpp = 3.5;
    781      config.max_dist = 0.0;
    782    }
    783    all_tests.push_back(config);
    784    config.jparams.use_flat_dc_luma_code = true;
    785    all_tests.push_back(config);
    786  }
    787  for (int xsize : {640, 641, 648, 649}) {
    788    for (int ysize : {640, 641, 648, 649}) {
    789      for (int h_sampling : {1, 2}) {
    790        for (int v_sampling : {1, 2}) {
    791          if (h_sampling == 1 && v_sampling == 1) continue;
    792          for (int progr : {0, 2}) {
    793            TestConfig config;
    794            config.input.xsize = xsize;
    795            config.input.ysize = ysize;
    796            config.input.color_space = JCS_YCbCr;
    797            config.jparams.h_sampling = {h_sampling, 1, 1};
    798            config.jparams.v_sampling = {v_sampling, 1, 1};
    799            config.jparams.progressive_mode = progr;
    800            config.input_mode = RAW_DATA;
    801            config.max_bpp = 1.75;
    802            config.max_dist = 2.0;
    803            all_tests.push_back(config);
    804            config.input_mode = COEFFICIENTS;
    805            if (xsize & 1) {
    806              config.jparams.add_marker = true;
    807            }
    808            config.max_bpp = 24.0;
    809            all_tests.push_back(config);
    810          }
    811        }
    812      }
    813    }
    814  }
    815  for (JpegliDataType data_type : {JPEGLI_TYPE_UINT16, JPEGLI_TYPE_FLOAT}) {
    816    for (JpegliEndianness endianness :
    817         {JPEGLI_LITTLE_ENDIAN, JPEGLI_BIG_ENDIAN, JPEGLI_NATIVE_ENDIAN}) {
    818      J_COLOR_SPACE colorspace[4] = {JCS_GRAYSCALE, JCS_UNKNOWN, JCS_RGB,
    819                                     JCS_CMYK};
    820      float max_bpp[4] = {1.32, 2.7, 1.6, 4.0};
    821      for (int channels = 1; channels <= 4; ++channels) {
    822        TestConfig config;
    823        config.input.data_type = data_type;
    824        config.input.endianness = endianness;
    825        config.input.components = channels;
    826        config.input.color_space = colorspace[channels - 1];
    827        config.max_bpp = max_bpp[channels - 1];
    828        config.max_dist = 2.2;
    829        all_tests.push_back(config);
    830      }
    831    }
    832  }
    833  for (int smoothing : {1, 5, 50, 100}) {
    834    for (int h_sampling : {1, 2}) {
    835      for (int v_sampling : {1, 2}) {
    836        TestConfig config;
    837        config.input.xsize = 257;
    838        config.input.ysize = 265;
    839        config.jparams.smoothing_factor = smoothing;
    840        config.jparams.h_sampling = {h_sampling, 1, 1};
    841        config.jparams.v_sampling = {v_sampling, 1, 1};
    842        config.max_bpp = 1.85;
    843        config.max_dist = 3.05f;
    844        all_tests.push_back(config);
    845      }
    846    }
    847  }
    848  return all_tests;
    849 };
    850 
    851 std::ostream& operator<<(std::ostream& os, const TestConfig& c) {
    852  os << c.input;
    853  os << c.jparams;
    854  if (c.input_mode == RAW_DATA) {
    855    os << "RawDataIn";
    856  } else if (c.input_mode == COEFFICIENTS) {
    857    os << "WriteCoeffs";
    858  }
    859  return os;
    860 }
    861 
    862 std::string TestDescription(
    863    const testing::TestParamInfo<EncodeAPITestParam::ParamType>& info) {
    864  std::stringstream name;
    865  name << info.param;
    866  return name.str();
    867 }
    868 
    869 JPEGLI_INSTANTIATE_TEST_SUITE_P(EncodeAPITest, EncodeAPITestParam,
    870                                testing::ValuesIn(GenerateTests()),
    871                                TestDescription);
    872 }  // namespace
    873 }  // namespace jpegli