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 31namespace genesys { 32 33 34void 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 61void 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 89void 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 114void 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 140void 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 170void 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 214void 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 245void 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 279void 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 307void 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 335void 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 368void 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 409void 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 442void 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 471void 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 503void 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 534void 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 566void 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 597void 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 628void 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 668void 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 752void 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 775void 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 802void 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 829void 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 856void 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 883void 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 919void 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 955void 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