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