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