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