Imported Upstream version 0.9.0
[platform/upstream/libjxl.git] / lib / jpegli / output_suspension_test.cc
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 "lib/jpegli/encode.h"
7 #include "lib/jpegli/test_utils.h"
8 #include "lib/jpegli/testing.h"
9
10 namespace jpegli {
11 namespace {
12
13 static constexpr size_t kInitialBufferSize = 1024;
14 static constexpr size_t kFinalBufferSize = 18;
15
16 struct DestinationManager {
17   jpeg_destination_mgr pub;
18   std::vector<uint8_t> buffer;
19
20   DestinationManager() {
21     pub.init_destination = init_destination;
22     pub.empty_output_buffer = empty_output_buffer;
23     pub.term_destination = term_destination;
24   }
25
26   void Rewind() {
27     pub.next_output_byte = buffer.data();
28     pub.free_in_buffer = buffer.size();
29   }
30
31   void EmptyTo(std::vector<uint8_t>* output, size_t new_size = 0) {
32     output->insert(output->end(), buffer.data(), pub.next_output_byte);
33     if (new_size > 0) {
34       buffer.resize(new_size);
35     }
36     Rewind();
37   }
38
39   static void init_destination(j_compress_ptr cinfo) {
40     auto us = reinterpret_cast<DestinationManager*>(cinfo->dest);
41     us->buffer.resize(kInitialBufferSize);
42     us->Rewind();
43   }
44
45   static boolean empty_output_buffer(j_compress_ptr cinfo) { return FALSE; }
46
47   static void term_destination(j_compress_ptr cinfo) {}
48 };
49
50 struct TestConfig {
51   TestImage input;
52   CompressParams jparams;
53   size_t buffer_size;
54   size_t lines_batch_size;
55 };
56
57 class OutputSuspensionTestParam : public ::testing::TestWithParam<TestConfig> {
58 };
59
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);
71
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);
81
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],
88              num_lines * 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];
92       }
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);
99         }
100       }
101     }
102     dest.EmptyTo(&compressed, kFinalBufferSize);
103     jpegli_finish_compress(&cinfo);
104     dest.EmptyTo(&compressed);
105     return true;
106   };
107   ASSERT_TRUE(try_catch_block());
108   jpegli_destroy_compress(&cinfo);
109   TestImage output;
110   DecodeWithLibjpeg(CompressParams(), DecompressParams(), compressed, &output);
111   VerifyOutputImage(input, output, 2.5);
112 }
113
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);
138
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];
146     }
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) {
154           rowdata[c][i] =
155               (y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr);
156         }
157       }
158       while (jpegli_write_raw_data(&cinfo, &data[0], max_lines) == 0) {
159         dest.EmptyTo(&compressed, config.buffer_size);
160       }
161     }
162     dest.EmptyTo(&compressed, kFinalBufferSize);
163     jpegli_finish_compress(&cinfo);
164     dest.EmptyTo(&compressed);
165     return true;
166   };
167   try_catch_block();
168   jpegli_destroy_compress(&cinfo);
169   DecompressParams dparams;
170   dparams.output_mode = RAW_DATA;
171   TestImage output;
172   DecodeWithLibjpeg(CompressParams(), dparams, compressed, &output);
173   VerifyOutputImage(input, output, 3.5);
174 }
175
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}) {
184           TestConfig config;
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);
192         }
193       }
194     }
195   }
196   return all_tests;
197 }
198
199 std::ostream& operator<<(std::ostream& os, const TestConfig& c) {
200   os << c.input;
201   os << c.jparams;
202   os << "Lines" << c.lines_batch_size;
203   os << "BufSize" << c.buffer_size;
204   return os;
205 }
206
207 std::string TestDescription(
208     const testing::TestParamInfo<OutputSuspensionTestParam::ParamType>& info) {
209   std::stringstream name;
210   name << info.param;
211   return name.str();
212 }
213
214 JPEGLI_INSTANTIATE_TEST_SUITE_P(OutputSuspensionTest, OutputSuspensionTestParam,
215                                 testing::ValuesIn(GenerateTests()),
216                                 TestDescription);
217
218 }  // namespace
219 }  // namespace jpegli