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