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