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 #ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H 22 #define BACKEND_GENESYS_IMAGE_PIPELINE_H 23 24 #include "image.h" 25 #include "image_pixel.h" 26 #include "image_buffer.h" 27 28 #include <algorithm> 29 #include <functional> 30 #include <memory> 31 32 namespace genesys { 33 34 class ImagePipelineNode 35 { 36 public: 37 virtual ~ImagePipelineNode(); 38 39 virtual std::size_t get_width() const = 0; 40 virtual std::size_t get_height() const = 0; 41 virtual PixelFormat get_format() const = 0; 42 get_row_bytes() const43 std::size_t get_row_bytes() const 44 { 45 return get_pixel_row_bytes(get_format(), get_width()); 46 } 47 48 virtual bool eof() const = 0; 49 50 // returns true if the row was filled successfully, false otherwise (e.g. if not enough data 51 // was available. 52 virtual bool get_next_row_data(std::uint8_t* out_data) = 0; 53 }; 54 55 // A pipeline node that produces data from a callable 56 class ImagePipelineNodeCallableSource : public ImagePipelineNode 57 { 58 public: 59 using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; 60 ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format, ProducerCallback producer)61 ImagePipelineNodeCallableSource(std::size_t width, std::size_t height, PixelFormat format, 62 ProducerCallback producer) : 63 producer_{producer}, 64 width_{width}, 65 height_{height}, 66 format_{format} 67 {} 68 69 std::size_t get_width() const override { return width_; } 70 std::size_t get_height() const override { return height_; } 71 PixelFormat get_format() const override { return format_; } 72 73 bool eof() const override { return eof_; } 74 75 bool get_next_row_data(std::uint8_t* out_data) override; 76 77 private: 78 ProducerCallback producer_; 79 std::size_t width_ = 0; 80 std::size_t height_ = 0; 81 PixelFormat format_ = PixelFormat::UNKNOWN; 82 bool eof_ = false; 83 }; 84 85 // A pipeline node that produces data from a callable requesting fixed-size chunks. 86 class ImagePipelineNodeBufferedCallableSource : public ImagePipelineNode 87 { 88 public: 89 using ProducerCallback = std::function<bool(std::size_t size, std::uint8_t* out_data)>; 90 91 ImagePipelineNodeBufferedCallableSource(std::size_t width, std::size_t height, 92 PixelFormat format, std::size_t input_batch_size, 93 ProducerCallback producer); 94 95 std::size_t get_width() const override { return width_; } 96 std::size_t get_height() const override { return height_; } 97 PixelFormat get_format() const override { return format_; } 98 99 bool eof() const override { return eof_; } 100 101 bool get_next_row_data(std::uint8_t* out_data) override; 102 remaining_bytes() const103 std::size_t remaining_bytes() const { return buffer_.remaining_size(); } set_remaining_bytes(std::size_t bytes)104 void set_remaining_bytes(std::size_t bytes) { buffer_.set_remaining_size(bytes); } set_last_read_multiple(std::size_t bytes)105 void set_last_read_multiple(std::size_t bytes) { buffer_.set_last_read_multiple(bytes); } 106 107 private: 108 ProducerCallback producer_; 109 std::size_t width_ = 0; 110 std::size_t height_ = 0; 111 PixelFormat format_ = PixelFormat::UNKNOWN; 112 113 bool eof_ = false; 114 std::size_t curr_row_ = 0; 115 116 ImageBuffer buffer_; 117 }; 118 119 // A pipeline node that produces data from the given array. 120 class ImagePipelineNodeArraySource : public ImagePipelineNode 121 { 122 public: 123 ImagePipelineNodeArraySource(std::size_t width, std::size_t height, PixelFormat format, 124 std::vector<std::uint8_t> data); 125 126 std::size_t get_width() const override { return width_; } 127 std::size_t get_height() const override { return height_; } 128 PixelFormat get_format() const override { return format_; } 129 130 bool eof() const override { return eof_; } 131 132 bool get_next_row_data(std::uint8_t* out_data) override; 133 134 private: 135 std::size_t width_ = 0; 136 std::size_t height_ = 0; 137 PixelFormat format_ = PixelFormat::UNKNOWN; 138 139 bool eof_ = false; 140 141 std::vector<std::uint8_t> data_; 142 std::size_t next_row_ = 0; 143 }; 144 145 146 /// A pipeline node that produces data from the given image 147 class ImagePipelineNodeImageSource : public ImagePipelineNode 148 { 149 public: 150 ImagePipelineNodeImageSource(const Image& source); 151 152 std::size_t get_width() const override { return source_.get_width(); } 153 std::size_t get_height() const override { return source_.get_height(); } 154 PixelFormat get_format() const override { return source_.get_format(); } 155 156 bool eof() const override { return next_row_ >= get_height(); } 157 158 bool get_next_row_data(std::uint8_t* out_data) override; 159 160 private: 161 const Image& source_; 162 std::size_t next_row_ = 0; 163 }; 164 165 // A pipeline node that converts between pixel formats 166 class ImagePipelineNodeFormatConvert : public ImagePipelineNode 167 { 168 public: ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format)169 ImagePipelineNodeFormatConvert(ImagePipelineNode& source, PixelFormat dst_format) : 170 source_(source), 171 dst_format_{dst_format} 172 {} 173 174 ~ImagePipelineNodeFormatConvert() override = default; 175 176 std::size_t get_width() const override { return source_.get_width(); } 177 std::size_t get_height() const override { return source_.get_height(); } 178 PixelFormat get_format() const override { return dst_format_; } 179 180 bool eof() const override { return source_.eof(); } 181 182 bool get_next_row_data(std::uint8_t* out_data) override; 183 184 private: 185 ImagePipelineNode& source_; 186 PixelFormat dst_format_; 187 std::vector<std::uint8_t> buffer_; 188 }; 189 190 // A pipeline node that handles data that comes out of segmented sensors. Note that the width of 191 // the output data does not necessarily match the input data width, because in many cases almost 192 // all width of the image needs to be read in order to desegment it. 193 class ImagePipelineNodeDesegment : public ImagePipelineNode 194 { 195 public: 196 ImagePipelineNodeDesegment(ImagePipelineNode& source, 197 std::size_t output_width, 198 const std::vector<unsigned>& segment_order, 199 std::size_t segment_pixels, 200 std::size_t interleaved_lines, 201 std::size_t pixels_per_chunk); 202 203 ImagePipelineNodeDesegment(ImagePipelineNode& source, 204 std::size_t output_width, 205 std::size_t segment_count, 206 std::size_t segment_pixels, 207 std::size_t interleaved_lines, 208 std::size_t pixels_per_chunk); 209 210 ~ImagePipelineNodeDesegment() override = default; 211 212 std::size_t get_width() const override { return output_width_; } 213 std::size_t get_height() const override { return source_.get_height() / interleaved_lines_; } 214 PixelFormat get_format() const override { return source_.get_format(); } 215 216 bool eof() const override { return source_.eof(); } 217 218 bool get_next_row_data(std::uint8_t* out_data) override; 219 220 private: 221 ImagePipelineNode& source_; 222 std::size_t output_width_; 223 std::vector<unsigned> segment_order_; 224 std::size_t segment_pixels_ = 0; 225 std::size_t interleaved_lines_ = 0; 226 std::size_t pixels_per_chunk_ = 0; 227 228 RowBuffer buffer_; 229 }; 230 231 // A pipeline node that deinterleaves data on multiple lines 232 class ImagePipelineNodeDeinterleaveLines : public ImagePipelineNodeDesegment 233 { 234 public: 235 ImagePipelineNodeDeinterleaveLines(ImagePipelineNode& source, 236 std::size_t interleaved_lines, 237 std::size_t pixels_per_chunk); 238 }; 239 240 // A pipeline that swaps bytes in 16-bit components and does nothing otherwise. 241 class ImagePipelineNodeSwap16BitEndian : public ImagePipelineNode 242 { 243 public: 244 ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source); 245 246 std::size_t get_width() const override { return source_.get_width(); } 247 std::size_t get_height() const override { return source_.get_height(); } 248 PixelFormat get_format() const override { return source_.get_format(); } 249 250 bool eof() const override { return source_.eof(); } 251 252 bool get_next_row_data(std::uint8_t* out_data) override; 253 254 private: 255 ImagePipelineNode& source_; 256 bool needs_swapping_ = false; 257 }; 258 259 class ImagePipelineNodeInvert : public ImagePipelineNode 260 { 261 public: 262 ImagePipelineNodeInvert(ImagePipelineNode& source); 263 264 std::size_t get_width() const override { return source_.get_width(); } 265 std::size_t get_height() const override { return source_.get_height(); } 266 PixelFormat get_format() const override { return source_.get_format(); } 267 268 bool eof() const override { return source_.eof(); } 269 270 bool get_next_row_data(std::uint8_t* out_data) override; 271 272 private: 273 ImagePipelineNode& source_; 274 }; 275 276 // A pipeline node that merges 3 mono lines into a color channel 277 class ImagePipelineNodeMergeMonoLinesToColor : public ImagePipelineNode 278 { 279 public: 280 ImagePipelineNodeMergeMonoLinesToColor(ImagePipelineNode& source, 281 ColorOrder color_order); 282 283 std::size_t get_width() const override { return source_.get_width(); } 284 std::size_t get_height() const override { return source_.get_height() / 3; } 285 PixelFormat get_format() const override { return output_format_; } 286 287 bool eof() const override { return source_.eof(); } 288 289 bool get_next_row_data(std::uint8_t* out_data) override; 290 291 private: 292 static PixelFormat get_output_format(PixelFormat input_format, ColorOrder order); 293 294 ImagePipelineNode& source_; 295 PixelFormat output_format_ = PixelFormat::UNKNOWN; 296 297 RowBuffer buffer_; 298 }; 299 300 // A pipeline node that splits a color channel into 3 mono lines 301 class ImagePipelineNodeSplitMonoLines : public ImagePipelineNode 302 { 303 public: 304 ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source); 305 306 std::size_t get_width() const override { return source_.get_width(); } 307 std::size_t get_height() const override { return source_.get_height() * 3; } 308 PixelFormat get_format() const override { return output_format_; } 309 310 bool eof() const override { return source_.eof(); } 311 312 bool get_next_row_data(std::uint8_t* out_data) override; 313 314 private: 315 static PixelFormat get_output_format(PixelFormat input_format); 316 317 ImagePipelineNode& source_; 318 PixelFormat output_format_ = PixelFormat::UNKNOWN; 319 320 std::vector<std::uint8_t> buffer_; 321 unsigned next_channel_ = 0; 322 }; 323 324 325 // A pipeline node that merges 3 mono lines into a gray channel 326 class ImagePipelineNodeMergeColorToGray : public ImagePipelineNode 327 { 328 public: 329 ImagePipelineNodeMergeColorToGray(ImagePipelineNode& source); 330 331 std::size_t get_width() const override { return source_.get_width(); } 332 std::size_t get_height() const override { return source_.get_height(); } 333 PixelFormat get_format() const override { return output_format_; } 334 335 bool eof() const override { return source_.eof(); } 336 337 bool get_next_row_data(std::uint8_t* out_data) override; 338 339 private: 340 static PixelFormat get_output_format(PixelFormat input_format); 341 342 ImagePipelineNode& source_; 343 PixelFormat output_format_ = PixelFormat::UNKNOWN; 344 float ch0_mult_ = 0; 345 float ch1_mult_ = 0; 346 float ch2_mult_ = 0; 347 348 std::vector<std::uint8_t> temp_buffer_; 349 }; 350 351 // A pipeline node that shifts colors across lines by the given offsets 352 class ImagePipelineNodeComponentShiftLines : public ImagePipelineNode 353 { 354 public: 355 ImagePipelineNodeComponentShiftLines(ImagePipelineNode& source, 356 unsigned shift_r, unsigned shift_g, unsigned shift_b); 357 358 std::size_t get_width() const override { return source_.get_width(); } 359 std::size_t get_height() const override { return height_; } 360 PixelFormat get_format() const override { return source_.get_format(); } 361 362 bool eof() const override { return source_.eof(); } 363 364 bool get_next_row_data(std::uint8_t* out_data) override; 365 366 private: 367 ImagePipelineNode& source_; 368 std::size_t extra_height_ = 0; 369 std::size_t height_ = 0; 370 371 std::array<unsigned, 3> channel_shifts_; 372 373 RowBuffer buffer_; 374 }; 375 376 // A pipeline node that shifts pixels across lines by the given offsets (performs vertical 377 // unstaggering) 378 class ImagePipelineNodePixelShiftLines : public ImagePipelineNode 379 { 380 public: 381 ImagePipelineNodePixelShiftLines(ImagePipelineNode& source, 382 const std::vector<std::size_t>& shifts); 383 384 std::size_t get_width() const override { return source_.get_width(); } 385 std::size_t get_height() const override { return height_; } 386 PixelFormat get_format() const override { return source_.get_format(); } 387 388 bool eof() const override { return source_.eof(); } 389 390 bool get_next_row_data(std::uint8_t* out_data) override; 391 392 private: 393 ImagePipelineNode& source_; 394 std::size_t extra_height_ = 0; 395 std::size_t height_ = 0; 396 397 std::vector<std::size_t> pixel_shifts_; 398 399 RowBuffer buffer_; 400 }; 401 402 // A pipeline node that shifts pixels across columns by the given offsets. Each row is divided 403 // into pixel groups of shifts.size() pixels. For each output group starting at position xgroup, 404 // the i-th pixel will be set to the input pixel at position xgroup + shifts[i]. 405 class ImagePipelineNodePixelShiftColumns : public ImagePipelineNode 406 { 407 public: 408 ImagePipelineNodePixelShiftColumns(ImagePipelineNode& source, 409 const std::vector<std::size_t>& shifts); 410 411 std::size_t get_width() const override { return width_; } 412 std::size_t get_height() const override { return source_.get_height(); } 413 PixelFormat get_format() const override { return source_.get_format(); } 414 415 bool eof() const override { return source_.eof(); } 416 417 bool get_next_row_data(std::uint8_t* out_data) override; 418 419 private: 420 ImagePipelineNode& source_; 421 std::size_t width_ = 0; 422 std::size_t extra_width_ = 0; 423 424 std::vector<std::size_t> pixel_shifts_; 425 426 std::vector<std::uint8_t> temp_buffer_; 427 }; 428 429 // exposed for tests 430 std::size_t compute_pixel_shift_extra_width(std::size_t source_width, 431 const std::vector<std::size_t>& shifts); 432 433 // A pipeline node that extracts a sub-image from the image. Padding and cropping is done as needed. 434 // The class can't pad to the left of the image currently, as only positive offsets are accepted. 435 class ImagePipelineNodeExtract : public ImagePipelineNode 436 { 437 public: 438 ImagePipelineNodeExtract(ImagePipelineNode& source, 439 std::size_t offset_x, std::size_t offset_y, 440 std::size_t width, std::size_t height); 441 442 ~ImagePipelineNodeExtract() override; 443 444 std::size_t get_width() const override { return width_; } 445 std::size_t get_height() const override { return height_; } 446 PixelFormat get_format() const override { return source_.get_format(); } 447 448 bool eof() const override { return source_.eof(); } 449 450 bool get_next_row_data(std::uint8_t* out_data) override; 451 452 private: 453 ImagePipelineNode& source_; 454 std::size_t offset_x_ = 0; 455 std::size_t offset_y_ = 0; 456 std::size_t width_ = 0; 457 std::size_t height_ = 0; 458 459 std::size_t current_line_ = 0; 460 std::vector<std::uint8_t> cached_line_; 461 }; 462 463 // A pipeline node that scales rows to the specified width by using a point filter 464 class ImagePipelineNodeScaleRows : public ImagePipelineNode 465 { 466 public: 467 ImagePipelineNodeScaleRows(ImagePipelineNode& source, std::size_t width); 468 469 std::size_t get_width() const override { return width_; } 470 std::size_t get_height() const override { return source_.get_height(); } 471 PixelFormat get_format() const override { return source_.get_format(); } 472 473 bool eof() const override { return source_.eof(); } 474 475 bool get_next_row_data(std::uint8_t* out_data) override; 476 477 private: 478 ImagePipelineNode& source_; 479 std::size_t width_ = 0; 480 481 std::vector<std::uint8_t> cached_line_; 482 }; 483 484 // A pipeline node that mimics the calibration behavior on Genesys chips 485 class ImagePipelineNodeCalibrate : public ImagePipelineNode 486 { 487 public: 488 489 ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom, 490 const std::vector<std::uint16_t>& top, std::size_t x_start); 491 492 std::size_t get_width() const override { return source_.get_width(); } 493 std::size_t get_height() const override { return source_.get_height(); } 494 PixelFormat get_format() const override { return source_.get_format(); } 495 496 bool eof() const override { return source_.eof(); } 497 498 bool get_next_row_data(std::uint8_t* out_data) override; 499 500 private: 501 ImagePipelineNode& source_; 502 503 std::vector<float> offset_; 504 std::vector<float> multiplier_; 505 }; 506 507 class ImagePipelineNodeDebug : public ImagePipelineNode 508 { 509 public: 510 ImagePipelineNodeDebug(ImagePipelineNode& source, const std::string& path); 511 ~ImagePipelineNodeDebug() override; 512 513 std::size_t get_width() const override { return source_.get_width(); } 514 std::size_t get_height() const override { return source_.get_height(); } 515 PixelFormat get_format() const override { return source_.get_format(); } 516 517 bool eof() const override { return source_.eof(); } 518 519 bool get_next_row_data(std::uint8_t* out_data) override; 520 521 private: 522 ImagePipelineNode& source_; 523 std::string path_; 524 RowBuffer buffer_; 525 }; 526 527 class ImagePipelineStack 528 { 529 public: ImagePipelineStack()530 ImagePipelineStack() {} ImagePipelineStack(ImagePipelineStack&& other)531 ImagePipelineStack(ImagePipelineStack&& other) 532 { 533 clear(); 534 nodes_ = std::move(other.nodes_); 535 } 536 operator =(ImagePipelineStack&& other)537 ImagePipelineStack& operator=(ImagePipelineStack&& other) 538 { 539 clear(); 540 nodes_ = std::move(other.nodes_); 541 return *this; 542 } 543 ~ImagePipelineStack()544 ~ImagePipelineStack() { clear(); } 545 546 std::size_t get_input_width() const; 547 std::size_t get_input_height() const; 548 PixelFormat get_input_format() const; 549 std::size_t get_input_row_bytes() const; 550 551 std::size_t get_output_width() const; 552 std::size_t get_output_height() const; 553 PixelFormat get_output_format() const; 554 std::size_t get_output_row_bytes() const; 555 front()556 ImagePipelineNode& front() { return *(nodes_.front().get()); } 557 eof() const558 bool eof() const { return nodes_.back()->eof(); } 559 560 void clear(); 561 562 template<class Node, class... Args> push_first_node(Args&&.... args)563 Node& push_first_node(Args&&... args) 564 { 565 if (!nodes_.empty()) { 566 throw SaneException("Trying to append first node when there are existing nodes"); 567 } 568 nodes_.emplace_back(std::unique_ptr<Node>(new Node(std::forward<Args>(args)...))); 569 return static_cast<Node&>(*nodes_.back()); 570 } 571 572 template<class Node, class... Args> push_node(Args&&.... args)573 Node& push_node(Args&&... args) 574 { 575 ensure_node_exists(); 576 nodes_.emplace_back(std::unique_ptr<Node>(new Node(*nodes_.back(), 577 std::forward<Args>(args)...))); 578 return static_cast<Node&>(*nodes_.back()); 579 } 580 get_next_row_data(std::uint8_t* out_data)581 bool get_next_row_data(std::uint8_t* out_data) 582 { 583 return nodes_.back()->get_next_row_data(out_data); 584 } 585 586 std::vector<std::uint8_t> get_all_data(); 587 588 Image get_image(); 589 590 private: 591 void ensure_node_exists() const; 592 593 std::vector<std::unique_ptr<ImagePipelineNode>> nodes_; 594 }; 595 596 } // namespace genesys 597 598 #endif // ifndef BACKEND_GENESYS_IMAGE_PIPELINE_H 599