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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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
= (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(cinfo
->colormap
, 3 * sizeof(JSAMPROW
));
198 for (int i
= 0; i
< kTestColorMapNumColors
; ++i
) {
199 cinfo
->colormap
[0][i
] = (kTestColorMap
[i
] >> 16) & 0xff;
200 cinfo
->colormap
[1][i
] = (kTestColorMap
[i
] >> 8) & 0xff;
201 cinfo
->colormap
[2][i
] = (kTestColorMap
[i
] >> 0) & 0xff;
204 JPEG_API_FN(new_colormap
)(cinfo
);
206 } else if (sparams
->color_quant_mode
== CQUANT_REUSE
) {
207 JXL_CHECK(cinfo
->out_color_space
== JCS_RGB
);
208 JXL_CHECK(cinfo
->colormap
);
213 void SetDecompressParams(const DecompressParams
& dparams
,
214 j_decompress_ptr cinfo
) {
215 cinfo
->do_block_smoothing
= dparams
.do_block_smoothing
;
216 cinfo
->do_fancy_upsampling
= dparams
.do_fancy_upsampling
;
217 if (dparams
.output_mode
== RAW_DATA
) {
218 cinfo
->raw_data_out
= TRUE
;
220 if (dparams
.set_out_color_space
) {
221 cinfo
->out_color_space
= (J_COLOR_SPACE
)dparams
.out_color_space
;
222 if (dparams
.out_color_space
== JCS_UNKNOWN
) {
223 cinfo
->jpeg_color_space
= JCS_UNKNOWN
;
226 cinfo
->scale_num
= dparams
.scale_num
;
227 cinfo
->scale_denom
= dparams
.scale_denom
;
228 cinfo
->quantize_colors
= dparams
.quantize_colors
;
229 cinfo
->desired_number_of_colors
= dparams
.desired_number_of_colors
;
230 if (!dparams
.scan_params
.empty()) {
231 if (cinfo
->buffered_image
) {
232 for (const auto& sparams
: dparams
.scan_params
) {
233 if (sparams
.color_quant_mode
== CQUANT_1PASS
) {
234 cinfo
->enable_1pass_quant
= TRUE
;
235 } else if (sparams
.color_quant_mode
== CQUANT_2PASS
) {
236 cinfo
->enable_2pass_quant
= TRUE
;
237 } else if (sparams
.color_quant_mode
== CQUANT_EXTERNAL
) {
238 cinfo
->enable_external_quant
= TRUE
;
241 SetScanDecompressParams(dparams
, cinfo
, 1);
243 SetScanDecompressParams(dparams
, cinfo
, kLastScan
);
248 void CheckMarkerPresent(j_decompress_ptr cinfo
, uint8_t marker_type
) {
249 bool marker_found
= false;
250 for (jpeg_saved_marker_ptr marker
= cinfo
->marker_list
; marker
!= nullptr;
251 marker
= marker
->next
) {
252 jxl::msan::UnpoisonMemory(marker
, sizeof(*marker
));
253 jxl::msan::UnpoisonMemory(marker
->data
, marker
->data_length
);
254 if (marker
->marker
== marker_type
&&
255 marker
->data_length
== sizeof(kMarkerData
) &&
256 memcmp(marker
->data
, kMarkerData
, sizeof(kMarkerData
)) == 0) {
260 JXL_CHECK(marker_found
);
263 void VerifyHeader(const CompressParams
& jparams
, j_decompress_ptr cinfo
) {
264 if (jparams
.set_jpeg_colorspace
) {
265 JXL_CHECK(cinfo
->jpeg_color_space
== jparams
.jpeg_color_space
);
267 if (jparams
.override_JFIF
>= 0) {
268 JXL_CHECK(cinfo
->saw_JFIF_marker
== jparams
.override_JFIF
);
270 if (jparams
.override_Adobe
>= 0) {
271 JXL_CHECK(cinfo
->saw_Adobe_marker
== jparams
.override_Adobe
);
273 if (jparams
.add_marker
) {
274 CheckMarkerPresent(cinfo
, kSpecialMarker0
);
275 CheckMarkerPresent(cinfo
, kSpecialMarker1
);
277 jxl::msan::UnpoisonMemory(
278 cinfo
->comp_info
, cinfo
->num_components
* sizeof(cinfo
->comp_info
[0]));
279 int max_h_samp_factor
= 1;
280 int max_v_samp_factor
= 1;
281 for (int i
= 0; i
< cinfo
->num_components
; ++i
) {
282 jpeg_component_info
* comp
= &cinfo
->comp_info
[i
];
283 if (!jparams
.comp_ids
.empty()) {
284 JXL_CHECK(comp
->component_id
== jparams
.comp_ids
[i
]);
286 if (!jparams
.h_sampling
.empty()) {
287 JXL_CHECK(comp
->h_samp_factor
== jparams
.h_sampling
[i
]);
289 if (!jparams
.v_sampling
.empty()) {
290 JXL_CHECK(comp
->v_samp_factor
== jparams
.v_sampling
[i
]);
292 if (!jparams
.quant_indexes
.empty()) {
293 JXL_CHECK(comp
->quant_tbl_no
== jparams
.quant_indexes
[i
]);
295 max_h_samp_factor
= std::max(max_h_samp_factor
, comp
->h_samp_factor
);
296 max_v_samp_factor
= std::max(max_v_samp_factor
, comp
->v_samp_factor
);
298 JXL_CHECK(max_h_samp_factor
== cinfo
->max_h_samp_factor
);
299 JXL_CHECK(max_v_samp_factor
== cinfo
->max_v_samp_factor
);
300 int referenced_tables
[NUM_QUANT_TBLS
] = {};
301 for (int i
= 0; i
< cinfo
->num_components
; ++i
) {
302 jpeg_component_info
* comp
= &cinfo
->comp_info
[i
];
303 JXL_CHECK(comp
->width_in_blocks
==
304 DivCeil(cinfo
->image_width
* comp
->h_samp_factor
,
305 max_h_samp_factor
* DCTSIZE
));
306 JXL_CHECK(comp
->height_in_blocks
==
307 DivCeil(cinfo
->image_height
* comp
->v_samp_factor
,
308 max_v_samp_factor
* DCTSIZE
));
309 referenced_tables
[comp
->quant_tbl_no
] = 1;
311 for (const auto& table
: jparams
.quant_tables
) {
312 JQUANT_TBL
* quant_table
= cinfo
->quant_tbl_ptrs
[table
.slot_idx
];
313 if (!referenced_tables
[table
.slot_idx
]) {
314 JXL_CHECK(quant_table
== nullptr);
317 JXL_CHECK(quant_table
!= nullptr);
318 jxl::msan::UnpoisonMemory(quant_table
, sizeof(*quant_table
));
319 for (int k
= 0; k
< DCTSIZE2
; ++k
) {
320 JXL_CHECK(quant_table
->quantval
[k
] == table
.quantval
[k
]);
325 void VerifyScanHeader(const CompressParams
& jparams
, j_decompress_ptr cinfo
) {
326 JXL_CHECK(cinfo
->input_scan_number
> 0);
327 if (cinfo
->progressive_mode
) {
328 JXL_CHECK(cinfo
->Ss
!= 0 || cinfo
->Se
!= 63);
330 JXL_CHECK(cinfo
->Ss
== 0 && cinfo
->Se
== 63);
332 if (jparams
.progressive_mode
> 2) {
333 JXL_CHECK(jparams
.progressive_mode
< 3 + kNumTestScripts
);
334 const ScanScript
& script
= kTestScript
[jparams
.progressive_mode
- 3];
335 JXL_CHECK(cinfo
->input_scan_number
<= script
.num_scans
);
336 const jpeg_scan_info
& scan
= script
.scans
[cinfo
->input_scan_number
- 1];
337 JXL_CHECK(cinfo
->comps_in_scan
== scan
.comps_in_scan
);
338 for (int i
= 0; i
< cinfo
->comps_in_scan
; ++i
) {
339 JXL_CHECK(cinfo
->cur_comp_info
[i
]->component_index
==
340 scan
.component_index
[i
]);
342 JXL_CHECK(cinfo
->Ss
== scan
.Ss
);
343 JXL_CHECK(cinfo
->Se
== scan
.Se
);
344 JXL_CHECK(cinfo
->Ah
== scan
.Ah
);
345 JXL_CHECK(cinfo
->Al
== scan
.Al
);
347 if (jparams
.restart_interval
> 0) {
348 JXL_CHECK(cinfo
->restart_interval
== jparams
.restart_interval
);
349 } else if (jparams
.restart_in_rows
> 0) {
350 JXL_CHECK(cinfo
->restart_interval
==
351 jparams
.restart_in_rows
* cinfo
->MCUs_per_row
);
353 if (jparams
.progressive_mode
== 0 && jparams
.optimize_coding
== 0) {
354 if (cinfo
->jpeg_color_space
== JCS_RGB
) {
355 JXL_CHECK(cinfo
->comp_info
[0].dc_tbl_no
== 0);
356 JXL_CHECK(cinfo
->comp_info
[1].dc_tbl_no
== 0);
357 JXL_CHECK(cinfo
->comp_info
[2].dc_tbl_no
== 0);
358 JXL_CHECK(cinfo
->comp_info
[0].ac_tbl_no
== 0);
359 JXL_CHECK(cinfo
->comp_info
[1].ac_tbl_no
== 0);
360 JXL_CHECK(cinfo
->comp_info
[2].ac_tbl_no
== 0);
361 } else if (cinfo
->jpeg_color_space
== JCS_YCbCr
) {
362 JXL_CHECK(cinfo
->comp_info
[0].dc_tbl_no
== 0);
363 JXL_CHECK(cinfo
->comp_info
[1].dc_tbl_no
== 1);
364 JXL_CHECK(cinfo
->comp_info
[2].dc_tbl_no
== 1);
365 JXL_CHECK(cinfo
->comp_info
[0].ac_tbl_no
== 0);
366 JXL_CHECK(cinfo
->comp_info
[1].ac_tbl_no
== 1);
367 JXL_CHECK(cinfo
->comp_info
[2].ac_tbl_no
== 1);
368 } else if (cinfo
->jpeg_color_space
== JCS_CMYK
) {
369 JXL_CHECK(cinfo
->comp_info
[0].dc_tbl_no
== 0);
370 JXL_CHECK(cinfo
->comp_info
[1].dc_tbl_no
== 0);
371 JXL_CHECK(cinfo
->comp_info
[2].dc_tbl_no
== 0);
372 JXL_CHECK(cinfo
->comp_info
[3].dc_tbl_no
== 0);
373 JXL_CHECK(cinfo
->comp_info
[0].ac_tbl_no
== 0);
374 JXL_CHECK(cinfo
->comp_info
[1].ac_tbl_no
== 0);
375 JXL_CHECK(cinfo
->comp_info
[2].ac_tbl_no
== 0);
376 JXL_CHECK(cinfo
->comp_info
[3].ac_tbl_no
== 0);
377 } else if (cinfo
->jpeg_color_space
== JCS_YCCK
) {
378 JXL_CHECK(cinfo
->comp_info
[0].dc_tbl_no
== 0);
379 JXL_CHECK(cinfo
->comp_info
[1].dc_tbl_no
== 1);
380 JXL_CHECK(cinfo
->comp_info
[2].dc_tbl_no
== 1);
381 JXL_CHECK(cinfo
->comp_info
[3].dc_tbl_no
== 0);
382 JXL_CHECK(cinfo
->comp_info
[0].ac_tbl_no
== 0);
383 JXL_CHECK(cinfo
->comp_info
[1].ac_tbl_no
== 1);
384 JXL_CHECK(cinfo
->comp_info
[2].ac_tbl_no
== 1);
385 JXL_CHECK(cinfo
->comp_info
[3].ac_tbl_no
== 0);
387 if (jparams
.use_flat_dc_luma_code
) {
388 JHUFF_TBL
* tbl
= cinfo
->dc_huff_tbl_ptrs
[0];
389 jxl::msan::UnpoisonMemory(tbl
, sizeof(*tbl
));
390 for (int i
= 0; i
< 15; ++i
) {
391 JXL_CHECK(tbl
->huffval
[i
] == i
);
397 void UnmapColors(uint8_t* row
, size_t xsize
, int components
,
398 JSAMPARRAY colormap
, size_t num_colors
) {
399 JXL_CHECK(colormap
!= nullptr);
400 std::vector
<uint8_t> tmp(xsize
* components
);
401 for (size_t x
= 0; x
< xsize
; ++x
) {
402 JXL_CHECK(row
[x
] < num_colors
);
403 for (int c
= 0; c
< components
; ++c
) {
404 tmp
[x
* components
+ c
] = colormap
[c
][row
[x
]];
407 memcpy(row
, tmp
.data(), tmp
.size());
410 void CopyCoefficients(j_decompress_ptr cinfo
, jvirt_barray_ptr
* coef_arrays
,
412 output
->xsize
= cinfo
->image_width
;
413 output
->ysize
= cinfo
->image_height
;
414 output
->components
= cinfo
->num_components
;
415 output
->color_space
= cinfo
->out_color_space
;
416 j_common_ptr comptr
= reinterpret_cast<j_common_ptr
>(cinfo
);
417 for (int c
= 0; c
< cinfo
->num_components
; ++c
) {
418 jpeg_component_info
* comp
= &cinfo
->comp_info
[c
];
419 std::vector
<JCOEF
> coeffs(comp
->width_in_blocks
* comp
->height_in_blocks
*
421 for (size_t by
= 0; by
< comp
->height_in_blocks
; ++by
) {
422 JBLOCKARRAY ba
= (*cinfo
->mem
->access_virt_barray
)(comptr
, coef_arrays
[c
],
424 size_t stride
= comp
->width_in_blocks
* sizeof(JBLOCK
);
425 size_t offset
= by
* comp
->width_in_blocks
* DCTSIZE2
;
426 memcpy(&coeffs
[offset
], ba
[0], stride
);
428 output
->coeffs
.emplace_back(std::move(coeffs
));