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