1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style
4 // license that can be found in the LICENSE file.
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.
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},
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},
22 // Rest of the scan scripts are progressive.
24 constexpr jpeg_scan_info kScript3
[] = {
25 // Interleaved full DC.
26 {3, {0, 1, 2}, 0, 0, 0, 0},
28 {1, {0}, 1, 63, 0, 0},
29 {1, {1}, 1, 63, 0, 0},
30 {1, {2}, 1, 63, 0, 0},
32 constexpr jpeg_scan_info kScript4
[] = {
33 // Non-interleaved full DC.
38 {1, {0}, 1, 63, 0, 0},
39 {1, {1}, 1, 63, 0, 0},
40 {1, {2}, 1, 63, 0, 0},
42 constexpr jpeg_scan_info kScript5
[] = {
43 // Partially interleaved full DC, chroma first.
44 {2, {1, 2}, 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},
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},
62 {1, {0}, 1, 63, 0, 0},
63 {1, {1}, 1, 63, 0, 0},
64 {1, {2}, 1, 63, 0, 0},
67 constexpr jpeg_scan_info kScript7
[] = {
68 // Non-interleaved DC shifted by 2 bits.
72 // Non-interleaved DC first refinement scans.
76 // Non-interleaved DC second refinement scans.
81 {1, {0}, 1, 63, 0, 0},
82 {1, {1}, 1, 63, 0, 0},
83 {1, {2}, 1, 63, 0, 0},
86 constexpr jpeg_scan_info kScript8
[] = {
87 // Partially interleaved DC shifted by 2 bits, chroma first
88 {2, {1, 2}, 0, 0, 0, 2},
90 // Partially interleaved DC first refinement scans.
91 {2, {0, 2}, 0, 0, 2, 1},
93 // Partially interleaved DC first refinement scans, chroma first.
94 {2, {1, 2}, 0, 0, 1, 0},
97 {1, {0}, 1, 63, 0, 0},
98 {1, {1}, 1, 63, 0, 0},
99 {1, {2}, 1, 63, 0, 0},
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},
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
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},
156 const jpeg_scan_info
* scans
;
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
},
166 constexpr int kNumTestScripts
= ARRAY_SIZE(kTestScript
);
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
) {
177 if (sparams
== nullptr) {
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;
205 JPEG_API_FN(new_colormap
)(cinfo
);
207 } else if (sparams
->color_quant_mode
== CQUANT_REUSE
) {
208 JXL_CHECK(cinfo
->out_color_space
== JCS_RGB
);
209 JXL_CHECK(cinfo
->colormap
);
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
;
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
;
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
;
243 SetScanDecompressParams(dparams
, cinfo
, 1);
245 SetScanDecompressParams(dparams
, cinfo
, kLastScan
);
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) {
262 JXL_CHECK(marker_found
);
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
);
269 if (jparams
.override_JFIF
>= 0) {
270 JXL_CHECK(cinfo
->saw_JFIF_marker
== jparams
.override_JFIF
);
272 if (jparams
.override_Adobe
>= 0) {
273 JXL_CHECK(cinfo
->saw_Adobe_marker
== jparams
.override_Adobe
);
275 if (jparams
.add_marker
) {
276 CheckMarkerPresent(cinfo
, kSpecialMarker0
);
277 CheckMarkerPresent(cinfo
, kSpecialMarker1
);
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
]);
288 if (!jparams
.h_sampling
.empty()) {
289 JXL_CHECK(comp
->h_samp_factor
== jparams
.h_sampling
[i
]);
291 if (!jparams
.v_sampling
.empty()) {
292 JXL_CHECK(comp
->v_samp_factor
== jparams
.v_sampling
[i
]);
294 if (!jparams
.quant_indexes
.empty()) {
295 JXL_CHECK(comp
->quant_tbl_no
== jparams
.quant_indexes
[i
]);
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
);
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;
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);
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
]);
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);
332 JXL_CHECK(cinfo
->Ss
== 0 && cinfo
->Se
== 63);
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
]);
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
);
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
);
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);
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
);
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
]];
409 memcpy(row
, tmp
.data(), tmp
.size());
412 void CopyCoefficients(j_decompress_ptr cinfo
, jvirt_barray_ptr
* coef_arrays
,
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
*
423 for (size_t by
= 0; by
< comp
->height_in_blocks
; ++by
) {
424 JBLOCKARRAY ba
= (*cinfo
->mem
->access_virt_barray
)(comptr
, coef_arrays
[c
],
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
);
430 output
->coeffs
.emplace_back(std::move(coeffs
));