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 #include "lib/jpegli/encode.h"
7 #include "lib/jpegli/test_utils.h"
8 #include "lib/jpegli/testing.h"
13 static constexpr size_t kInitialBufferSize
= 1024;
14 static constexpr size_t kFinalBufferSize
= 18;
16 struct DestinationManager
{
17 jpeg_destination_mgr pub
;
18 std::vector
<uint8_t> buffer
;
20 DestinationManager() {
21 pub
.init_destination
= init_destination
;
22 pub
.empty_output_buffer
= empty_output_buffer
;
23 pub
.term_destination
= term_destination
;
27 pub
.next_output_byte
= buffer
.data();
28 pub
.free_in_buffer
= buffer
.size();
31 void EmptyTo(std::vector
<uint8_t>* output
, size_t new_size
= 0) {
32 output
->insert(output
->end(), buffer
.data(), pub
.next_output_byte
);
34 buffer
.resize(new_size
);
39 static void init_destination(j_compress_ptr cinfo
) {
40 auto us
= reinterpret_cast<DestinationManager
*>(cinfo
->dest
);
41 us
->buffer
.resize(kInitialBufferSize
);
45 static boolean
empty_output_buffer(j_compress_ptr cinfo
) { return FALSE
; }
47 static void term_destination(j_compress_ptr cinfo
) {}
52 CompressParams jparams
;
54 size_t lines_batch_size
;
57 class OutputSuspensionTestParam
: public ::testing::TestWithParam
<TestConfig
> {
60 TEST_P(OutputSuspensionTestParam
, PixelData
) {
61 jpeg_compress_struct cinfo
= {};
62 TestConfig config
= GetParam();
63 TestImage
& input
= config
.input
;
64 GeneratePixels(&input
);
65 DestinationManager dest
;
66 std::vector
<uint8_t> compressed
;
67 const auto try_catch_block
= [&]() -> bool {
68 ERROR_HANDLER_SETUP(jpegli
);
69 jpegli_create_compress(&cinfo
);
70 cinfo
.dest
= reinterpret_cast<jpeg_destination_mgr
*>(&dest
);
72 cinfo
.image_width
= input
.xsize
;
73 cinfo
.image_height
= input
.ysize
;
74 cinfo
.input_components
= input
.components
;
75 cinfo
.in_color_space
= JCS_RGB
;
76 jpegli_set_defaults(&cinfo
);
77 cinfo
.comp_info
[0].v_samp_factor
= config
.jparams
.v_sampling
[0];
78 jpegli_set_progressive_level(&cinfo
, 0);
79 cinfo
.optimize_coding
= FALSE
;
80 jpegli_start_compress(&cinfo
, TRUE
);
82 size_t stride
= cinfo
.image_width
* cinfo
.input_components
;
83 std::vector
<uint8_t> row_bytes(config
.lines_batch_size
* stride
);
84 while (cinfo
.next_scanline
< cinfo
.image_height
) {
85 size_t lines_left
= cinfo
.image_height
- cinfo
.next_scanline
;
86 size_t num_lines
= std::min(config
.lines_batch_size
, lines_left
);
87 memcpy(&row_bytes
[0], &input
.pixels
[cinfo
.next_scanline
* stride
],
89 std::vector
<JSAMPROW
> rows(num_lines
);
90 for (size_t i
= 0; i
< num_lines
; ++i
) {
91 rows
[i
] = &row_bytes
[i
* stride
];
93 size_t lines_done
= 0;
94 while (lines_done
< num_lines
) {
95 lines_done
+= jpegli_write_scanlines(&cinfo
, &rows
[lines_done
],
96 num_lines
- lines_done
);
97 if (lines_done
< num_lines
) {
98 dest
.EmptyTo(&compressed
, config
.buffer_size
);
102 dest
.EmptyTo(&compressed
, kFinalBufferSize
);
103 jpegli_finish_compress(&cinfo
);
104 dest
.EmptyTo(&compressed
);
107 ASSERT_TRUE(try_catch_block());
108 jpegli_destroy_compress(&cinfo
);
110 DecodeWithLibjpeg(CompressParams(), DecompressParams(), compressed
, &output
);
111 VerifyOutputImage(input
, output
, 2.5);
114 TEST_P(OutputSuspensionTestParam
, RawData
) {
115 jpeg_compress_struct cinfo
= {};
116 TestConfig config
= GetParam();
117 if (config
.lines_batch_size
!= 1) return;
118 TestImage
& input
= config
.input
;
119 input
.color_space
= JCS_YCbCr
;
120 GeneratePixels(&input
);
121 GenerateRawData(config
.jparams
, &input
);
122 DestinationManager dest
;
123 std::vector
<uint8_t> compressed
;
124 const auto try_catch_block
= [&]() -> bool {
125 ERROR_HANDLER_SETUP(jpegli
);
126 jpegli_create_compress(&cinfo
);
127 cinfo
.dest
= reinterpret_cast<jpeg_destination_mgr
*>(&dest
);
128 cinfo
.image_width
= input
.xsize
;
129 cinfo
.image_height
= input
.ysize
;
130 cinfo
.input_components
= input
.components
;
131 cinfo
.in_color_space
= JCS_YCbCr
;
132 jpegli_set_defaults(&cinfo
);
133 cinfo
.comp_info
[0].v_samp_factor
= config
.jparams
.v_sampling
[0];
134 jpegli_set_progressive_level(&cinfo
, 0);
135 cinfo
.optimize_coding
= FALSE
;
136 cinfo
.raw_data_in
= TRUE
;
137 jpegli_start_compress(&cinfo
, TRUE
);
139 std::vector
<std::vector
<uint8_t>> raw_data
= input
.raw_data
;
140 size_t max_lines
= config
.jparams
.max_v_sample() * DCTSIZE
;
141 std::vector
<std::vector
<JSAMPROW
>> rowdata(cinfo
.num_components
);
142 std::vector
<JSAMPARRAY
> data(cinfo
.num_components
);
143 for (int c
= 0; c
< cinfo
.num_components
; ++c
) {
144 rowdata
[c
].resize(config
.jparams
.v_samp(c
) * DCTSIZE
);
145 data
[c
] = &rowdata
[c
][0];
147 while (cinfo
.next_scanline
< cinfo
.image_height
) {
148 for (int c
= 0; c
< cinfo
.num_components
; ++c
) {
149 size_t cwidth
= cinfo
.comp_info
[c
].width_in_blocks
* DCTSIZE
;
150 size_t cheight
= cinfo
.comp_info
[c
].height_in_blocks
* DCTSIZE
;
151 size_t num_lines
= config
.jparams
.v_samp(c
) * DCTSIZE
;
152 size_t y0
= (cinfo
.next_scanline
/ max_lines
) * num_lines
;
153 for (size_t i
= 0; i
< num_lines
; ++i
) {
155 (y0
+ i
< cheight
? &raw_data
[c
][(y0
+ i
) * cwidth
] : nullptr);
158 while (jpegli_write_raw_data(&cinfo
, &data
[0], max_lines
) == 0) {
159 dest
.EmptyTo(&compressed
, config
.buffer_size
);
162 dest
.EmptyTo(&compressed
, kFinalBufferSize
);
163 jpegli_finish_compress(&cinfo
);
164 dest
.EmptyTo(&compressed
);
168 jpegli_destroy_compress(&cinfo
);
169 DecompressParams dparams
;
170 dparams
.output_mode
= RAW_DATA
;
172 DecodeWithLibjpeg(CompressParams(), dparams
, compressed
, &output
);
173 VerifyOutputImage(input
, output
, 3.5);
176 std::vector
<TestConfig
> GenerateTests() {
177 std::vector
<TestConfig
> all_tests
;
178 const size_t xsize0
= 1920;
179 const size_t ysize0
= 1080;
180 for (int dysize
: {0, 1, 8, 9}) {
181 for (int v_sampling
: {1, 2}) {
182 for (int nlines
: {1, 8, 117}) {
183 for (int bufsize
: {1, 16, 16 << 10}) {
185 config
.lines_batch_size
= nlines
;
186 config
.buffer_size
= bufsize
;
187 config
.input
.xsize
= xsize0
;
188 config
.input
.ysize
= ysize0
+ dysize
;
189 config
.jparams
.h_sampling
= {1, 1, 1};
190 config
.jparams
.v_sampling
= {v_sampling
, 1, 1};
191 all_tests
.push_back(config
);
199 std::ostream
& operator<<(std::ostream
& os
, const TestConfig
& c
) {
202 os
<< "Lines" << c
.lines_batch_size
;
203 os
<< "BufSize" << c
.buffer_size
;
207 std::string
TestDescription(
208 const testing::TestParamInfo
<OutputSuspensionTestParam::ParamType
>& info
) {
209 std::stringstream name
;
214 JPEGLI_INSTANTIATE_TEST_SUITE_P(OutputSuspensionTest
, OutputSuspensionTestParam
,
215 testing::ValuesIn(GenerateTests()),
219 } // namespace jpegli