libjxl

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

encode_api_test.cc (28049B)


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