// Copyright (c) the JPEG XL Project Authors. All rights reserved. // // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include "lib/jpegli/encode.h" #include "lib/jpegli/libjpeg_test_util.h" #include "lib/jpegli/test_params.h" #include "lib/jpegli/test_utils.h" #include "lib/jpegli/testing.h" #include "lib/jpegli/types.h" #include "lib/jxl/base/status.h" namespace jpegli { namespace { struct TestConfig { TestImage input; CompressParams jparams; JpegIOMode input_mode = PIXELS; double max_bpp; double max_dist; }; class EncodeAPITestParam : public ::testing::TestWithParam {}; void GenerateInput(JpegIOMode input_mode, const CompressParams& jparams, TestImage* input) { GeneratePixels(input); if (input_mode == RAW_DATA) { GenerateRawData(jparams, input); } else if (input_mode == COEFFICIENTS) { GenerateCoeffs(jparams, input); } } TEST_P(EncodeAPITestParam, TestAPI) { TestConfig config = GetParam(); GenerateInput(config.input_mode, config.jparams, &config.input); std::vector compressed; ASSERT_TRUE(EncodeWithJpegli(config.input, config.jparams, &compressed)); if (config.jparams.icc.empty()) { double bpp = compressed.size() * 8.0 / (config.input.xsize * config.input.ysize); printf("bpp: %f\n", bpp); EXPECT_LT(bpp, config.max_bpp); } DecompressParams dparams; dparams.output_mode = config.input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS; if (config.jparams.set_jpeg_colorspace && config.jparams.jpeg_color_space == JCS_GRAYSCALE) { ConvertToGrayscale(&config.input); } else { dparams.set_out_color_space = true; dparams.out_color_space = config.input.color_space; } TestImage output; DecodeWithLibjpeg(config.jparams, dparams, compressed, &output); VerifyOutputImage(config.input, output, config.max_dist); } TEST(EncodeAPITest, ReuseCinfoSameImageTwice) { TestImage input; input.xsize = 129; input.ysize = 73; CompressParams jparams; GenerateInput(PIXELS, jparams, &input); uint8_t* buffer = nullptr; unsigned long buffer_size = 0; // NOLINT std::vector compressed0; std::vector compressed1; jpeg_compress_struct cinfo; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); jpegli_mem_dest(&cinfo, &buffer, &buffer_size); EncodeWithJpegli(input, jparams, &cinfo); compressed0.assign(buffer, buffer + buffer_size); jpegli_mem_dest(&cinfo, &buffer, &buffer_size); EncodeWithJpegli(input, jparams, &cinfo); compressed1.assign(buffer, buffer + buffer_size); return true; }; EXPECT_TRUE(try_catch_block()); jpegli_destroy_compress(&cinfo); if (buffer) free(buffer); ASSERT_EQ(compressed0.size(), compressed1.size()); EXPECT_EQ(0, memcmp(compressed0.data(), compressed1.data(), compressed0.size())); } std::vector GenerateBasicConfigs() { std::vector all_configs; for (int samp : {1, 2}) { for (int progr : {0, 2}) { for (int optimize : {0, 1}) { if (progr && optimize) continue; TestConfig config; config.input.xsize = 257 + samp * 37; config.input.ysize = 265 + optimize * 17; config.jparams.h_sampling = {samp, 1, 1}; config.jparams.v_sampling = {samp, 1, 1}; config.jparams.progressive_mode = progr; config.jparams.optimize_coding = optimize; config.max_dist = 2.4f; GeneratePixels(&config.input); all_configs.push_back(config); } } } return all_configs; } TEST(EncodeAPITest, ReuseCinfoSameMemOutput) { std::vector all_configs = GenerateBasicConfigs(); uint8_t* buffer = nullptr; unsigned long buffer_size = 0; // NOLINT { jpeg_compress_struct cinfo; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); jpegli_mem_dest(&cinfo, &buffer, &buffer_size); for (const TestConfig& config : all_configs) { EncodeWithJpegli(config.input, config.jparams, &cinfo); } return true; }; EXPECT_TRUE(try_catch_block()); jpegli_destroy_compress(&cinfo); } size_t pos = 0; for (auto& config : all_configs) { TestImage output; pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, buffer + pos, buffer_size - pos, &output); VerifyOutputImage(config.input, output, config.max_dist); } if (buffer) free(buffer); } TEST(EncodeAPITest, ReuseCinfoSameStdOutput) { std::vector all_configs = GenerateBasicConfigs(); FILE* tmpf = tmpfile(); JXL_CHECK(tmpf); { jpeg_compress_struct cinfo; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); jpegli_stdio_dest(&cinfo, tmpf); for (const TestConfig& config : all_configs) { EncodeWithJpegli(config.input, config.jparams, &cinfo); } return true; }; EXPECT_TRUE(try_catch_block()); jpegli_destroy_compress(&cinfo); } size_t total_size = ftell(tmpf); fseek(tmpf, 0, SEEK_SET); std::vector compressed(total_size); JXL_CHECK(total_size == fread(compressed.data(), 1, total_size, tmpf)); fclose(tmpf); size_t pos = 0; for (auto& config : all_configs) { TestImage output; pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, &compressed[pos], compressed.size() - pos, &output); VerifyOutputImage(config.input, output, config.max_dist); } } TEST(EncodeAPITest, ReuseCinfoChangeParams) { TestImage input; TestImage output; CompressParams jparams; DecompressParams dparams; uint8_t* buffer = nullptr; unsigned long buffer_size = 0; // NOLINT std::vector compressed; jpeg_compress_struct cinfo; const auto max_rms = [](int q, int hs, int vs) { if (hs == 1 && vs == 1) return q == 90 ? 2.2 : 0.6; if (hs == 2 && vs == 2) return q == 90 ? 2.8 : 1.2; return q == 90 ? 2.4 : 1.0; }; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); input.xsize = 129; input.ysize = 73; dparams.set_out_color_space = true; for (JpegIOMode input_mode : {PIXELS, RAW_DATA, PIXELS, COEFFICIENTS}) { for (int h_samp : {2, 1}) { for (int v_samp : {2, 1}) { for (int progr : {0, 2}) { for (int quality : {90, 100}) { input.Clear(); input.color_space = (input_mode == RAW_DATA ? JCS_YCbCr : JCS_RGB); jparams.quality = quality; jparams.h_sampling = {h_samp, 1, 1}; jparams.v_sampling = {v_samp, 1, 1}; jparams.progressive_mode = progr; printf( "Generating input with quality %d chroma subsampling %dx%d " "input mode %d progressive_mode %d\n", quality, h_samp, v_samp, input_mode, progr); GenerateInput(input_mode, jparams, &input); jpegli_mem_dest(&cinfo, &buffer, &buffer_size); if (input_mode != COEFFICIENTS) { cinfo.image_width = input.xsize; cinfo.image_height = input.ysize; cinfo.input_components = input.components; jpegli_set_defaults(&cinfo); jpegli_start_compress(&cinfo, TRUE); jpegli_abort_compress(&cinfo); jpegli_mem_dest(&cinfo, &buffer, &buffer_size); } EncodeWithJpegli(input, jparams, &cinfo); compressed.resize(buffer_size); std::copy_n(buffer, buffer_size, compressed.data()); dparams.output_mode = input_mode == COEFFICIENTS ? COEFFICIENTS : PIXELS; dparams.out_color_space = input.color_space; output.Clear(); DecodeWithLibjpeg(jparams, dparams, compressed, &output); VerifyOutputImage(input, output, max_rms(quality, h_samp, v_samp)); } } } } } return true; }; EXPECT_TRUE(try_catch_block()); jpegli_destroy_compress(&cinfo); if (buffer) free(buffer); } TEST(EncodeAPITest, AbbreviatedStreams) { uint8_t* table_stream = nullptr; unsigned long table_stream_size = 0; // NOLINT uint8_t* data_stream = nullptr; unsigned long data_stream_size = 0; // NOLINT { jpeg_compress_struct cinfo; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); jpegli_mem_dest(&cinfo, &table_stream, &table_stream_size); cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpegli_set_defaults(&cinfo); jpegli_write_tables(&cinfo); jpegli_mem_dest(&cinfo, &data_stream, &data_stream_size); cinfo.image_width = 1; cinfo.image_height = 1; cinfo.optimize_coding = FALSE; jpegli_set_progressive_level(&cinfo, 0); jpegli_start_compress(&cinfo, FALSE); JSAMPLE image[3] = {0}; JSAMPROW row[] = {image}; jpegli_write_scanlines(&cinfo, row, 1); jpegli_finish_compress(&cinfo); return true; }; EXPECT_TRUE(try_catch_block()); EXPECT_LT(data_stream_size, 50); jpegli_destroy_compress(&cinfo); } TestImage output; DecodeWithLibjpeg(CompressParams(), DecompressParams(), table_stream, table_stream_size, data_stream, data_stream_size, &output); EXPECT_EQ(1, output.xsize); EXPECT_EQ(1, output.ysize); EXPECT_EQ(3, output.components); EXPECT_EQ(0, output.pixels[0]); EXPECT_EQ(0, output.pixels[1]); EXPECT_EQ(0, output.pixels[2]); if (table_stream) free(table_stream); if (data_stream) free(data_stream); } void CopyQuantTables(j_compress_ptr cinfo, uint16_t* quant_tables) { for (int c = 0; c < cinfo->num_components; ++c) { int quant_idx = cinfo->comp_info[c].quant_tbl_no; JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[quant_idx]; for (int k = 0; k < DCTSIZE2; ++k) { quant_tables[c * DCTSIZE2 + k] = quant_table->quantval[k]; } } } TEST(EncodeAPITest, QualitySettings) { // Test that jpegli_set_quality, jpegli_set_linear_quality and // jpegli_quality_scaling are consistent with each other. uint16_t quant_tables0[3 * DCTSIZE2]; uint16_t quant_tables1[3 * DCTSIZE2]; jpeg_compress_struct cinfo; const auto try_catch_block = [&]() -> bool { ERROR_HANDLER_SETUP(jpegli); jpegli_create_compress(&cinfo); cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpegli_set_defaults(&cinfo); for (boolean baseline : {FALSE, TRUE}) { for (int q = 1; q <= 100; ++q) { jpegli_set_quality(&cinfo, q, baseline); CopyQuantTables(&cinfo, quant_tables0); jpegli_set_linear_quality(&cinfo, jpegli_quality_scaling(q), baseline); CopyQuantTables(&cinfo, quant_tables1); EXPECT_EQ(0, memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0))); #if JPEG_LIB_VERSION >= 70 for (int i = 0; i < NUM_QUANT_TBLS; ++i) { cinfo.q_scale_factor[i] = jpegli_quality_scaling(q); } jpegli_default_qtables(&cinfo, baseline); CopyQuantTables(&cinfo, quant_tables1); EXPECT_EQ(0, memcmp(quant_tables0, quant_tables1, sizeof(quant_tables0))); #endif } } return true; }; EXPECT_TRUE(try_catch_block()); jpegli_destroy_compress(&cinfo); // Test jpegli_quality_scaling for some specific values . EXPECT_EQ(5000, jpegli_quality_scaling(-1)); EXPECT_EQ(5000, jpegli_quality_scaling(0)); EXPECT_EQ(5000, jpegli_quality_scaling(1)); EXPECT_EQ(100, jpegli_quality_scaling(50)); EXPECT_EQ(50, jpegli_quality_scaling(75)); EXPECT_EQ(20, jpegli_quality_scaling(90)); EXPECT_EQ(0, jpegli_quality_scaling(100)); EXPECT_EQ(0, jpegli_quality_scaling(101)); } std::vector GenerateTests() { std::vector all_tests; for (int h_samp : {1, 2}) { for (int v_samp : {1, 2}) { for (int progr : {0, 2}) { for (int optimize : {0, 1}) { if (progr && optimize) continue; TestConfig config; config.jparams.h_sampling = {h_samp, 1, 1}; config.jparams.v_sampling = {v_samp, 1, 1}; config.jparams.progressive_mode = progr; if (!progr) { config.jparams.optimize_coding = optimize; } const float kMaxBpp[4] = {1.55, 1.4, 1.4, 1.32}; const float kMaxDist[4] = {1.95, 2.2, 2.2, 2.0}; const int idx = v_samp * 2 + h_samp - 3; config.max_bpp = kMaxBpp[idx] * (optimize ? 0.97 : 1.0) * (progr ? 0.97 : 1.0); config.max_dist = kMaxDist[idx]; all_tests.push_back(config); } } } } { TestConfig config; config.jparams.quality = 100; config.max_bpp = 6.6; config.max_dist = 0.6; all_tests.push_back(config); } { TestConfig config; config.jparams.quality = 80; config.max_bpp = 1.05; config.max_dist = 2.7; all_tests.push_back(config); } for (int samp : {1, 2}) { for (int progr : {0, 2}) { for (int optimize : {0, 1}) { if (progr && optimize) continue; TestConfig config; config.input.xsize = 257; config.input.ysize = 265; config.jparams.h_sampling = {samp, 1, 1}; config.jparams.v_sampling = {samp, 1, 1}; config.jparams.progressive_mode = progr; if (!progr) { config.jparams.optimize_coding = optimize; } config.jparams.use_adaptive_quantization = false; config.max_bpp = 2.05f; config.max_dist = 2.3f; all_tests.push_back(config); } } } for (int h0_samp : {1, 2, 4}) { for (int v0_samp : {1, 2, 4}) { for (int h2_samp : {1, 2, 4}) { for (int v2_samp : {1, 2, 4}) { TestConfig config; config.input.xsize = 137; config.input.ysize = 75; config.jparams.progressive_mode = 2; config.jparams.h_sampling = {h0_samp, 1, h2_samp}; config.jparams.v_sampling = {v0_samp, 1, v2_samp}; config.max_bpp = 2.5; config.max_dist = 12.0; all_tests.push_back(config); } } } } for (int h0_samp : {1, 3}) { for (int v0_samp : {1, 3}) { for (int h2_samp : {1, 3}) { for (int v2_samp : {1, 3}) { TestConfig config; config.input.xsize = 205; config.input.ysize = 99; config.jparams.progressive_mode = 2; config.jparams.h_sampling = {h0_samp, 1, h2_samp}; config.jparams.v_sampling = {v0_samp, 1, v2_samp}; config.max_bpp = 2.5; config.max_dist = 10.0; all_tests.push_back(config); } } } } for (int h0_samp : {1, 2, 3, 4}) { for (int v0_samp : {1, 2, 3, 4}) { TestConfig config; config.input.xsize = 217; config.input.ysize = 129; config.jparams.progressive_mode = 2; config.jparams.h_sampling = {h0_samp, 1, 1}; config.jparams.v_sampling = {v0_samp, 1, 1}; config.max_bpp = 2.0; config.max_dist = 5.5; all_tests.push_back(config); } } for (int p = 0; p < 3 + NumTestScanScripts(); ++p) { for (int samp : {1, 2}) { for (int quality : {100, 90, 1}) { for (int r : {0, 1024, 1}) { for (int optimize : {0, 1}) { bool progressive = p == 1 || p == 2 || p > 4; if (progressive && !optimize) continue; TestConfig config; config.input.xsize = 273; config.input.ysize = 265; config.jparams.progressive_mode = p; if (!progressive) { config.jparams.optimize_coding = optimize; } config.jparams.h_sampling = {samp, 1, 1}; config.jparams.v_sampling = {samp, 1, 1}; config.jparams.quality = quality; config.jparams.restart_interval = r; config.max_bpp = quality == 100 ? 8.0 : 1.9; if (r == 1) { config.max_bpp += 10.0; } config.max_dist = quality == 1 ? 20.0 : 2.1; all_tests.push_back(config); } } } } } { TestConfig config; config.jparams.simple_progression = true; config.max_bpp = 1.48; config.max_dist = 2.0; all_tests.push_back(config); } { TestConfig config; config.input_mode = COEFFICIENTS; config.jparams.h_sampling = {2, 1, 1}; config.jparams.v_sampling = {2, 1, 1}; config.jparams.progressive_mode = 0; config.jparams.optimize_coding = 0; config.max_bpp = 16; config.max_dist = 0.0; all_tests.push_back(config); } { TestConfig config; config.jparams.xyb_mode = true; config.jparams.progressive_mode = 2; config.max_bpp = 1.5; config.max_dist = 3.5; all_tests.push_back(config); } { TestConfig config; config.jparams.libjpeg_mode = true; config.max_bpp = 2.1; config.max_dist = 1.7; all_tests.push_back(config); } for (J_COLOR_SPACE in_color_space : {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE}) { for (J_COLOR_SPACE jpeg_color_space : {JCS_RGB, JCS_YCbCr, JCS_GRAYSCALE}) { if (jpeg_color_space == JCS_RGB && in_color_space == JCS_YCbCr) continue; TestConfig config; config.input.xsize = config.input.ysize = 256; config.input.color_space = in_color_space; config.jparams.set_jpeg_colorspace = true; config.jparams.jpeg_color_space = jpeg_color_space; config.max_bpp = jpeg_color_space == JCS_RGB ? 4.5 : 1.85; config.max_dist = jpeg_color_space == JCS_RGB ? 1.4 : 2.05; all_tests.push_back(config); } } for (J_COLOR_SPACE in_color_space : {JCS_CMYK, JCS_YCCK}) { for (J_COLOR_SPACE jpeg_color_space : {JCS_CMYK, JCS_YCCK}) { if (jpeg_color_space == JCS_CMYK && in_color_space == JCS_YCCK) continue; TestConfig config; config.input.xsize = config.input.ysize = 256; config.input.color_space = in_color_space; if (in_color_space != jpeg_color_space) { config.jparams.set_jpeg_colorspace = true; config.jparams.jpeg_color_space = jpeg_color_space; } config.max_bpp = jpeg_color_space == JCS_CMYK ? 4.0 : 3.6; config.max_dist = jpeg_color_space == JCS_CMYK ? 1.2 : 1.5; all_tests.push_back(config); } } { TestConfig config; config.input.color_space = JCS_YCbCr; config.max_bpp = 1.6; config.max_dist = 1.35; all_tests.push_back(config); } for (bool xyb : {false, true}) { TestConfig config; config.input.color_space = JCS_GRAYSCALE; config.jparams.xyb_mode = xyb; config.max_bpp = 1.35; config.max_dist = 1.4; all_tests.push_back(config); } for (int channels = 1; channels <= 4; ++channels) { TestConfig config; config.input.color_space = JCS_UNKNOWN; config.input.components = channels; config.max_bpp = 1.35 * channels; config.max_dist = 1.4; all_tests.push_back(config); } for (size_t r : {1, 3, 17, 1024}) { for (int progr : {0, 2}) { TestConfig config; config.jparams.restart_interval = r; config.jparams.progressive_mode = progr; config.max_bpp = 1.58 + 5.5 / r; config.max_dist = 2.2; all_tests.push_back(config); } } for (size_t rr : {1, 3, 8, 100}) { TestConfig config; config.jparams.restart_in_rows = rr; config.max_bpp = 1.6; config.max_dist = 2.2; all_tests.push_back(config); } for (int type : {0, 1, 10, 100, 10000}) { for (int scale : {1, 50, 100, 200, 500}) { for (bool add_raw : {false, true}) { for (bool baseline : {true, false}) { if (!baseline && (add_raw || type * scale < 25500)) continue; TestConfig config; config.input.xsize = 64; config.input.ysize = 64; CustomQuantTable table; table.table_type = type; table.scale_factor = scale; table.force_baseline = baseline; table.add_raw = add_raw; table.Generate(); config.jparams.optimize_coding = 1; config.jparams.quant_tables.push_back(table); config.jparams.quant_indexes = {0, 0, 0}; float q = (type == 0 ? 16 : type) * scale * 0.01f; if (baseline && !add_raw) q = std::max(1.0f, std::min(255.0f, q)); config.max_bpp = 1.5f + 25.0f / q; config.max_dist = 0.6f + 0.25f * q; all_tests.push_back(config); } } } } for (int qidx = 0; qidx < 8; ++qidx) { if (qidx == 3) continue; TestConfig config; config.input.xsize = 256; config.input.ysize = 256; config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, (qidx >> 0) & 1}; config.max_bpp = 2.25; config.max_dist = 2.8; all_tests.push_back(config); } for (int qidx = 0; qidx < 8; ++qidx) { for (int slot_idx = 0; slot_idx < 2; ++slot_idx) { if (qidx == 0 && slot_idx == 0) continue; TestConfig config; config.input.xsize = 256; config.input.ysize = 256; config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, (qidx >> 0) & 1}; CustomQuantTable table; table.slot_idx = slot_idx; table.Generate(); config.jparams.quant_tables.push_back(table); config.max_bpp = 2.3; config.max_dist = 2.9; all_tests.push_back(config); } } for (int qidx = 0; qidx < 8; ++qidx) { for (bool xyb : {false, true}) { TestConfig config; config.input.xsize = 256; config.input.ysize = 256; config.jparams.xyb_mode = xyb; config.jparams.quant_indexes = {(qidx >> 2) & 1, (qidx >> 1) & 1, (qidx >> 0) & 1}; { CustomQuantTable table; table.slot_idx = 0; table.Generate(); config.jparams.quant_tables.push_back(table); } { CustomQuantTable table; table.slot_idx = 1; table.table_type = 20; table.Generate(); config.jparams.quant_tables.push_back(table); } config.max_bpp = 2.0; config.max_dist = 3.85; all_tests.push_back(config); } } for (bool xyb : {false, true}) { TestConfig config; config.input.xsize = 256; config.input.ysize = 256; config.jparams.xyb_mode = xyb; config.jparams.quant_indexes = {0, 1, 2}; { CustomQuantTable table; table.slot_idx = 0; table.Generate(); config.jparams.quant_tables.push_back(table); } { CustomQuantTable table; table.slot_idx = 1; table.table_type = 20; table.Generate(); config.jparams.quant_tables.push_back(table); } { CustomQuantTable table; table.slot_idx = 2; table.table_type = 30; table.Generate(); config.jparams.quant_tables.push_back(table); } config.max_bpp = 1.5; config.max_dist = 3.75; all_tests.push_back(config); } { TestConfig config; config.jparams.comp_ids = {7, 17, 177}; config.input.xsize = config.input.ysize = 128; config.max_bpp = 2.25; config.max_dist = 2.4; all_tests.push_back(config); } for (int override_JFIF : {-1, 0, 1}) { for (int override_Adobe : {-1, 0, 1}) { if (override_JFIF == -1 && override_Adobe == -1) continue; TestConfig config; config.input.xsize = config.input.ysize = 128; config.jparams.override_JFIF = override_JFIF; config.jparams.override_Adobe = override_Adobe; config.max_bpp = 2.25; config.max_dist = 2.4; all_tests.push_back(config); } } { TestConfig config; config.input.xsize = config.input.ysize = 256; config.max_bpp = 1.85; config.max_dist = 2.05; config.jparams.add_marker = true; all_tests.push_back(config); } for (size_t icc_size : {728, 70000, 1000000}) { TestConfig config; config.input.xsize = config.input.ysize = 256; config.max_dist = 2.05; config.jparams.icc.resize(icc_size); for (size_t i = 0; i < icc_size; ++i) { config.jparams.icc[i] = (i * 17) & 0xff; } all_tests.push_back(config); } for (JpegIOMode input_mode : {PIXELS, RAW_DATA, COEFFICIENTS}) { TestConfig config; config.input.xsize = config.input.ysize = 256; config.input_mode = input_mode; if (input_mode == RAW_DATA) { config.input.color_space = JCS_YCbCr; } config.jparams.progressive_mode = 0; config.jparams.optimize_coding = 0; config.max_bpp = 1.85; config.max_dist = 2.05; if (input_mode == COEFFICIENTS) { config.max_bpp = 3.5; config.max_dist = 0.0; } all_tests.push_back(config); config.jparams.use_flat_dc_luma_code = true; all_tests.push_back(config); } for (int xsize : {640, 641, 648, 649}) { for (int ysize : {640, 641, 648, 649}) { for (int h_sampling : {1, 2}) { for (int v_sampling : {1, 2}) { if (h_sampling == 1 && v_sampling == 1) continue; for (int progr : {0, 2}) { TestConfig config; config.input.xsize = xsize; config.input.ysize = ysize; config.input.color_space = JCS_YCbCr; config.jparams.h_sampling = {h_sampling, 1, 1}; config.jparams.v_sampling = {v_sampling, 1, 1}; config.jparams.progressive_mode = progr; config.input_mode = RAW_DATA; config.max_bpp = 1.75; config.max_dist = 2.0; all_tests.push_back(config); config.input_mode = COEFFICIENTS; if (xsize & 1) { config.jparams.add_marker = true; } config.max_bpp = 24.0; all_tests.push_back(config); } } } } } for (JpegliDataType data_type : {JPEGLI_TYPE_UINT16, JPEGLI_TYPE_FLOAT}) { for (JpegliEndianness endianness : {JPEGLI_LITTLE_ENDIAN, JPEGLI_BIG_ENDIAN, JPEGLI_NATIVE_ENDIAN}) { J_COLOR_SPACE colorspace[4] = {JCS_GRAYSCALE, JCS_UNKNOWN, JCS_RGB, JCS_CMYK}; float max_bpp[4] = {1.32, 2.7, 1.6, 4.0}; for (int channels = 1; channels <= 4; ++channels) { TestConfig config; config.input.data_type = data_type; config.input.endianness = endianness; config.input.components = channels; config.input.color_space = colorspace[channels - 1]; config.max_bpp = max_bpp[channels - 1]; config.max_dist = 2.2; all_tests.push_back(config); } } } for (int smoothing : {1, 5, 50, 100}) { for (int h_sampling : {1, 2}) { for (int v_sampling : {1, 2}) { TestConfig config; config.input.xsize = 257; config.input.ysize = 265; config.jparams.smoothing_factor = smoothing; config.jparams.h_sampling = {h_sampling, 1, 1}; config.jparams.v_sampling = {v_sampling, 1, 1}; config.max_bpp = 1.85; config.max_dist = 3.05f; all_tests.push_back(config); } } } return all_tests; }; std::ostream& operator<<(std::ostream& os, const TestConfig& c) { os << c.input; os << c.jparams; if (c.input_mode == RAW_DATA) { os << "RawDataIn"; } else if (c.input_mode == COEFFICIENTS) { os << "WriteCoeffs"; } return os; } std::string TestDescription( const testing::TestParamInfo& info) { std::stringstream name; name << info.param; return name.str(); } JPEGLI_INSTANTIATE_TEST_SUITE_P(EncodeAPITest, EncodeAPITestParam, testing::ValuesIn(GenerateTests()), TestDescription); } // namespace } // namespace jpegli