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 #define DEBUG_DECLARE_ONLY
43 
44 #include "image.h"
45 
46 #if defined(HAVE_TIFFIO_H)
47 #include <tiffio.h>
48 #endif
49 
50 #include <array>
51 
52 namespace genesys {
53 
54 Image::Image() = default;
55 
Image(std::size_t width,std::size_t height,PixelFormat format)56 Image::Image(std::size_t width, std::size_t height, PixelFormat format) :
57     width_{width},
58     height_{height},
59     format_{format},
60     row_bytes_{get_pixel_row_bytes(format_, width_)}
61 {
62     data_.resize(get_row_bytes() * height);
63 }
64 
get_row_ptr(std::size_t y)65 std::uint8_t* Image::get_row_ptr(std::size_t y)
66 {
67     return data_.data() + row_bytes_ * y;
68 }
69 
get_row_ptr(std::size_t y) const70 const std::uint8_t* Image::get_row_ptr(std::size_t y) const
71 {
72     return data_.data() + row_bytes_ * y;
73 }
74 
get_pixel(std::size_t x,std::size_t y) const75 Pixel Image::get_pixel(std::size_t x, std::size_t y) const
76 {
77     return get_pixel_from_row(get_row_ptr(y), x, format_);
78 }
79 
set_pixel(std::size_t x,std::size_t y,const Pixel & pixel)80 void Image::set_pixel(std::size_t x, std::size_t y, const Pixel& pixel)
81 {
82     set_pixel_to_row(get_row_ptr(y), x, pixel, format_);
83 }
84 
get_raw_pixel(std::size_t x,std::size_t y) const85 RawPixel Image::get_raw_pixel(std::size_t x, std::size_t y) const
86 {
87     return get_raw_pixel_from_row(get_row_ptr(y), x, format_);
88 }
89 
get_raw_channel(std::size_t x,std::size_t y,unsigned channel) const90 std::uint16_t Image::get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const
91 {
92     return get_raw_channel_from_row(get_row_ptr(y), x, channel, format_);
93 }
94 
set_raw_pixel(std::size_t x,std::size_t y,const RawPixel & pixel)95 void Image::set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel)
96 {
97     set_raw_pixel_to_row(get_row_ptr(y), x, pixel, format_);
98 }
99 
resize(std::size_t width,std::size_t height,PixelFormat format)100 void Image::resize(std::size_t width, std::size_t height, PixelFormat format)
101 {
102     width_ = width;
103     height_ = height;
104     format_ = format;
105     row_bytes_ = get_pixel_row_bytes(format_, width_);
106     data_.resize(get_row_bytes() * height);
107 }
108 
109 template<PixelFormat SrcFormat, PixelFormat DstFormat>
convert_pixel_row_impl2(const std::uint8_t * in_data,std::uint8_t * out_data,std::size_t count)110 void convert_pixel_row_impl2(const std::uint8_t* in_data, std::uint8_t* out_data,
111                              std::size_t count)
112 {
113     for (std::size_t i = 0; i < count; ++i) {
114         Pixel pixel = get_pixel_from_row(in_data, i, SrcFormat);
115         set_pixel_to_row(out_data, i, pixel, DstFormat);
116     }
117 }
118 
119 template<PixelFormat SrcFormat>
convert_pixel_row_impl(const std::uint8_t * in_data,std::uint8_t * out_data,PixelFormat out_format,std::size_t count)120 void convert_pixel_row_impl(const std::uint8_t* in_data, std::uint8_t* out_data,
121                             PixelFormat out_format, std::size_t count)
122 {
123     switch (out_format) {
124         case PixelFormat::I1: {
125             convert_pixel_row_impl2<SrcFormat, PixelFormat::I1>(in_data, out_data, count);
126             return;
127         }
128         case PixelFormat::RGB111: {
129             convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB111>(in_data, out_data, count);
130             return;
131         }
132         case PixelFormat::I8: {
133             convert_pixel_row_impl2<SrcFormat, PixelFormat::I8>(in_data, out_data, count);
134             return;
135         }
136         case PixelFormat::RGB888: {
137             convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB888>(in_data, out_data, count);
138             return;
139         }
140         case PixelFormat::BGR888: {
141             convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR888>(in_data, out_data, count);
142             return;
143         }
144         case PixelFormat::I16: {
145             convert_pixel_row_impl2<SrcFormat, PixelFormat::I16>(in_data, out_data, count);
146             return;
147         }
148         case PixelFormat::RGB161616: {
149             convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB161616>(in_data, out_data, count);
150             return;
151         }
152         case PixelFormat::BGR161616: {
153             convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR161616>(in_data, out_data, count);
154             return;
155         }
156         default:
157             throw SaneException("Unknown pixel format %d", static_cast<unsigned>(out_format));
158     }
159 }
convert_pixel_row_format(const std::uint8_t * in_data,PixelFormat in_format,std::uint8_t * out_data,PixelFormat out_format,std::size_t count)160 void convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format,
161                               std::uint8_t* out_data, PixelFormat out_format, std::size_t count)
162 {
163     if (in_format == out_format) {
164         std::memcpy(out_data, in_data, get_pixel_row_bytes(in_format, count));
165         return;
166     }
167 
168     switch (in_format) {
169         case PixelFormat::I1: {
170             convert_pixel_row_impl<PixelFormat::I1>(in_data, out_data, out_format, count);
171             return;
172         }
173         case PixelFormat::RGB111: {
174             convert_pixel_row_impl<PixelFormat::RGB111>(in_data, out_data, out_format, count);
175             return;
176         }
177         case PixelFormat::I8: {
178             convert_pixel_row_impl<PixelFormat::I8>(in_data, out_data, out_format, count);
179             return;
180         }
181         case PixelFormat::RGB888: {
182             convert_pixel_row_impl<PixelFormat::RGB888>(in_data, out_data, out_format, count);
183             return;
184         }
185         case PixelFormat::BGR888: {
186             convert_pixel_row_impl<PixelFormat::BGR888>(in_data, out_data, out_format, count);
187             return;
188         }
189         case PixelFormat::I16: {
190             convert_pixel_row_impl<PixelFormat::I16>(in_data, out_data, out_format, count);
191             return;
192         }
193         case PixelFormat::RGB161616: {
194             convert_pixel_row_impl<PixelFormat::RGB161616>(in_data, out_data, out_format, count);
195             return;
196         }
197         case PixelFormat::BGR161616: {
198             convert_pixel_row_impl<PixelFormat::BGR161616>(in_data, out_data, out_format, count);
199             return;
200         }
201         default:
202             throw SaneException("Unknown pixel format %d", static_cast<unsigned>(in_format));
203     }
204 }
205 
write_tiff_file(const std::string & filename,const void * data,int depth,int channels,int pixels_per_line,int lines)206 void write_tiff_file(const std::string& filename, const void* data, int depth, int channels,
207                      int pixels_per_line, int lines)
208 {
209     DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels,
210                     pixels_per_line, lines);
211 #if defined(HAVE_TIFFIO_H)
212     auto image = TIFFOpen(filename.c_str(), "w");
213     if (!image) {
214         dbg.log(DBG_error, "Could not save debug image");
215         return;
216     }
217     TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line);
218     TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines);
219     TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth);
220     TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels);
221     if (channels > 1) {
222         TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
223     } else {
224         TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
225     }
226     TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
227     TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
228 
229     std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8;
230     const std::uint8_t* data_ptr = reinterpret_cast<const std::uint8_t*>(data);
231 
232     // we don't need to handle endian because libtiff will handle that
233     for (int iline = 0; iline < lines; ++iline) {
234         const auto* line_data = data_ptr + bytes_per_line * iline;
235         TIFFWriteScanline(image, const_cast<std::uint8_t*>(line_data), iline, 0);
236     }
237     TIFFClose(image);
238 
239 #else
240     dbg.log(DBG_error, "Backend has been built without TIFF library support. "
241             "Debug images will not be saved");
242 #endif
243 }
244 
is_supported_write_tiff_file_image_format(PixelFormat format)245 bool is_supported_write_tiff_file_image_format(PixelFormat format)
246 {
247     switch (format) {
248         case PixelFormat::I1:
249         case PixelFormat::RGB111:
250         case PixelFormat::I8:
251         case PixelFormat::RGB888:
252         case PixelFormat::I16:
253         case PixelFormat::RGB161616:
254             return true;
255         default:
256             return false;
257     }
258 }
259 
write_tiff_file(const std::string & filename,const Image & image)260 void write_tiff_file(const std::string& filename, const Image& image)
261 {
262     if (!is_supported_write_tiff_file_image_format(image.get_format())) {
263         throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format()));
264     }
265 
266     write_tiff_file(filename, image.get_row_ptr(0), get_pixel_format_depth(image.get_format()),
267                     get_pixel_channels(image.get_format()), image.get_width(), image.get_height());
268 }
269 
270 } // namespace genesys
271