/src/libjxl/lib/jxl/test_image.cc
Line | Count | Source |
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/jxl/test_image.h" |
7 | | |
8 | | #include <jxl/codestream_header.h> |
9 | | #include <jxl/color_encoding.h> |
10 | | #include <jxl/encode.h> |
11 | | #include <jxl/types.h> |
12 | | |
13 | | #include <algorithm> |
14 | | #include <cstddef> |
15 | | #include <cstdint> |
16 | | #include <cstring> |
17 | | #include <string> |
18 | | #include <utility> |
19 | | #include <vector> |
20 | | |
21 | | #include "lib/extras/dec/color_description.h" |
22 | | #include "lib/extras/dec/color_hints.h" |
23 | | #include "lib/extras/dec/decode.h" |
24 | | #include "lib/extras/packed_image.h" |
25 | | #include "lib/jxl/base/byte_order.h" |
26 | | #include "lib/jxl/base/random.h" |
27 | | #include "lib/jxl/base/span.h" |
28 | | #include "lib/jxl/base/status.h" |
29 | | #include "lib/jxl/color_encoding_internal.h" |
30 | | #include "lib/jxl/test_utils.h" |
31 | | |
32 | | namespace jxl { |
33 | | namespace test { |
34 | | |
35 | | namespace { |
36 | | |
37 | | void StoreValue(float val, size_t bits_per_sample, JxlPixelFormat format, |
38 | 0 | uint8_t** out) { |
39 | 0 | const float mul = (1u << bits_per_sample) - 1; |
40 | 0 | if (format.data_type == JXL_TYPE_UINT8) { |
41 | 0 | **out = val * mul; |
42 | 0 | } else if (format.data_type == JXL_TYPE_UINT16) { |
43 | 0 | uint16_t uval = val * mul; |
44 | 0 | if (SwapEndianness(format.endianness)) { |
45 | 0 | uval = JXL_BSWAP16(uval); |
46 | 0 | } |
47 | 0 | memcpy(*out, &uval, 2); |
48 | 0 | } else if (format.data_type == JXL_TYPE_FLOAT) { |
49 | | // TODO(szabadka) Add support for custom bits / exponent bits floats. |
50 | 0 | if (SwapEndianness(format.endianness)) { |
51 | 0 | val = BSwapFloat(val); |
52 | 0 | } |
53 | 0 | memcpy(*out, &val, 4); |
54 | 0 | } else { |
55 | | // TODO(szabadka) Add support for FLOAT16. |
56 | 0 | } |
57 | 0 | *out += extras::PackedImage::BitsPerChannel(format.data_type) / 8; |
58 | 0 | } |
59 | | |
60 | | void FillPackedImage(size_t bits_per_sample, uint16_t seed, |
61 | 0 | extras::PackedImage* image) { |
62 | 0 | const size_t xsize = image->xsize; |
63 | 0 | const size_t ysize = image->ysize; |
64 | 0 | const JxlPixelFormat format = image->format; |
65 | | |
66 | | // Cause more significant image difference for successive seeds. |
67 | 0 | Rng generator(seed); |
68 | | |
69 | | // Returns random integer in interval [0, max_value) |
70 | 0 | auto rngu = [&generator](size_t max_value) -> size_t { |
71 | 0 | return generator.UniformU(0, max_value); |
72 | 0 | }; |
73 | | |
74 | | // Returns random float in interval [0.0, max_value) |
75 | 0 | auto rngf = [&generator](float max_value) { |
76 | 0 | return generator.UniformF(0.0f, max_value); |
77 | 0 | }; |
78 | | |
79 | | // Dark background gradient color |
80 | 0 | float r0 = rngf(0.5f); |
81 | 0 | float g0 = rngf(0.5f); |
82 | 0 | float b0 = rngf(0.5f); |
83 | 0 | float a0 = rngf(0.5f); |
84 | 0 | float r1 = rngf(0.5f); |
85 | 0 | float g1 = rngf(0.5f); |
86 | 0 | float b1 = rngf(0.5f); |
87 | 0 | float a1 = rngf(0.5f); |
88 | | |
89 | | // Circle with different color |
90 | 0 | size_t circle_x = rngu(xsize); |
91 | 0 | size_t circle_y = rngu(ysize); |
92 | 0 | size_t circle_r = rngu(std::min(xsize, ysize)); |
93 | | |
94 | | // Rectangle with random noise |
95 | 0 | size_t rect_x0 = rngu(xsize); |
96 | 0 | size_t rect_y0 = rngu(ysize); |
97 | 0 | size_t rect_x1 = rngu(xsize); |
98 | 0 | size_t rect_y1 = rngu(ysize); |
99 | 0 | if (rect_x1 < rect_x0) std::swap(rect_x0, rect_x1); |
100 | 0 | if (rect_y1 < rect_y0) std::swap(rect_y0, rect_y1); |
101 | | |
102 | | // Create pixel content to test, actual content does not matter as long as it |
103 | | // can be compared after roundtrip. |
104 | 0 | const float imul16 = 1.0f / 65536.0f; |
105 | 0 | for (size_t y = 0; y < ysize; y++) { |
106 | 0 | uint8_t* out = |
107 | 0 | reinterpret_cast<uint8_t*>(image->pixels()) + y * image->stride; |
108 | 0 | for (size_t x = 0; x < xsize; x++) { |
109 | 0 | float r = r0 * (ysize - y - 1) / ysize + r1 * y / ysize; |
110 | 0 | float g = g0 * (ysize - y - 1) / ysize + g1 * y / ysize; |
111 | 0 | float b = b0 * (ysize - y - 1) / ysize + b1 * y / ysize; |
112 | 0 | float a = a0 * (ysize - y - 1) / ysize + a1 * y / ysize; |
113 | | // put some shape in there for visual debugging |
114 | 0 | if ((x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y) < |
115 | 0 | circle_r * circle_r) { |
116 | 0 | r = std::min(1.0f, ((65535 - x * y) ^ seed) * imul16); |
117 | 0 | g = std::min(1.0f, ((x << 8) + y + seed) * imul16); |
118 | 0 | b = std::min(1.0f, ((y << 8) + x * seed) * imul16); |
119 | 0 | a = std::min(1.0f, (32768 + x * 256 - y) * imul16); |
120 | 0 | } else if (x > rect_x0 && x < rect_x1 && y > rect_y0 && y < rect_y1) { |
121 | 0 | r = rngf(1.0f); |
122 | 0 | g = rngf(1.0f); |
123 | 0 | b = rngf(1.0f); |
124 | 0 | a = rngf(1.0f); |
125 | 0 | } |
126 | 0 | if (format.num_channels == 1) { |
127 | 0 | StoreValue(g, bits_per_sample, format, &out); |
128 | 0 | } else if (format.num_channels == 2) { |
129 | 0 | StoreValue(g, bits_per_sample, format, &out); |
130 | 0 | StoreValue(a, bits_per_sample, format, &out); |
131 | 0 | } else if (format.num_channels == 3) { |
132 | 0 | StoreValue(r, bits_per_sample, format, &out); |
133 | 0 | StoreValue(g, bits_per_sample, format, &out); |
134 | 0 | StoreValue(b, bits_per_sample, format, &out); |
135 | 0 | } else if (format.num_channels == 4) { |
136 | 0 | StoreValue(r, bits_per_sample, format, &out); |
137 | 0 | StoreValue(g, bits_per_sample, format, &out); |
138 | 0 | StoreValue(b, bits_per_sample, format, &out); |
139 | 0 | StoreValue(a, bits_per_sample, format, &out); |
140 | 0 | } |
141 | 0 | } |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | } // namespace |
146 | | |
147 | | std::vector<uint8_t> GetSomeTestImage(size_t xsize, size_t ysize, |
148 | 224 | size_t num_channels, uint16_t seed) { |
149 | | // Cause more significant image difference for successive seeds. |
150 | 224 | Rng generator(seed); |
151 | | |
152 | | // Returns random integer in interval [0, max_value) |
153 | 3.36k | auto rng = [&generator](size_t max_value) -> size_t { |
154 | 3.36k | return generator.UniformU(0, max_value); |
155 | 3.36k | }; |
156 | | |
157 | | // Dark background gradient color |
158 | 224 | uint16_t r0 = rng(32768); |
159 | 224 | uint16_t g0 = rng(32768); |
160 | 224 | uint16_t b0 = rng(32768); |
161 | 224 | uint16_t a0 = rng(32768); |
162 | 224 | uint16_t r1 = rng(32768); |
163 | 224 | uint16_t g1 = rng(32768); |
164 | 224 | uint16_t b1 = rng(32768); |
165 | 224 | uint16_t a1 = rng(32768); |
166 | | |
167 | | // Circle with different color |
168 | 224 | size_t circle_x = rng(xsize); |
169 | 224 | size_t circle_y = rng(ysize); |
170 | 224 | size_t circle_r = rng(std::min(xsize, ysize)); |
171 | | |
172 | | // Rectangle with random noise |
173 | 224 | size_t rect_x0 = rng(xsize); |
174 | 224 | size_t rect_y0 = rng(ysize); |
175 | 224 | size_t rect_x1 = rng(xsize); |
176 | 224 | size_t rect_y1 = rng(ysize); |
177 | 224 | if (rect_x1 < rect_x0) std::swap(rect_x0, rect_x1); |
178 | 224 | if (rect_y1 < rect_y0) std::swap(rect_y0, rect_y1); |
179 | | |
180 | 224 | size_t num_pixels = xsize * ysize; |
181 | | // 16 bits per channel, big endian, 4 channels |
182 | 224 | std::vector<uint8_t> pixels(num_pixels * num_channels * 2); |
183 | | // Create pixel content to test, actual content does not matter as long as it |
184 | | // can be compared after roundtrip. |
185 | 448 | for (size_t y = 0; y < ysize; y++) { |
186 | 448 | for (size_t x = 0; x < xsize; x++) { |
187 | 224 | uint16_t r = r0 * (ysize - y - 1) / ysize + r1 * y / ysize; |
188 | 224 | uint16_t g = g0 * (ysize - y - 1) / ysize + g1 * y / ysize; |
189 | 224 | uint16_t b = b0 * (ysize - y - 1) / ysize + b1 * y / ysize; |
190 | 224 | uint16_t a = a0 * (ysize - y - 1) / ysize + a1 * y / ysize; |
191 | | // put some shape in there for visual debugging |
192 | 224 | if ((x - circle_x) * (x - circle_x) + (y - circle_y) * (y - circle_y) < |
193 | 224 | circle_r * circle_r) { |
194 | 0 | r = (65535 - x * y) ^ seed; |
195 | 0 | g = (x << 8) + y + seed; |
196 | 0 | b = (y << 8) + x * seed; |
197 | 0 | a = 32768 + x * 256 - y; |
198 | 224 | } else if (x > rect_x0 && x < rect_x1 && y > rect_y0 && y < rect_y1) { |
199 | 0 | r = rng(65536); |
200 | 0 | g = rng(65536); |
201 | 0 | b = rng(65536); |
202 | 0 | a = rng(65536); |
203 | 0 | } |
204 | 224 | size_t i = (y * xsize + x) * 2 * num_channels; |
205 | 224 | pixels[i + 0] = (r >> 8); |
206 | 224 | pixels[i + 1] = (r & 255); |
207 | 224 | if (num_channels >= 2) { |
208 | | // This may store what is called 'g' in the alpha channel of a 2-channel |
209 | | // image, but that's ok since the content is arbitrary |
210 | 224 | pixels[i + 2] = (g >> 8); |
211 | 224 | pixels[i + 3] = (g & 255); |
212 | 224 | } |
213 | 224 | if (num_channels >= 3) { |
214 | 224 | pixels[i + 4] = (b >> 8); |
215 | 224 | pixels[i + 5] = (b & 255); |
216 | 224 | } |
217 | 224 | if (num_channels >= 4) { |
218 | 0 | pixels[i + 6] = (a >> 8); |
219 | 0 | pixels[i + 7] = (a & 255); |
220 | 0 | } |
221 | 224 | } |
222 | 224 | } |
223 | 224 | return pixels; |
224 | 224 | } |
225 | | |
226 | 0 | TestImage::TestImage() { |
227 | 0 | Check(SetChannels(3)); |
228 | 0 | SetAllBitDepths(8); |
229 | 0 | Check(SetColorEncoding("RGB_D65_SRG_Rel_SRG")); |
230 | 0 | } |
231 | | |
232 | 0 | Status TestImage::DecodeFromBytes(const std::vector<uint8_t>& bytes) { |
233 | 0 | ColorEncoding c_enc; |
234 | 0 | JXL_RETURN_IF_ERROR(c_enc.FromExternal(ppf_.color_encoding)); |
235 | 0 | extras::ColorHints color_hints; |
236 | 0 | color_hints.Add("color_space", Description(c_enc)); |
237 | 0 | JXL_RETURN_IF_ERROR(extras::DecodeBytes(Bytes(bytes), color_hints, &ppf_)); |
238 | 0 | return true; |
239 | 0 | } |
240 | | |
241 | 0 | TestImage& TestImage::ClearMetadata() { |
242 | 0 | ppf_.metadata = extras::PackedMetadata(); |
243 | 0 | return *this; |
244 | 0 | } |
245 | | |
246 | 0 | Status TestImage::SetDimensions(size_t xsize, size_t ysize) { |
247 | 0 | if (xsize <= ppf_.info.xsize && ysize <= ppf_.info.ysize) { |
248 | 0 | for (auto& frame : ppf_.frames) { |
249 | 0 | CropLayerInfo(xsize, ysize, &frame.frame_info.layer_info); |
250 | 0 | CropImage(xsize, ysize, &frame.color); |
251 | 0 | for (auto& ec : frame.extra_channels) { |
252 | 0 | CropImage(xsize, ysize, &ec); |
253 | 0 | } |
254 | 0 | } |
255 | 0 | } else { |
256 | 0 | JXL_ENSURE(ppf_.info.xsize == 0 && ppf_.info.ysize == 0); |
257 | 0 | } |
258 | 0 | ppf_.info.xsize = xsize; |
259 | 0 | ppf_.info.ysize = ysize; |
260 | 0 | return true; |
261 | 0 | } |
262 | | |
263 | 0 | Status TestImage::SetChannels(size_t num_channels) { |
264 | 0 | JXL_ENSURE(ppf_.frames.empty()); |
265 | 0 | JXL_ENSURE(!ppf_.preview_frame); |
266 | 0 | ppf_.info.num_color_channels = num_channels < 3 ? 1 : 3; |
267 | 0 | ppf_.info.num_extra_channels = num_channels - ppf_.info.num_color_channels; |
268 | 0 | if (ppf_.info.num_extra_channels > 0 && ppf_.info.alpha_bits == 0) { |
269 | 0 | ppf_.info.alpha_bits = ppf_.info.bits_per_sample; |
270 | 0 | ppf_.info.alpha_exponent_bits = ppf_.info.exponent_bits_per_sample; |
271 | 0 | } |
272 | 0 | ppf_.extra_channels_info.clear(); |
273 | 0 | for (size_t i = 1; i < ppf_.info.num_extra_channels; ++i) { |
274 | 0 | extras::PackedExtraChannel ec; |
275 | 0 | ec.index = i; |
276 | 0 | JxlEncoderInitExtraChannelInfo(JXL_CHANNEL_ALPHA, &ec.ec_info); |
277 | 0 | if (ec.ec_info.bits_per_sample == 0) { |
278 | 0 | ec.ec_info.bits_per_sample = ppf_.info.bits_per_sample; |
279 | 0 | ec.ec_info.exponent_bits_per_sample = ppf_.info.exponent_bits_per_sample; |
280 | 0 | } |
281 | 0 | ppf_.extra_channels_info.emplace_back(std::move(ec)); |
282 | 0 | } |
283 | 0 | format_.num_channels = std::min(static_cast<size_t>(4), num_channels); |
284 | 0 | if (ppf_.info.num_color_channels == 1 && |
285 | 0 | ppf_.color_encoding.color_space != JXL_COLOR_SPACE_GRAY) { |
286 | 0 | JXL_RETURN_IF_ERROR(SetColorEncoding("Gra_D65_Rel_SRG")); |
287 | 0 | } |
288 | 0 | return true; |
289 | 0 | } |
290 | | |
291 | | // Sets the same bit depth on color, alpha and all extra channels. |
292 | | TestImage& TestImage::SetAllBitDepths(uint32_t bits_per_sample, |
293 | 0 | uint32_t exponent_bits_per_sample) { |
294 | 0 | ppf_.info.bits_per_sample = bits_per_sample; |
295 | 0 | ppf_.info.exponent_bits_per_sample = exponent_bits_per_sample; |
296 | 0 | if (ppf_.info.num_extra_channels > 0) { |
297 | 0 | ppf_.info.alpha_bits = bits_per_sample; |
298 | 0 | ppf_.info.alpha_exponent_bits = exponent_bits_per_sample; |
299 | 0 | } |
300 | 0 | for (auto& ec : ppf_.extra_channels_info) { |
301 | 0 | ec.ec_info.bits_per_sample = bits_per_sample; |
302 | 0 | ec.ec_info.exponent_bits_per_sample = exponent_bits_per_sample; |
303 | 0 | } |
304 | 0 | format_.data_type = DefaultDataType(ppf_.info); |
305 | 0 | return *this; |
306 | 0 | } |
307 | | |
308 | 0 | TestImage& TestImage::SetDataType(JxlDataType data_type) { |
309 | 0 | format_.data_type = data_type; |
310 | 0 | return *this; |
311 | 0 | } |
312 | | |
313 | 0 | TestImage& TestImage::SetEndianness(JxlEndianness endianness) { |
314 | 0 | format_.endianness = endianness; |
315 | 0 | return *this; |
316 | 0 | } |
317 | | |
318 | 0 | TestImage& TestImage::SetRowAlignment(size_t align) { |
319 | 0 | format_.align = align; |
320 | 0 | return *this; |
321 | 0 | } |
322 | | |
323 | 0 | Status TestImage::SetColorEncoding(const std::string& description) { |
324 | 0 | JXL_RETURN_IF_ERROR(ParseDescription(description, &ppf_.color_encoding)); |
325 | 0 | ColorEncoding c_enc; |
326 | 0 | JXL_RETURN_IF_ERROR(c_enc.FromExternal(ppf_.color_encoding)); |
327 | 0 | IccBytes icc = c_enc.ICC(); |
328 | 0 | JXL_ENSURE(!icc.empty()); |
329 | 0 | ppf_.icc.assign(icc.begin(), icc.end()); |
330 | 0 | return true; |
331 | 0 | } |
332 | | |
333 | 0 | Status TestImage::CoalesceGIFAnimationWithAlpha() { |
334 | 0 | JXL_ASSIGN_OR_RETURN(extras::PackedFrame canvas, ppf_.frames[0].Copy()); |
335 | 0 | JXL_ENSURE(canvas.color.format.num_channels == 3); |
336 | 0 | JXL_ENSURE(canvas.color.format.data_type == JXL_TYPE_UINT8); |
337 | 0 | JXL_ENSURE(canvas.extra_channels.size() == 1); |
338 | 0 | for (size_t i = 1; i < ppf_.frames.size(); i++) { |
339 | 0 | const extras::PackedFrame& frame = ppf_.frames[i]; |
340 | 0 | JXL_ENSURE(frame.extra_channels.size() == 1); |
341 | 0 | const JxlLayerInfo& layer_info = frame.frame_info.layer_info; |
342 | 0 | JXL_ASSIGN_OR_RETURN(extras::PackedFrame rendered, canvas.Copy()); |
343 | 0 | uint8_t* pixels_rendered = |
344 | 0 | reinterpret_cast<uint8_t*>(rendered.color.pixels()); |
345 | 0 | const uint8_t* pixels_frame = |
346 | 0 | reinterpret_cast<const uint8_t*>(frame.color.pixels()); |
347 | 0 | uint8_t* alpha_rendered = |
348 | 0 | reinterpret_cast<uint8_t*>(rendered.extra_channels[0].pixels()); |
349 | 0 | const uint8_t* alpha_frame = |
350 | 0 | reinterpret_cast<const uint8_t*>(frame.extra_channels[0].pixels()); |
351 | 0 | for (size_t y = 0; y < frame.color.ysize; y++) { |
352 | 0 | for (size_t x = 0; x < frame.color.xsize; x++) { |
353 | 0 | size_t idx_frame = y * frame.color.xsize + x; |
354 | 0 | size_t idx_rendered = ((layer_info.crop_y0 + y) * rendered.color.xsize + |
355 | 0 | (layer_info.crop_x0 + x)); |
356 | 0 | if (alpha_frame[idx_frame] != 0) { |
357 | 0 | memcpy(&pixels_rendered[idx_rendered * 3], |
358 | 0 | &pixels_frame[idx_frame * 3], 3); |
359 | 0 | alpha_rendered[idx_rendered] = alpha_frame[idx_frame]; |
360 | 0 | } |
361 | 0 | } |
362 | 0 | } |
363 | 0 | if (layer_info.save_as_reference != 0) { |
364 | 0 | JXL_ASSIGN_OR_RETURN(canvas, rendered.Copy()); |
365 | 0 | } |
366 | 0 | ppf_.frames[i] = std::move(rendered); |
367 | 0 | } |
368 | 0 | return true; |
369 | 0 | } |
370 | | |
371 | | TestImage::Frame::Frame(TestImage* parent, bool is_preview, size_t index) |
372 | 0 | : parent_(parent), is_preview_(is_preview), index_(index) {} |
373 | | |
374 | 0 | void TestImage::Frame::ZeroFill() { |
375 | 0 | memset(frame().color.pixels(), 0, frame().color.pixels_size); |
376 | 0 | for (auto& ec : frame().extra_channels) { |
377 | 0 | memset(ec.pixels(), 0, ec.pixels_size); |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | 0 | void TestImage::Frame::RandomFill(uint16_t seed) { |
382 | 0 | FillPackedImage(ppf().info.bits_per_sample, seed, &frame().color); |
383 | 0 | for (size_t i = 0; i < ppf().extra_channels_info.size(); ++i) { |
384 | 0 | FillPackedImage(ppf().extra_channels_info[i].ec_info.bits_per_sample, |
385 | 0 | seed + 1 + i, &frame().extra_channels[i]); |
386 | 0 | } |
387 | 0 | } |
388 | | |
389 | 0 | Status TestImage::Frame::SetValue(size_t y, size_t x, size_t c, float val) { |
390 | 0 | const extras::PackedImage& color = frame().color; |
391 | 0 | JxlPixelFormat format = color.format; |
392 | 0 | JXL_ENSURE(y < ppf().info.ysize); |
393 | 0 | JXL_ENSURE(x < ppf().info.xsize); |
394 | 0 | JXL_ENSURE(c < format.num_channels); |
395 | 0 | size_t pwidth = extras::PackedImage::BitsPerChannel(format.data_type) / 8; |
396 | 0 | size_t idx = ((y * color.xsize + x) * format.num_channels + c) * pwidth; |
397 | 0 | uint8_t* pixels = reinterpret_cast<uint8_t*>(frame().color.pixels()); |
398 | 0 | uint8_t* p = pixels + idx; |
399 | 0 | StoreValue(val, ppf().info.bits_per_sample, frame().color.format, &p); |
400 | 0 | return true; |
401 | 0 | } |
402 | | |
403 | 0 | StatusOr<TestImage::Frame> TestImage::AddFrame() { |
404 | 0 | size_t index = ppf_.frames.size(); |
405 | 0 | JXL_ASSIGN_OR_RETURN( |
406 | 0 | extras::PackedFrame frame, |
407 | 0 | extras::PackedFrame::Create(ppf_.info.xsize, ppf_.info.ysize, format_)); |
408 | 0 | for (size_t i = 0; i < ppf_.extra_channels_info.size(); ++i) { |
409 | 0 | JxlPixelFormat ec_format = {1, format_.data_type, format_.endianness, 0}; |
410 | 0 | JXL_ASSIGN_OR_RETURN(extras::PackedImage image, |
411 | 0 | extras::PackedImage::Create( |
412 | 0 | ppf_.info.xsize, ppf_.info.ysize, ec_format)); |
413 | 0 | frame.extra_channels.emplace_back(std::move(image)); |
414 | 0 | } |
415 | 0 | ppf_.frames.emplace_back(std::move(frame)); |
416 | 0 | return Frame(this, false, index); |
417 | 0 | } |
418 | | |
419 | 0 | void TestImage::CropLayerInfo(size_t xsize, size_t ysize, JxlLayerInfo* info) { |
420 | 0 | if (info->crop_x0 < static_cast<ptrdiff_t>(xsize)) { |
421 | 0 | info->xsize = std::min<size_t>(info->xsize, xsize - info->crop_x0); |
422 | 0 | } else { |
423 | 0 | info->xsize = 0; |
424 | 0 | } |
425 | 0 | if (info->crop_y0 < static_cast<ptrdiff_t>(ysize)) { |
426 | 0 | info->ysize = std::min<size_t>(info->ysize, ysize - info->crop_y0); |
427 | 0 | } else { |
428 | 0 | info->ysize = 0; |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | void TestImage::CropImage(size_t xsize, size_t ysize, |
433 | 0 | extras::PackedImage* image) { |
434 | 0 | size_t new_stride = (image->stride / image->xsize) * xsize; |
435 | 0 | uint8_t* buf = reinterpret_cast<uint8_t*>(image->pixels()); |
436 | 0 | for (size_t y = 0; y < ysize; ++y) { |
437 | 0 | memmove(&buf[y * new_stride], &buf[y * image->stride], new_stride); |
438 | 0 | } |
439 | 0 | image->xsize = xsize; |
440 | 0 | image->ysize = ysize; |
441 | 0 | image->stride = new_stride; |
442 | 0 | image->pixels_size = ysize * new_stride; |
443 | 0 | } |
444 | | |
445 | 0 | JxlDataType TestImage::DefaultDataType(const JxlBasicInfo& info) { |
446 | 0 | if (info.bits_per_sample == 16 && info.exponent_bits_per_sample == 5) { |
447 | 0 | return JXL_TYPE_FLOAT16; |
448 | 0 | } else if (info.exponent_bits_per_sample > 0 || info.bits_per_sample > 16) { |
449 | 0 | return JXL_TYPE_FLOAT; |
450 | 0 | } else if (info.bits_per_sample > 8) { |
451 | 0 | return JXL_TYPE_UINT16; |
452 | 0 | } else { |
453 | 0 | return JXL_TYPE_UINT8; |
454 | 0 | } |
455 | 0 | } |
456 | | |
457 | | } // namespace test |
458 | | } // namespace jxl |