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