test_utils-inl.h (15861B)
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 // This template file is included in both the libjpeg_test_util.cc and the 7 // test_utils.cc files with different JPEG_API_FN macros and possibly different 8 // include paths for the jpeg headers. 9 10 // Sequential non-interleaved. 11 constexpr jpeg_scan_info kScript1[] = { 12 {1, {0}, 0, 63, 0, 0}, 13 {1, {1}, 0, 63, 0, 0}, 14 {1, {2}, 0, 63, 0, 0}, 15 }; 16 // Sequential partially interleaved, chroma first. 17 constexpr jpeg_scan_info kScript2[] = { 18 {2, {1, 2}, 0, 63, 0, 0}, 19 {1, {0}, 0, 63, 0, 0}, 20 }; 21 22 // Rest of the scan scripts are progressive. 23 24 constexpr jpeg_scan_info kScript3[] = { 25 // Interleaved full DC. 26 {3, {0, 1, 2}, 0, 0, 0, 0}, 27 // Full AC scans. 28 {1, {0}, 1, 63, 0, 0}, 29 {1, {1}, 1, 63, 0, 0}, 30 {1, {2}, 1, 63, 0, 0}, 31 }; 32 constexpr jpeg_scan_info kScript4[] = { 33 // Non-interleaved full DC. 34 {1, {0}, 0, 0, 0, 0}, 35 {1, {1}, 0, 0, 0, 0}, 36 {1, {2}, 0, 0, 0, 0}, 37 // Full AC scans. 38 {1, {0}, 1, 63, 0, 0}, 39 {1, {1}, 1, 63, 0, 0}, 40 {1, {2}, 1, 63, 0, 0}, 41 }; 42 constexpr jpeg_scan_info kScript5[] = { 43 // Partially interleaved full DC, chroma first. 44 {2, {1, 2}, 0, 0, 0, 0}, 45 {1, {0}, 0, 0, 0, 0}, 46 // AC shifted by 1 bit. 47 {1, {0}, 1, 63, 0, 1}, 48 {1, {1}, 1, 63, 0, 1}, 49 {1, {2}, 1, 63, 0, 1}, 50 // AC refinement scan. 51 {1, {0}, 1, 63, 1, 0}, 52 {1, {1}, 1, 63, 1, 0}, 53 {1, {2}, 1, 63, 1, 0}, 54 }; 55 constexpr jpeg_scan_info kScript6[] = { 56 // Interleaved DC shifted by 2 bits. 57 {3, {0, 1, 2}, 0, 0, 0, 2}, 58 // Interleaved DC refinement scans. 59 {3, {0, 1, 2}, 0, 0, 2, 1}, 60 {3, {0, 1, 2}, 0, 0, 1, 0}, 61 // Full AC scans. 62 {1, {0}, 1, 63, 0, 0}, 63 {1, {1}, 1, 63, 0, 0}, 64 {1, {2}, 1, 63, 0, 0}, 65 }; 66 67 constexpr jpeg_scan_info kScript7[] = { 68 // Non-interleaved DC shifted by 2 bits. 69 {1, {0}, 0, 0, 0, 2}, 70 {1, {1}, 0, 0, 0, 2}, 71 {1, {2}, 0, 0, 0, 2}, 72 // Non-interleaved DC first refinement scans. 73 {1, {0}, 0, 0, 2, 1}, 74 {1, {1}, 0, 0, 2, 1}, 75 {1, {2}, 0, 0, 2, 1}, 76 // Non-interleaved DC second refinement scans. 77 {1, {0}, 0, 0, 1, 0}, 78 {1, {1}, 0, 0, 1, 0}, 79 {1, {2}, 0, 0, 1, 0}, 80 // Full AC scans. 81 {1, {0}, 1, 63, 0, 0}, 82 {1, {1}, 1, 63, 0, 0}, 83 {1, {2}, 1, 63, 0, 0}, 84 }; 85 86 constexpr jpeg_scan_info kScript8[] = { 87 // Partially interleaved DC shifted by 2 bits, chroma first 88 {2, {1, 2}, 0, 0, 0, 2}, 89 {1, {0}, 0, 0, 0, 2}, 90 // Partially interleaved DC first refinement scans. 91 {2, {0, 2}, 0, 0, 2, 1}, 92 {1, {1}, 0, 0, 2, 1}, 93 // Partially interleaved DC first refinement scans, chroma first. 94 {2, {1, 2}, 0, 0, 1, 0}, 95 {1, {0}, 0, 0, 1, 0}, 96 // Full AC scans. 97 {1, {0}, 1, 63, 0, 0}, 98 {1, {1}, 1, 63, 0, 0}, 99 {1, {2}, 1, 63, 0, 0}, 100 }; 101 102 constexpr jpeg_scan_info kScript9[] = { 103 // Interleaved full DC. 104 {3, {0, 1, 2}, 0, 0, 0, 0}, 105 // AC scans for component 0 106 // shifted by 1 bit, two spectral ranges 107 {1, {0}, 1, 6, 0, 1}, 108 {1, {0}, 7, 63, 0, 1}, 109 // refinement scan, full 110 {1, {0}, 1, 63, 1, 0}, 111 // AC scans for component 1 112 // shifted by 1 bit, full 113 {1, {1}, 1, 63, 0, 1}, 114 // refinement scan, two spectral ranges 115 {1, {1}, 1, 6, 1, 0}, 116 {1, {1}, 7, 63, 1, 0}, 117 // AC scans for component 2 118 // shifted by 1 bit, two spectral ranges 119 {1, {2}, 1, 6, 0, 1}, 120 {1, {2}, 7, 63, 0, 1}, 121 // refinement scan, two spectral ranges (but different from above) 122 {1, {2}, 1, 16, 1, 0}, 123 {1, {2}, 17, 63, 1, 0}, 124 }; 125 126 constexpr jpeg_scan_info kScript10[] = { 127 // Interleaved full DC. 128 {3, {0, 1, 2}, 0, 0, 0, 0}, 129 // AC scans for spectral range 1..16 130 // shifted by 1 131 {1, {0}, 1, 16, 0, 1}, 132 {1, {1}, 1, 16, 0, 1}, 133 {1, {2}, 1, 16, 0, 1}, 134 // refinement scans, two sub-ranges 135 {1, {0}, 1, 8, 1, 0}, 136 {1, {0}, 9, 16, 1, 0}, 137 {1, {1}, 1, 8, 1, 0}, 138 {1, {1}, 9, 16, 1, 0}, 139 {1, {2}, 1, 8, 1, 0}, 140 {1, {2}, 9, 16, 1, 0}, 141 // AC scans for spectral range 17..63 142 {1, {0}, 17, 63, 0, 1}, 143 {1, {1}, 17, 63, 0, 1}, 144 {1, {2}, 17, 63, 0, 1}, 145 // refinement scans, two sub-ranges 146 {1, {0}, 17, 28, 1, 0}, 147 {1, {0}, 29, 63, 1, 0}, 148 {1, {1}, 17, 28, 1, 0}, 149 {1, {1}, 29, 63, 1, 0}, 150 {1, {2}, 17, 28, 1, 0}, 151 {1, {2}, 29, 63, 1, 0}, 152 }; 153 154 struct ScanScript { 155 int num_scans; 156 const jpeg_scan_info* scans; 157 }; 158 159 constexpr ScanScript kTestScript[] = { 160 {ARRAY_SIZE(kScript1), kScript1}, {ARRAY_SIZE(kScript2), kScript2}, 161 {ARRAY_SIZE(kScript3), kScript3}, {ARRAY_SIZE(kScript4), kScript4}, 162 {ARRAY_SIZE(kScript5), kScript5}, {ARRAY_SIZE(kScript6), kScript6}, 163 {ARRAY_SIZE(kScript7), kScript7}, {ARRAY_SIZE(kScript8), kScript8}, 164 {ARRAY_SIZE(kScript9), kScript9}, {ARRAY_SIZE(kScript10), kScript10}, 165 }; 166 constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript); 167 168 void SetScanDecompressParams(const DecompressParams& dparams, 169 j_decompress_ptr cinfo, int scan_number) { 170 const ScanDecompressParams* sparams = nullptr; 171 for (const auto& sp : dparams.scan_params) { 172 if (scan_number <= sp.max_scan_number) { 173 sparams = &sp; 174 break; 175 } 176 } 177 if (sparams == nullptr) { 178 return; 179 } 180 if (dparams.quantize_colors) { 181 cinfo->dither_mode = static_cast<J_DITHER_MODE>(sparams->dither_mode); 182 if (sparams->color_quant_mode == CQUANT_1PASS) { 183 cinfo->two_pass_quantize = FALSE; 184 cinfo->colormap = nullptr; 185 } else if (sparams->color_quant_mode == CQUANT_2PASS) { 186 JXL_CHECK(cinfo->out_color_space == JCS_RGB); 187 cinfo->two_pass_quantize = TRUE; 188 cinfo->colormap = nullptr; 189 } else if (sparams->color_quant_mode == CQUANT_EXTERNAL) { 190 JXL_CHECK(cinfo->out_color_space == JCS_RGB); 191 cinfo->two_pass_quantize = FALSE; 192 bool have_colormap = cinfo->colormap != nullptr; 193 cinfo->actual_number_of_colors = kTestColorMapNumColors; 194 cinfo->colormap = (*cinfo->mem->alloc_sarray)( 195 reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, 196 cinfo->actual_number_of_colors, 3); 197 jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(cinfo->colormap), 198 3 * sizeof(JSAMPLE*)); 199 for (int i = 0; i < kTestColorMapNumColors; ++i) { 200 cinfo->colormap[0][i] = (kTestColorMap[i] >> 16) & 0xff; 201 cinfo->colormap[1][i] = (kTestColorMap[i] >> 8) & 0xff; 202 cinfo->colormap[2][i] = (kTestColorMap[i] >> 0) & 0xff; 203 } 204 if (have_colormap) { 205 JPEG_API_FN(new_colormap)(cinfo); 206 } 207 } else if (sparams->color_quant_mode == CQUANT_REUSE) { 208 JXL_CHECK(cinfo->out_color_space == JCS_RGB); 209 JXL_CHECK(cinfo->colormap); 210 } 211 } 212 } 213 214 void SetDecompressParams(const DecompressParams& dparams, 215 j_decompress_ptr cinfo) { 216 cinfo->do_block_smoothing = dparams.do_block_smoothing ? 1 : 0; 217 cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling ? 1 : 0; 218 if (dparams.output_mode == RAW_DATA) { 219 cinfo->raw_data_out = TRUE; 220 } 221 if (dparams.set_out_color_space) { 222 cinfo->out_color_space = 223 static_cast<J_COLOR_SPACE>(dparams.out_color_space); 224 if (dparams.out_color_space == JCS_UNKNOWN) { 225 cinfo->jpeg_color_space = JCS_UNKNOWN; 226 } 227 } 228 cinfo->scale_num = dparams.scale_num; 229 cinfo->scale_denom = dparams.scale_denom; 230 cinfo->quantize_colors = dparams.quantize_colors ? 1 : 0; 231 cinfo->desired_number_of_colors = dparams.desired_number_of_colors; 232 if (!dparams.scan_params.empty()) { 233 if (cinfo->buffered_image) { 234 for (const auto& sparams : dparams.scan_params) { 235 if (sparams.color_quant_mode == CQUANT_1PASS) { 236 cinfo->enable_1pass_quant = TRUE; 237 } else if (sparams.color_quant_mode == CQUANT_2PASS) { 238 cinfo->enable_2pass_quant = TRUE; 239 } else if (sparams.color_quant_mode == CQUANT_EXTERNAL) { 240 cinfo->enable_external_quant = TRUE; 241 } 242 } 243 SetScanDecompressParams(dparams, cinfo, 1); 244 } else { 245 SetScanDecompressParams(dparams, cinfo, kLastScan); 246 } 247 } 248 } 249 250 void CheckMarkerPresent(j_decompress_ptr cinfo, uint8_t marker_type) { 251 bool marker_found = false; 252 for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr; 253 marker = marker->next) { 254 jxl::msan::UnpoisonMemory(marker, sizeof(*marker)); 255 jxl::msan::UnpoisonMemory(marker->data, marker->data_length); 256 if (marker->marker == marker_type && 257 marker->data_length == sizeof(kMarkerData) && 258 memcmp(marker->data, kMarkerData, sizeof(kMarkerData)) == 0) { 259 marker_found = true; 260 } 261 } 262 JXL_CHECK(marker_found); 263 } 264 265 void VerifyHeader(const CompressParams& jparams, j_decompress_ptr cinfo) { 266 if (jparams.set_jpeg_colorspace) { 267 JXL_CHECK(cinfo->jpeg_color_space == jparams.jpeg_color_space); 268 } 269 if (jparams.override_JFIF >= 0) { 270 JXL_CHECK(cinfo->saw_JFIF_marker == jparams.override_JFIF); 271 } 272 if (jparams.override_Adobe >= 0) { 273 JXL_CHECK(cinfo->saw_Adobe_marker == jparams.override_Adobe); 274 } 275 if (jparams.add_marker) { 276 CheckMarkerPresent(cinfo, kSpecialMarker0); 277 CheckMarkerPresent(cinfo, kSpecialMarker1); 278 } 279 jxl::msan::UnpoisonMemory( 280 cinfo->comp_info, cinfo->num_components * sizeof(cinfo->comp_info[0])); 281 int max_h_samp_factor = 1; 282 int max_v_samp_factor = 1; 283 for (int i = 0; i < cinfo->num_components; ++i) { 284 jpeg_component_info* comp = &cinfo->comp_info[i]; 285 if (!jparams.comp_ids.empty()) { 286 JXL_CHECK(comp->component_id == jparams.comp_ids[i]); 287 } 288 if (!jparams.h_sampling.empty()) { 289 JXL_CHECK(comp->h_samp_factor == jparams.h_sampling[i]); 290 } 291 if (!jparams.v_sampling.empty()) { 292 JXL_CHECK(comp->v_samp_factor == jparams.v_sampling[i]); 293 } 294 if (!jparams.quant_indexes.empty()) { 295 JXL_CHECK(comp->quant_tbl_no == jparams.quant_indexes[i]); 296 } 297 max_h_samp_factor = std::max(max_h_samp_factor, comp->h_samp_factor); 298 max_v_samp_factor = std::max(max_v_samp_factor, comp->v_samp_factor); 299 } 300 JXL_CHECK(max_h_samp_factor == cinfo->max_h_samp_factor); 301 JXL_CHECK(max_v_samp_factor == cinfo->max_v_samp_factor); 302 int referenced_tables[NUM_QUANT_TBLS] = {}; 303 for (int i = 0; i < cinfo->num_components; ++i) { 304 jpeg_component_info* comp = &cinfo->comp_info[i]; 305 JXL_CHECK(comp->width_in_blocks == 306 DivCeil(cinfo->image_width * comp->h_samp_factor, 307 max_h_samp_factor * DCTSIZE)); 308 JXL_CHECK(comp->height_in_blocks == 309 DivCeil(cinfo->image_height * comp->v_samp_factor, 310 max_v_samp_factor * DCTSIZE)); 311 referenced_tables[comp->quant_tbl_no] = 1; 312 } 313 for (const auto& table : jparams.quant_tables) { 314 JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[table.slot_idx]; 315 if (!referenced_tables[table.slot_idx]) { 316 JXL_CHECK(quant_table == nullptr); 317 continue; 318 } 319 JXL_CHECK(quant_table != nullptr); 320 jxl::msan::UnpoisonMemory(quant_table, sizeof(*quant_table)); 321 for (int k = 0; k < DCTSIZE2; ++k) { 322 JXL_CHECK(quant_table->quantval[k] == table.quantval[k]); 323 } 324 } 325 } 326 327 void VerifyScanHeader(const CompressParams& jparams, j_decompress_ptr cinfo) { 328 JXL_CHECK(cinfo->input_scan_number > 0); 329 if (cinfo->progressive_mode) { 330 JXL_CHECK(cinfo->Ss != 0 || cinfo->Se != 63); 331 } else { 332 JXL_CHECK(cinfo->Ss == 0 && cinfo->Se == 63); 333 } 334 if (jparams.progressive_mode > 2) { 335 JXL_CHECK(jparams.progressive_mode < 3 + kNumTestScripts); 336 const ScanScript& script = kTestScript[jparams.progressive_mode - 3]; 337 JXL_CHECK(cinfo->input_scan_number <= script.num_scans); 338 const jpeg_scan_info& scan = script.scans[cinfo->input_scan_number - 1]; 339 JXL_CHECK(cinfo->comps_in_scan == scan.comps_in_scan); 340 for (int i = 0; i < cinfo->comps_in_scan; ++i) { 341 JXL_CHECK(cinfo->cur_comp_info[i]->component_index == 342 scan.component_index[i]); 343 } 344 JXL_CHECK(cinfo->Ss == scan.Ss); 345 JXL_CHECK(cinfo->Se == scan.Se); 346 JXL_CHECK(cinfo->Ah == scan.Ah); 347 JXL_CHECK(cinfo->Al == scan.Al); 348 } 349 if (jparams.restart_interval > 0) { 350 JXL_CHECK(cinfo->restart_interval == jparams.restart_interval); 351 } else if (jparams.restart_in_rows > 0) { 352 JXL_CHECK(cinfo->restart_interval == 353 jparams.restart_in_rows * cinfo->MCUs_per_row); 354 } 355 if (jparams.progressive_mode == 0 && jparams.optimize_coding == 0) { 356 if (cinfo->jpeg_color_space == JCS_RGB) { 357 JXL_CHECK(cinfo->comp_info[0].dc_tbl_no == 0); 358 JXL_CHECK(cinfo->comp_info[1].dc_tbl_no == 0); 359 JXL_CHECK(cinfo->comp_info[2].dc_tbl_no == 0); 360 JXL_CHECK(cinfo->comp_info[0].ac_tbl_no == 0); 361 JXL_CHECK(cinfo->comp_info[1].ac_tbl_no == 0); 362 JXL_CHECK(cinfo->comp_info[2].ac_tbl_no == 0); 363 } else if (cinfo->jpeg_color_space == JCS_YCbCr) { 364 JXL_CHECK(cinfo->comp_info[0].dc_tbl_no == 0); 365 JXL_CHECK(cinfo->comp_info[1].dc_tbl_no == 1); 366 JXL_CHECK(cinfo->comp_info[2].dc_tbl_no == 1); 367 JXL_CHECK(cinfo->comp_info[0].ac_tbl_no == 0); 368 JXL_CHECK(cinfo->comp_info[1].ac_tbl_no == 1); 369 JXL_CHECK(cinfo->comp_info[2].ac_tbl_no == 1); 370 } else if (cinfo->jpeg_color_space == JCS_CMYK) { 371 JXL_CHECK(cinfo->comp_info[0].dc_tbl_no == 0); 372 JXL_CHECK(cinfo->comp_info[1].dc_tbl_no == 0); 373 JXL_CHECK(cinfo->comp_info[2].dc_tbl_no == 0); 374 JXL_CHECK(cinfo->comp_info[3].dc_tbl_no == 0); 375 JXL_CHECK(cinfo->comp_info[0].ac_tbl_no == 0); 376 JXL_CHECK(cinfo->comp_info[1].ac_tbl_no == 0); 377 JXL_CHECK(cinfo->comp_info[2].ac_tbl_no == 0); 378 JXL_CHECK(cinfo->comp_info[3].ac_tbl_no == 0); 379 } else if (cinfo->jpeg_color_space == JCS_YCCK) { 380 JXL_CHECK(cinfo->comp_info[0].dc_tbl_no == 0); 381 JXL_CHECK(cinfo->comp_info[1].dc_tbl_no == 1); 382 JXL_CHECK(cinfo->comp_info[2].dc_tbl_no == 1); 383 JXL_CHECK(cinfo->comp_info[3].dc_tbl_no == 0); 384 JXL_CHECK(cinfo->comp_info[0].ac_tbl_no == 0); 385 JXL_CHECK(cinfo->comp_info[1].ac_tbl_no == 1); 386 JXL_CHECK(cinfo->comp_info[2].ac_tbl_no == 1); 387 JXL_CHECK(cinfo->comp_info[3].ac_tbl_no == 0); 388 } 389 if (jparams.use_flat_dc_luma_code) { 390 JHUFF_TBL* tbl = cinfo->dc_huff_tbl_ptrs[0]; 391 jxl::msan::UnpoisonMemory(tbl, sizeof(*tbl)); 392 for (int i = 0; i < 15; ++i) { 393 JXL_CHECK(tbl->huffval[i] == i); 394 } 395 } 396 } 397 } 398 399 void UnmapColors(uint8_t* row, size_t xsize, int components, 400 JSAMPARRAY colormap, size_t num_colors) { 401 JXL_CHECK(colormap != nullptr); 402 std::vector<uint8_t> tmp(xsize * components); 403 for (size_t x = 0; x < xsize; ++x) { 404 JXL_CHECK(row[x] < num_colors); 405 for (int c = 0; c < components; ++c) { 406 tmp[x * components + c] = colormap[c][row[x]]; 407 } 408 } 409 memcpy(row, tmp.data(), tmp.size()); 410 } 411 412 void CopyCoefficients(j_decompress_ptr cinfo, jvirt_barray_ptr* coef_arrays, 413 TestImage* output) { 414 output->xsize = cinfo->image_width; 415 output->ysize = cinfo->image_height; 416 output->components = cinfo->num_components; 417 output->color_space = cinfo->out_color_space; 418 j_common_ptr comptr = reinterpret_cast<j_common_ptr>(cinfo); 419 for (int c = 0; c < cinfo->num_components; ++c) { 420 jpeg_component_info* comp = &cinfo->comp_info[c]; 421 std::vector<JCOEF> coeffs(comp->width_in_blocks * comp->height_in_blocks * 422 DCTSIZE2); 423 for (size_t by = 0; by < comp->height_in_blocks; ++by) { 424 JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(comptr, coef_arrays[c], 425 by, 1, TRUE); 426 size_t stride = comp->width_in_blocks * sizeof(JBLOCK); 427 size_t offset = by * comp->width_in_blocks * DCTSIZE2; 428 memcpy(&coeffs[offset], ba[0], stride); 429 } 430 output->coeffs.emplace_back(std::move(coeffs)); 431 } 432 }