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