1 /* sane - Scanner Access Now Easy.
2
3 Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4
5 This file is part of the SANE package.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #define DEBUG_DECLARE_ONLY
22
23 #include "tests.h"
24 #include "tests_printers.h"
25 #include "minigtest.h"
26
27 #include "../../../backend/genesys/image_pipeline.h"
28
29 #include <numeric>
30
31 namespace genesys {
32
33
test_image_buffer_exact_reads()34 void test_image_buffer_exact_reads()
35 {
36 std::vector<std::size_t> requests;
37
38 auto on_read = [&](std::size_t x, std::uint8_t* data)
39 {
40 (void) data;
41 requests.push_back(x);
42 return true;
43 };
44
45 ImageBuffer buffer{1000, on_read};
46 buffer.set_remaining_size(2500);
47
48 std::vector<std::uint8_t> dummy;
49 dummy.resize(1000);
50
51 ASSERT_TRUE(buffer.get_data(1000, dummy.data()));
52 ASSERT_TRUE(buffer.get_data(1000, dummy.data()));
53 ASSERT_TRUE(buffer.get_data(500, dummy.data()));
54
55 std::vector<std::size_t> expected = {
56 1000, 1000, 500
57 };
58 ASSERT_EQ(requests, expected);
59 }
60
test_image_buffer_smaller_reads()61 void test_image_buffer_smaller_reads()
62 {
63 std::vector<std::size_t> requests;
64
65 auto on_read = [&](std::size_t x, std::uint8_t* data)
66 {
67 (void) data;
68 requests.push_back(x);
69 return true;
70 };
71
72 ImageBuffer buffer{1000, on_read};
73 buffer.set_remaining_size(2500);
74
75 std::vector<std::uint8_t> dummy;
76 dummy.resize(700);
77
78 ASSERT_TRUE(buffer.get_data(600, dummy.data()));
79 ASSERT_TRUE(buffer.get_data(600, dummy.data()));
80 ASSERT_TRUE(buffer.get_data(600, dummy.data()));
81 ASSERT_TRUE(buffer.get_data(700, dummy.data()));
82
83 std::vector<std::size_t> expected = {
84 1000, 1000, 500
85 };
86 ASSERT_EQ(requests, expected);
87 }
88
test_image_buffer_larger_reads()89 void test_image_buffer_larger_reads()
90 {
91 std::vector<std::size_t> requests;
92
93 auto on_read = [&](std::size_t x, std::uint8_t* data)
94 {
95 (void) data;
96 requests.push_back(x);
97 return true;
98 };
99
100 ImageBuffer buffer{1000, on_read};
101 buffer.set_remaining_size(2500);
102
103 std::vector<std::uint8_t> dummy;
104 dummy.resize(2500);
105
106 ASSERT_TRUE(buffer.get_data(2500, dummy.data()));
107
108 std::vector<std::size_t> expected = {
109 1000, 1000, 500
110 };
111 ASSERT_EQ(requests, expected);
112 }
113
test_image_buffer_uncapped_remaining_bytes()114 void test_image_buffer_uncapped_remaining_bytes()
115 {
116 std::vector<std::size_t> requests;
117 unsigned request_count = 0;
118 auto on_read = [&](std::size_t x, std::uint8_t* data)
119 {
120 (void) data;
121 requests.push_back(x);
122 request_count++;
123 return request_count < 4;
124 };
125
126 ImageBuffer buffer{1000, on_read};
127
128 std::vector<std::uint8_t> dummy;
129 dummy.resize(3000);
130
131 ASSERT_TRUE(buffer.get_data(3000, dummy.data()));
132 ASSERT_FALSE(buffer.get_data(3000, dummy.data()));
133
134 std::vector<std::size_t> expected = {
135 1000, 1000, 1000, 1000
136 };
137 ASSERT_EQ(requests, expected);
138 }
139
test_image_buffer_capped_remaining_bytes()140 void test_image_buffer_capped_remaining_bytes()
141 {
142 std::vector<std::size_t> requests;
143
144 auto on_read = [&](std::size_t x, std::uint8_t* data)
145 {
146 (void) data;
147 requests.push_back(x);
148 return true;
149 };
150
151 ImageBuffer buffer{1000, on_read};
152 buffer.set_remaining_size(10000);
153 buffer.set_last_read_multiple(16);
154
155 std::vector<std::uint8_t> dummy;
156 dummy.resize(2000);
157
158 ASSERT_TRUE(buffer.get_data(2000, dummy.data()));
159 ASSERT_TRUE(buffer.get_data(2000, dummy.data()));
160 buffer.set_remaining_size(100);
161 ASSERT_FALSE(buffer.get_data(200, dummy.data()));
162
163 std::vector<std::size_t> expected = {
164 // note that the sizes are rounded-up to 16 bytes
165 1000, 1000, 1000, 1000, 112
166 };
167 ASSERT_EQ(requests, expected);
168 }
169
test_node_buffered_callable_source()170 void test_node_buffered_callable_source()
171 {
172 using Data = std::vector<std::uint8_t>;
173
174 Data in_data = {
175 0, 1, 2, 3,
176 4, 5, 6, 7,
177 8, 9, 10, 11
178 };
179
180 std::size_t chunk_size = 3;
181 std::size_t curr_index = 0;
182
183 auto data_source_cb = [&](std::size_t size, std::uint8_t* out_data)
184 {
185 ASSERT_EQ(size, chunk_size);
186 std::copy(in_data.begin() + curr_index,
187 in_data.begin() + curr_index + chunk_size, out_data);
188 curr_index += chunk_size;
189 return true;
190 };
191
192 ImagePipelineStack stack;
193 stack.push_first_node<ImagePipelineNodeBufferedCallableSource>(4, 3, PixelFormat::I8,
194 chunk_size, data_source_cb);
195
196 Data out_data;
197 out_data.resize(4);
198
199 ASSERT_EQ(curr_index, 0u);
200
201 ASSERT_TRUE(stack.get_next_row_data(out_data.data()));
202 ASSERT_EQ(out_data, Data({0, 1, 2, 3}));
203 ASSERT_EQ(curr_index, 6u);
204
205 ASSERT_TRUE(stack.get_next_row_data(out_data.data()));
206 ASSERT_EQ(out_data, Data({4, 5, 6, 7}));
207 ASSERT_EQ(curr_index, 9u);
208
209 ASSERT_TRUE(stack.get_next_row_data(out_data.data()));
210 ASSERT_EQ(out_data, Data({8, 9, 10, 11}));
211 ASSERT_EQ(curr_index, 12u);
212 }
213
test_node_format_convert()214 void test_node_format_convert()
215 {
216 using Data = std::vector<std::uint8_t>;
217
218 Data in_data = {
219 0x12, 0x34, 0x56,
220 0x78, 0x98, 0xab,
221 0xcd, 0xef, 0x21,
222 };
223
224 ImagePipelineStack stack;
225 stack.push_first_node<ImagePipelineNodeArraySource>(3, 1, PixelFormat::RGB888,
226 std::move(in_data));
227 stack.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::BGR161616);
228
229 ASSERT_EQ(stack.get_output_width(), 3u);
230 ASSERT_EQ(stack.get_output_height(), 1u);
231 ASSERT_EQ(stack.get_output_row_bytes(), 6u * 3);
232 ASSERT_EQ(stack.get_output_format(), PixelFormat::BGR161616);
233
234 auto out_data = stack.get_all_data();
235
236 Data expected_data = {
237 0x56, 0x56, 0x34, 0x34, 0x12, 0x12,
238 0xab, 0xab, 0x98, 0x98, 0x78, 0x78,
239 0x21, 0x21, 0xef, 0xef, 0xcd, 0xcd,
240 };
241
242 ASSERT_EQ(out_data, expected_data);
243 }
244
test_node_desegment_1_line()245 void test_node_desegment_1_line()
246 {
247 using Data = std::vector<std::uint8_t>;
248
249 Data in_data = {
250 1, 5, 9, 13, 17,
251 3, 7, 11, 15, 19,
252 2, 6, 10, 14, 18,
253 4, 8, 12, 16, 20,
254 21, 25, 29, 33, 37,
255 23, 27, 31, 35, 39,
256 22, 26, 30, 34, 38,
257 24, 28, 32, 36, 40,
258 };
259
260 ImagePipelineStack stack;
261 stack.push_first_node<ImagePipelineNodeArraySource>(20, 2, PixelFormat::I8,
262 std::move(in_data));
263 stack.push_node<ImagePipelineNodeDesegment>(20, std::vector<unsigned>{ 0, 2, 1, 3 }, 5, 1, 1);
264
265 ASSERT_EQ(stack.get_output_width(), 20u);
266 ASSERT_EQ(stack.get_output_height(), 2u);
267 ASSERT_EQ(stack.get_output_row_bytes(), 20u);
268 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
269
270 auto out_data = stack.get_all_data();
271
272 Data expected_data;
273 expected_data.resize(40, 0);
274 std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 40
275
276 ASSERT_EQ(out_data, expected_data);
277 }
278
test_node_deinterleave_lines_i8()279 void test_node_deinterleave_lines_i8()
280 {
281 using Data = std::vector<std::uint8_t>;
282
283 Data in_data = {
284 1, 3, 5, 7, 9, 11, 13, 15, 17, 19,
285 2, 4, 6, 8, 10, 12, 14, 16, 18, 20,
286 };
287
288 ImagePipelineStack stack;
289 stack.push_first_node<ImagePipelineNodeArraySource>(10, 2, PixelFormat::I8,
290 std::move(in_data));
291 stack.push_node<ImagePipelineNodeDeinterleaveLines>(2, 1);
292
293 ASSERT_EQ(stack.get_output_width(), 20u);
294 ASSERT_EQ(stack.get_output_height(), 1u);
295 ASSERT_EQ(stack.get_output_row_bytes(), 20u);
296 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
297
298 auto out_data = stack.get_all_data();
299
300 Data expected_data;
301 expected_data.resize(20, 0);
302 std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 20
303
304 ASSERT_EQ(out_data, expected_data);
305 }
306
test_node_deinterleave_lines_rgb888()307 void test_node_deinterleave_lines_rgb888()
308 {
309 using Data = std::vector<std::uint8_t>;
310
311 Data in_data = {
312 1, 2, 3, 7, 8, 9, 13, 14, 15, 19, 20, 21,
313 4, 5, 6, 10, 11, 12, 16, 17, 18, 22, 23, 24,
314 };
315
316 ImagePipelineStack stack;
317 stack.push_first_node<ImagePipelineNodeArraySource>(4, 2, PixelFormat::RGB888,
318 std::move(in_data));
319 stack.push_node<ImagePipelineNodeDeinterleaveLines>(2, 1);
320
321 ASSERT_EQ(stack.get_output_width(), 8u);
322 ASSERT_EQ(stack.get_output_height(), 1u);
323 ASSERT_EQ(stack.get_output_row_bytes(), 24u);
324 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
325
326 auto out_data = stack.get_all_data();
327
328 Data expected_data;
329 expected_data.resize(24, 0);
330 std::iota(expected_data.begin(), expected_data.end(), 1); // will fill with 1, 2, 3, ..., 20
331
332 ASSERT_EQ(out_data, expected_data);
333 }
334
test_node_swap_16bit_endian()335 void test_node_swap_16bit_endian()
336 {
337 using Data = std::vector<std::uint8_t>;
338
339 Data in_data = {
340 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
341 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
342 0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
343 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
344 };
345
346 ImagePipelineStack stack;
347 stack.push_first_node<ImagePipelineNodeArraySource>(4, 1, PixelFormat::RGB161616,
348 std::move(in_data));
349 stack.push_node<ImagePipelineNodeSwap16BitEndian>();
350
351 ASSERT_EQ(stack.get_output_width(), 4u);
352 ASSERT_EQ(stack.get_output_height(), 1u);
353 ASSERT_EQ(stack.get_output_row_bytes(), 24u);
354 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616);
355
356 auto out_data = stack.get_all_data();
357
358 Data expected_data = {
359 0x20, 0x10, 0x11, 0x30, 0x31, 0x21,
360 0x22, 0x12, 0x13, 0x32, 0x33, 0x23,
361 0x24, 0x14, 0x15, 0x34, 0x35, 0x25,
362 0x26, 0x16, 0x17, 0x36, 0x37, 0x27,
363 };
364
365 ASSERT_EQ(out_data, expected_data);
366 }
367
test_node_invert_16_bits()368 void test_node_invert_16_bits()
369 {
370 using Data16 = std::vector<std::uint16_t>;
371 using Data = std::vector<std::uint8_t>;
372
373 Data16 in_data = {
374 0x1020, 0x3011, 0x2131,
375 0x1222, 0x3213, 0x2333,
376 0x1424, 0x3415, 0x2525,
377 0x1626, 0x3617, 0x2737,
378 };
379
380 Data in_data_8bit;
381 in_data_8bit.resize(in_data.size() * 2);
382 std::memcpy(in_data_8bit.data(), in_data.data(), in_data_8bit.size());
383
384 ImagePipelineStack stack;
385 stack.push_first_node<ImagePipelineNodeArraySource>(4, 1, PixelFormat::RGB161616,
386 std::move(in_data_8bit));
387 stack.push_node<ImagePipelineNodeInvert>();
388
389 ASSERT_EQ(stack.get_output_width(), 4u);
390 ASSERT_EQ(stack.get_output_height(), 1u);
391 ASSERT_EQ(stack.get_output_row_bytes(), 24u);
392 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616);
393
394 auto out_data_8bit = stack.get_all_data();
395 Data16 out_data;
396 out_data.resize(out_data_8bit.size() / 2);
397 std::memcpy(out_data.data(), out_data_8bit.data(), out_data_8bit.size());
398
399 Data16 expected_data = {
400 0xefdf, 0xcfee, 0xdece,
401 0xeddd, 0xcdec, 0xdccc,
402 0xebdb, 0xcbea, 0xdada,
403 0xe9d9, 0xc9e8, 0xd8c8,
404 };
405
406 ASSERT_EQ(out_data, expected_data);
407 }
408
test_node_invert_8_bits()409 void test_node_invert_8_bits()
410 {
411 using Data = std::vector<std::uint8_t>;
412
413 Data in_data = {
414 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
415 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
416 0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
417 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
418 };
419
420 ImagePipelineStack stack;
421 stack.push_first_node<ImagePipelineNodeArraySource>(8, 1, PixelFormat::RGB888,
422 std::move(in_data));
423 stack.push_node<ImagePipelineNodeInvert>();
424
425 ASSERT_EQ(stack.get_output_width(), 8u);
426 ASSERT_EQ(stack.get_output_height(), 1u);
427 ASSERT_EQ(stack.get_output_row_bytes(), 24u);
428 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
429
430 auto out_data = stack.get_all_data();
431
432 Data expected_data = {
433 0xef, 0xdf, 0xcf, 0xee, 0xde, 0xce,
434 0xed, 0xdd, 0xcd, 0xec, 0xdc, 0xcc,
435 0xeb, 0xdb, 0xcb, 0xea, 0xda, 0xca,
436 0xe9, 0xd9, 0xc9, 0xe8, 0xd8, 0xc8,
437 };
438
439 ASSERT_EQ(out_data, expected_data);
440 }
441
test_node_invert_1_bits()442 void test_node_invert_1_bits()
443 {
444 using Data = std::vector<std::uint8_t>;
445
446 Data in_data = {
447 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
448 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
449 };
450
451 ImagePipelineStack stack;
452 stack.push_first_node<ImagePipelineNodeArraySource>(32, 1, PixelFormat::RGB111,
453 std::move(in_data));
454 stack.push_node<ImagePipelineNodeInvert>();
455
456 ASSERT_EQ(stack.get_output_width(), 32u);
457 ASSERT_EQ(stack.get_output_height(), 1u);
458 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
459 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB111);
460
461 auto out_data = stack.get_all_data();
462
463 Data expected_data = {
464 0xef, 0xdf, 0xcf, 0xee, 0xde, 0xce,
465 0xe9, 0xd9, 0xc9, 0xe8, 0xd8, 0xc8,
466 };
467
468 ASSERT_EQ(out_data, expected_data);
469 }
470
test_node_merge_mono_lines_to_color()471 void test_node_merge_mono_lines_to_color()
472 {
473 using Data = std::vector<std::uint8_t>;
474
475 Data in_data = {
476 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
477 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
478 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
479 };
480
481 ImagePipelineStack stack;
482 stack.push_first_node<ImagePipelineNodeArraySource>(8, 3, PixelFormat::I8,
483 std::move(in_data));
484 stack.push_node<ImagePipelineNodeMergeMonoLinesToColor>(ColorOrder::RGB);
485
486 ASSERT_EQ(stack.get_output_width(), 8u);
487 ASSERT_EQ(stack.get_output_height(), 1u);
488 ASSERT_EQ(stack.get_output_row_bytes(), 24u);
489 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
490
491 auto out_data = stack.get_all_data();
492
493 Data expected_data = {
494 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
495 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
496 0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
497 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
498 };
499
500 ASSERT_EQ(out_data, expected_data);
501 }
502
test_node_merge_color_to_gray()503 void test_node_merge_color_to_gray()
504 {
505 using Data = std::vector<std::uint8_t>;
506
507 Data in_data = {
508 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
509 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
510 0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
511 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
512 };
513
514 ImagePipelineStack stack;
515 stack.push_first_node<ImagePipelineNodeArraySource>(8, 1, PixelFormat::RGB888,
516 std::move(in_data));
517 stack.push_node<ImagePipelineNodeMergeColorToGray>();
518
519 ASSERT_EQ(stack.get_output_width(), 8u);
520 ASSERT_EQ(stack.get_output_height(), 1u);
521 ASSERT_EQ(stack.get_output_row_bytes(), 8u);
522 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
523
524 auto out_data = stack.get_all_data();
525
526 Data expected_data = {
527 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24
528 };
529
530 ASSERT_EQ(out_data, expected_data);
531 }
532
533
test_node_split_mono_lines()534 void test_node_split_mono_lines()
535 {
536 using Data = std::vector<std::uint8_t>;
537
538 Data in_data = {
539 0x10, 0x20, 0x30, 0x11, 0x21, 0x31,
540 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
541 0x14, 0x24, 0x34, 0x15, 0x25, 0x35,
542 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
543 };
544
545 ImagePipelineStack stack;
546 stack.push_first_node<ImagePipelineNodeArraySource>(8, 1, PixelFormat::RGB888,
547 std::move(in_data));
548 stack.push_node<ImagePipelineNodeSplitMonoLines>();
549
550 ASSERT_EQ(stack.get_output_width(), 8u);
551 ASSERT_EQ(stack.get_output_height(), 3u);
552 ASSERT_EQ(stack.get_output_row_bytes(), 8u);
553 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
554
555 auto out_data = stack.get_all_data();
556
557 Data expected_data = {
558 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
559 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
560 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
561 };
562
563 ASSERT_EQ(out_data, expected_data);
564 }
565
test_node_component_shift_lines()566 void test_node_component_shift_lines()
567 {
568 using Data = std::vector<std::uint8_t>;
569
570 Data in_data = {
571 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
572 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
573 0x18, 0x28, 0x38, 0x19, 0x29, 0x39, 0x1a, 0x2a, 0x3a, 0x1b, 0x2b, 0x3b,
574 0x1c, 0x2c, 0x3c, 0x1d, 0x2d, 0x3d, 0x1e, 0x2e, 0x3e, 0x1f, 0x2f, 0x3f,
575 };
576
577 ImagePipelineStack stack;
578 stack.push_first_node<ImagePipelineNodeArraySource>(4, 4, PixelFormat::RGB888,
579 std::move(in_data));
580 stack.push_node<ImagePipelineNodeComponentShiftLines>(0, 1, 2);
581
582 ASSERT_EQ(stack.get_output_width(), 4u);
583 ASSERT_EQ(stack.get_output_height(), 2u);
584 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
585 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
586
587 auto out_data = stack.get_all_data();
588
589 Data expected_data = {
590 0x10, 0x24, 0x38, 0x11, 0x25, 0x39, 0x12, 0x26, 0x3a, 0x13, 0x27, 0x3b,
591 0x14, 0x28, 0x3c, 0x15, 0x29, 0x3d, 0x16, 0x2a, 0x3e, 0x17, 0x2b, 0x3f,
592 };
593
594 ASSERT_EQ(out_data, expected_data);
595 }
596
test_node_pixel_shift_lines_2lines()597 void test_node_pixel_shift_lines_2lines()
598 {
599 using Data = std::vector<std::uint8_t>;
600
601 Data in_data = {
602 0x10, 0x20, 0x30, 0x11, 0x21, 0x31, 0x12, 0x22, 0x32, 0x13, 0x23, 0x33,
603 0x14, 0x24, 0x34, 0x15, 0x25, 0x35, 0x16, 0x26, 0x36, 0x17, 0x27, 0x37,
604 0x18, 0x28, 0x38, 0x19, 0x29, 0x39, 0x1a, 0x2a, 0x3a, 0x1b, 0x2b, 0x3b,
605 0x1c, 0x2c, 0x3c, 0x1d, 0x2d, 0x3d, 0x1e, 0x2e, 0x3e, 0x1f, 0x2f, 0x3f,
606 };
607
608 ImagePipelineStack stack;
609 stack.push_first_node<ImagePipelineNodeArraySource>(4, 4, PixelFormat::RGB888,
610 std::move(in_data));
611 stack.push_node<ImagePipelineNodePixelShiftLines>(std::vector<std::size_t>{0, 2});
612
613 ASSERT_EQ(stack.get_output_width(), 4u);
614 ASSERT_EQ(stack.get_output_height(), 2u);
615 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
616 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
617
618 auto out_data = stack.get_all_data();
619
620 Data expected_data = {
621 0x10, 0x20, 0x30, 0x19, 0x29, 0x39, 0x12, 0x22, 0x32, 0x1b, 0x2b, 0x3b,
622 0x14, 0x24, 0x34, 0x1d, 0x2d, 0x3d, 0x16, 0x26, 0x36, 0x1f, 0x2f, 0x3f,
623 };
624
625 ASSERT_EQ(out_data, expected_data);
626 }
627
test_node_pixel_shift_lines_4lines()628 void test_node_pixel_shift_lines_4lines()
629 {
630 using Data = std::vector<std::uint8_t>;
631
632 Data in_data = {
633 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
634 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
635 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
636 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
637 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
638 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
639 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
640 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b,
641 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b,
642 };
643
644 ImagePipelineStack stack;
645 stack.push_first_node<ImagePipelineNodeArraySource>(12, 9, PixelFormat::I8,
646 std::move(in_data));
647 stack.push_node<ImagePipelineNodePixelShiftLines>(std::vector<std::size_t>{0, 2, 1, 3});
648
649 ASSERT_EQ(stack.get_output_width(), 12u);
650 ASSERT_EQ(stack.get_output_height(), 6u);
651 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
652 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
653
654 auto out_data = stack.get_all_data();
655
656 Data expected_data = {
657 0x00, 0x21, 0x12, 0x33, 0x04, 0x25, 0x16, 0x37, 0x08, 0x29, 0x1a, 0x3b,
658 0x10, 0x31, 0x22, 0x43, 0x14, 0x35, 0x26, 0x47, 0x18, 0x39, 0x2a, 0x4b,
659 0x20, 0x41, 0x32, 0x53, 0x24, 0x45, 0x36, 0x57, 0x28, 0x49, 0x3a, 0x5b,
660 0x30, 0x51, 0x42, 0x63, 0x34, 0x55, 0x46, 0x67, 0x38, 0x59, 0x4a, 0x6b,
661 0x40, 0x61, 0x52, 0x73, 0x44, 0x65, 0x56, 0x77, 0x48, 0x69, 0x5a, 0x7b,
662 0x50, 0x71, 0x62, 0x83, 0x54, 0x75, 0x66, 0x87, 0x58, 0x79, 0x6a, 0x8b,
663 };
664
665 ASSERT_EQ(out_data, expected_data);
666 }
667
test_node_pixel_shift_columns_compute_max_width()668 void test_node_pixel_shift_columns_compute_max_width()
669 {
670 ASSERT_EQ(compute_pixel_shift_extra_width(12, {0, 1, 2, 3}), 0u);
671 ASSERT_EQ(compute_pixel_shift_extra_width(13, {0, 1, 2, 3}), 0u);
672 ASSERT_EQ(compute_pixel_shift_extra_width(14, {0, 1, 2, 3}), 0u);
673 ASSERT_EQ(compute_pixel_shift_extra_width(15, {0, 1, 2, 3}), 0u);
674 ASSERT_EQ(compute_pixel_shift_extra_width(16, {0, 1, 2, 3}), 0u);
675 ASSERT_EQ(compute_pixel_shift_extra_width(17, {0, 1, 2, 3}), 0u);
676 ASSERT_EQ(compute_pixel_shift_extra_width(18, {0, 1, 2, 3}), 0u);
677 ASSERT_EQ(compute_pixel_shift_extra_width(19, {0, 1, 2, 3}), 0u);
678
679 ASSERT_EQ(compute_pixel_shift_extra_width(12, {1, 1, 2, 3}), 0u);
680 ASSERT_EQ(compute_pixel_shift_extra_width(13, {1, 1, 2, 3}), 1u);
681 ASSERT_EQ(compute_pixel_shift_extra_width(14, {1, 1, 2, 3}), 0u);
682 ASSERT_EQ(compute_pixel_shift_extra_width(15, {1, 1, 2, 3}), 0u);
683 ASSERT_EQ(compute_pixel_shift_extra_width(16, {1, 1, 2, 3}), 0u);
684 ASSERT_EQ(compute_pixel_shift_extra_width(17, {1, 1, 2, 3}), 1u);
685 ASSERT_EQ(compute_pixel_shift_extra_width(18, {1, 1, 2, 3}), 0u);
686 ASSERT_EQ(compute_pixel_shift_extra_width(19, {1, 1, 2, 3}), 0u);
687
688 ASSERT_EQ(compute_pixel_shift_extra_width(12, {2, 1, 2, 3}), 0u);
689 ASSERT_EQ(compute_pixel_shift_extra_width(13, {2, 1, 2, 3}), 1u);
690 ASSERT_EQ(compute_pixel_shift_extra_width(14, {2, 1, 2, 3}), 2u);
691 ASSERT_EQ(compute_pixel_shift_extra_width(15, {2, 1, 2, 3}), 0u);
692 ASSERT_EQ(compute_pixel_shift_extra_width(16, {2, 1, 2, 3}), 0u);
693 ASSERT_EQ(compute_pixel_shift_extra_width(17, {2, 1, 2, 3}), 1u);
694 ASSERT_EQ(compute_pixel_shift_extra_width(18, {2, 1, 2, 3}), 2u);
695 ASSERT_EQ(compute_pixel_shift_extra_width(19, {2, 1, 2, 3}), 0u);
696
697 ASSERT_EQ(compute_pixel_shift_extra_width(12, {3, 1, 2, 3}), 0u);
698 ASSERT_EQ(compute_pixel_shift_extra_width(13, {3, 1, 2, 3}), 1u);
699 ASSERT_EQ(compute_pixel_shift_extra_width(14, {3, 1, 2, 3}), 2u);
700 ASSERT_EQ(compute_pixel_shift_extra_width(15, {3, 1, 2, 3}), 3u);
701 ASSERT_EQ(compute_pixel_shift_extra_width(16, {3, 1, 2, 3}), 0u);
702 ASSERT_EQ(compute_pixel_shift_extra_width(17, {3, 1, 2, 3}), 1u);
703 ASSERT_EQ(compute_pixel_shift_extra_width(18, {3, 1, 2, 3}), 2u);
704 ASSERT_EQ(compute_pixel_shift_extra_width(19, {3, 1, 2, 3}), 3u);
705
706 ASSERT_EQ(compute_pixel_shift_extra_width(12, {7, 1, 2, 3}), 4u);
707 ASSERT_EQ(compute_pixel_shift_extra_width(13, {7, 1, 2, 3}), 5u);
708 ASSERT_EQ(compute_pixel_shift_extra_width(14, {7, 1, 2, 3}), 6u);
709 ASSERT_EQ(compute_pixel_shift_extra_width(15, {7, 1, 2, 3}), 7u);
710 ASSERT_EQ(compute_pixel_shift_extra_width(16, {7, 1, 2, 3}), 4u);
711 ASSERT_EQ(compute_pixel_shift_extra_width(17, {7, 1, 2, 3}), 5u);
712 ASSERT_EQ(compute_pixel_shift_extra_width(18, {7, 1, 2, 3}), 6u);
713 ASSERT_EQ(compute_pixel_shift_extra_width(19, {7, 1, 2, 3}), 7u);
714
715 ASSERT_EQ(compute_pixel_shift_extra_width(12, {0, 1, 3, 3}), 0u);
716 ASSERT_EQ(compute_pixel_shift_extra_width(13, {0, 1, 3, 3}), 0u);
717 ASSERT_EQ(compute_pixel_shift_extra_width(14, {0, 1, 3, 3}), 0u);
718 ASSERT_EQ(compute_pixel_shift_extra_width(15, {0, 1, 3, 3}), 1u);
719 ASSERT_EQ(compute_pixel_shift_extra_width(16, {0, 1, 3, 3}), 0u);
720 ASSERT_EQ(compute_pixel_shift_extra_width(17, {0, 1, 3, 3}), 0u);
721 ASSERT_EQ(compute_pixel_shift_extra_width(18, {0, 1, 3, 3}), 0u);
722 ASSERT_EQ(compute_pixel_shift_extra_width(19, {0, 1, 3, 3}), 1u);
723
724 ASSERT_EQ(compute_pixel_shift_extra_width(12, {0, 1, 4, 3}), 2u);
725 ASSERT_EQ(compute_pixel_shift_extra_width(13, {0, 1, 4, 3}), 0u);
726 ASSERT_EQ(compute_pixel_shift_extra_width(14, {0, 1, 4, 3}), 0u);
727 ASSERT_EQ(compute_pixel_shift_extra_width(15, {0, 1, 4, 3}), 1u);
728 ASSERT_EQ(compute_pixel_shift_extra_width(16, {0, 1, 4, 3}), 2u);
729 ASSERT_EQ(compute_pixel_shift_extra_width(17, {0, 1, 4, 3}), 0u);
730 ASSERT_EQ(compute_pixel_shift_extra_width(18, {0, 1, 4, 3}), 0u);
731 ASSERT_EQ(compute_pixel_shift_extra_width(19, {0, 1, 4, 3}), 1u);
732
733 ASSERT_EQ(compute_pixel_shift_extra_width(12, {0, 1, 5, 3}), 2u);
734 ASSERT_EQ(compute_pixel_shift_extra_width(13, {0, 1, 5, 3}), 3u);
735 ASSERT_EQ(compute_pixel_shift_extra_width(14, {0, 1, 5, 3}), 0u);
736 ASSERT_EQ(compute_pixel_shift_extra_width(15, {0, 1, 5, 3}), 1u);
737 ASSERT_EQ(compute_pixel_shift_extra_width(16, {0, 1, 5, 3}), 2u);
738 ASSERT_EQ(compute_pixel_shift_extra_width(17, {0, 1, 5, 3}), 3u);
739 ASSERT_EQ(compute_pixel_shift_extra_width(18, {0, 1, 5, 3}), 0u);
740 ASSERT_EQ(compute_pixel_shift_extra_width(19, {0, 1, 5, 3}), 1u);
741
742 ASSERT_EQ(compute_pixel_shift_extra_width(12, {0, 1, 9, 3}), 6u);
743 ASSERT_EQ(compute_pixel_shift_extra_width(13, {0, 1, 9, 3}), 7u);
744 ASSERT_EQ(compute_pixel_shift_extra_width(14, {0, 1, 9, 3}), 4u);
745 ASSERT_EQ(compute_pixel_shift_extra_width(15, {0, 1, 9, 3}), 5u);
746 ASSERT_EQ(compute_pixel_shift_extra_width(16, {0, 1, 9, 3}), 6u);
747 ASSERT_EQ(compute_pixel_shift_extra_width(17, {0, 1, 9, 3}), 7u);
748 ASSERT_EQ(compute_pixel_shift_extra_width(18, {0, 1, 9, 3}), 4u);
749 ASSERT_EQ(compute_pixel_shift_extra_width(19, {0, 1, 9, 3}), 5u);
750 }
751
test_node_pixel_shift_columns_no_switch()752 void test_node_pixel_shift_columns_no_switch()
753 {
754 using Data = std::vector<std::uint8_t>;
755
756 Data in_data = {
757 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
758 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
759 };
760
761 ImagePipelineStack stack;
762 stack.push_first_node<ImagePipelineNodeArraySource>(12, 2, PixelFormat::I8, in_data);
763 stack.push_node<ImagePipelineNodePixelShiftColumns>(std::vector<std::size_t>{0, 1, 2, 3});
764
765 ASSERT_EQ(stack.get_output_width(), 12u);
766 ASSERT_EQ(stack.get_output_height(), 2u);
767 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
768 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
769
770 auto out_data = stack.get_all_data();
771
772 ASSERT_EQ(out_data, in_data);
773 }
774
test_node_pixel_shift_columns_group_switch_pixel_multiple()775 void test_node_pixel_shift_columns_group_switch_pixel_multiple()
776 {
777 using Data = std::vector<std::uint8_t>;
778
779 Data in_data = {
780 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
781 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
782 };
783
784 ImagePipelineStack stack;
785 stack.push_first_node<ImagePipelineNodeArraySource>(12, 2, PixelFormat::I8, in_data);
786 stack.push_node<ImagePipelineNodePixelShiftColumns>(std::vector<std::size_t>{3, 1, 2, 0});
787
788 ASSERT_EQ(stack.get_output_width(), 12u);
789 ASSERT_EQ(stack.get_output_height(), 2u);
790 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
791 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
792
793 auto out_data = stack.get_all_data();
794
795 Data expected_data = {
796 0x03, 0x01, 0x02, 0x00, 0x07, 0x05, 0x06, 0x04, 0x0b, 0x09, 0x0a, 0x08,
797 0x13, 0x11, 0x12, 0x10, 0x17, 0x15, 0x16, 0x14, 0x1b, 0x19, 0x1a, 0x18,
798 };
799 ASSERT_EQ(out_data, expected_data);
800 }
801
test_node_pixel_shift_columns_group_switch_pixel_not_multiple()802 void test_node_pixel_shift_columns_group_switch_pixel_not_multiple()
803 {
804 using Data = std::vector<std::uint8_t>;
805
806 Data in_data = {
807 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
808 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
809 };
810
811 ImagePipelineStack stack;
812 stack.push_first_node<ImagePipelineNodeArraySource>(13, 2, PixelFormat::I8, in_data);
813 stack.push_node<ImagePipelineNodePixelShiftColumns>(std::vector<std::size_t>{3, 1, 2, 0});
814
815 ASSERT_EQ(stack.get_output_width(), 12u);
816 ASSERT_EQ(stack.get_output_height(), 2u);
817 ASSERT_EQ(stack.get_output_row_bytes(), 12u);
818 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
819
820 auto out_data = stack.get_all_data();
821
822 Data expected_data = {
823 0x03, 0x01, 0x02, 0x00, 0x07, 0x05, 0x06, 0x04, 0x0b, 0x09, 0x0a, 0x08,
824 0x13, 0x11, 0x12, 0x10, 0x17, 0x15, 0x16, 0x14, 0x1b, 0x19, 0x1a, 0x18,
825 };
826 ASSERT_EQ(out_data, expected_data);
827 }
828
test_node_pixel_shift_columns_group_switch_pixel_large_offsets_multiple()829 void test_node_pixel_shift_columns_group_switch_pixel_large_offsets_multiple()
830 {
831 using Data = std::vector<std::uint8_t>;
832
833 Data in_data = {
834 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
835 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
836 };
837
838 ImagePipelineStack stack;
839 stack.push_first_node<ImagePipelineNodeArraySource>(12, 2, PixelFormat::I8, in_data);
840 stack.push_node<ImagePipelineNodePixelShiftColumns>(std::vector<std::size_t>{7, 1, 5, 0});
841
842 ASSERT_EQ(stack.get_output_width(), 8u);
843 ASSERT_EQ(stack.get_output_height(), 2u);
844 ASSERT_EQ(stack.get_output_row_bytes(), 8u);
845 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
846
847 auto out_data = stack.get_all_data();
848
849 Data expected_data = {
850 0x07, 0x01, 0x05, 0x00, 0x0b, 0x05, 0x09, 0x04,
851 0x17, 0x11, 0x15, 0x10, 0x1b, 0x15, 0x19, 0x14,
852 };
853 ASSERT_EQ(out_data, expected_data);
854 }
855
test_node_pixel_shift_columns_group_switch_pixel_large_offsets_not_multiple()856 void test_node_pixel_shift_columns_group_switch_pixel_large_offsets_not_multiple()
857 {
858 using Data = std::vector<std::uint8_t>;
859
860 Data in_data = {
861 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c,
862 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
863 };
864
865 ImagePipelineStack stack;
866 stack.push_first_node<ImagePipelineNodeArraySource>(13, 2, PixelFormat::I8, in_data);
867 stack.push_node<ImagePipelineNodePixelShiftColumns>(std::vector<std::size_t>{7, 1, 5, 0});
868
869 ASSERT_EQ(stack.get_output_width(), 8u);
870 ASSERT_EQ(stack.get_output_height(), 2u);
871 ASSERT_EQ(stack.get_output_row_bytes(), 8u);
872 ASSERT_EQ(stack.get_output_format(), PixelFormat::I8);
873
874 auto out_data = stack.get_all_data();
875
876 Data expected_data = {
877 0x07, 0x01, 0x05, 0x00, 0x0b, 0x05, 0x09, 0x04,
878 0x17, 0x11, 0x15, 0x10, 0x1b, 0x15, 0x19, 0x14,
879 };
880 ASSERT_EQ(out_data, expected_data);
881 }
882
test_node_calibrate_8bit()883 void test_node_calibrate_8bit()
884 {
885 using Data = std::vector<std::uint8_t>;
886
887 Data in_data = {
888 0x20, 0x38, 0x38
889 };
890
891 std::vector<std::uint16_t> bottom = {
892 0x1000, 0x2000, 0x3000
893 };
894
895 std::vector<std::uint16_t> top = {
896 0x3000, 0x4000, 0x5000
897 };
898
899 ImagePipelineStack stack;
900 stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB888,
901 std::move(in_data));
902 stack.push_node<ImagePipelineNodeCalibrate>(bottom, top, 0);
903
904 ASSERT_EQ(stack.get_output_width(), 1u);
905 ASSERT_EQ(stack.get_output_height(), 1u);
906 ASSERT_EQ(stack.get_output_row_bytes(), 3u);
907 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
908
909 auto out_data = stack.get_all_data();
910
911 Data expected_data = {
912 // note that we don't handle rounding properly in the implementation
913 0x80, 0xc1, 0x41
914 };
915
916 ASSERT_EQ(out_data, expected_data);
917 }
918
test_node_calibrate_16bit()919 void test_node_calibrate_16bit()
920 {
921 using Data = std::vector<std::uint8_t>;
922
923 Data in_data = {
924 0x00, 0x20, 0x00, 0x38, 0x00, 0x38
925 };
926
927 std::vector<std::uint16_t> bottom = {
928 0x1000, 0x2000, 0x3000
929 };
930
931 std::vector<std::uint16_t> top = {
932 0x3000, 0x4000, 0x5000
933 };
934
935 ImagePipelineStack stack;
936 stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB161616,
937 std::move(in_data));
938 stack.push_node<ImagePipelineNodeCalibrate>(bottom, top, 0);
939
940 ASSERT_EQ(stack.get_output_width(), 1u);
941 ASSERT_EQ(stack.get_output_height(), 1u);
942 ASSERT_EQ(stack.get_output_row_bytes(), 6u);
943 ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616);
944
945 auto out_data = stack.get_all_data();
946
947 Data expected_data = {
948 // note that we don't handle rounding properly in the implementation
949 0x00, 0x80, 0xff, 0xbf, 0x00, 0x40
950 };
951
952 ASSERT_EQ(out_data, expected_data);
953 }
954
test_image_pipeline()955 void test_image_pipeline()
956 {
957 test_image_buffer_exact_reads();
958 test_image_buffer_smaller_reads();
959 test_image_buffer_larger_reads();
960 test_image_buffer_uncapped_remaining_bytes();
961 test_image_buffer_capped_remaining_bytes();
962 test_node_buffered_callable_source();
963 test_node_format_convert();
964 test_node_desegment_1_line();
965 test_node_deinterleave_lines_i8();
966 test_node_deinterleave_lines_rgb888();
967 test_node_swap_16bit_endian();
968 test_node_invert_16_bits();
969 test_node_invert_8_bits();
970 test_node_invert_1_bits();
971 test_node_merge_mono_lines_to_color();
972 test_node_merge_color_to_gray();
973 test_node_split_mono_lines();
974 test_node_component_shift_lines();
975 test_node_pixel_shift_columns_no_switch();
976 test_node_pixel_shift_columns_group_switch_pixel_multiple();
977 test_node_pixel_shift_columns_group_switch_pixel_not_multiple();
978 test_node_pixel_shift_columns_group_switch_pixel_large_offsets_multiple();
979 test_node_pixel_shift_columns_group_switch_pixel_large_offsets_not_multiple();
980 test_node_pixel_shift_lines_2lines();
981 test_node_pixel_shift_lines_4lines();
982 test_node_pixel_shift_columns_compute_max_width();
983 test_node_calibrate_8bit();
984 test_node_calibrate_16bit();
985 }
986
987 } // namespace genesys
988