1141cc406Sopenharmony_ci/* sane - Scanner Access Now Easy.
2141cc406Sopenharmony_ci
3141cc406Sopenharmony_ci   Copyright (C) 2019 Povilas Kanapickas <povilas@radix.lt>
4141cc406Sopenharmony_ci
5141cc406Sopenharmony_ci   This file is part of the SANE package.
6141cc406Sopenharmony_ci
7141cc406Sopenharmony_ci   This program is free software; you can redistribute it and/or
8141cc406Sopenharmony_ci   modify it under the terms of the GNU General Public License as
9141cc406Sopenharmony_ci   published by the Free Software Foundation; either version 2 of the
10141cc406Sopenharmony_ci   License, or (at your option) any later version.
11141cc406Sopenharmony_ci
12141cc406Sopenharmony_ci   This program is distributed in the hope that it will be useful, but
13141cc406Sopenharmony_ci   WITHOUT ANY WARRANTY; without even the implied warranty of
14141cc406Sopenharmony_ci   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15141cc406Sopenharmony_ci   General Public License for more details.
16141cc406Sopenharmony_ci
17141cc406Sopenharmony_ci   You should have received a copy of the GNU General Public License
18141cc406Sopenharmony_ci   along with this program.  If not, see <https://www.gnu.org/licenses/>.
19141cc406Sopenharmony_ci*/
20141cc406Sopenharmony_ci
21141cc406Sopenharmony_ci#define DEBUG_DECLARE_ONLY
22141cc406Sopenharmony_ci
23141cc406Sopenharmony_ci#include "image.h"
24141cc406Sopenharmony_ci
25141cc406Sopenharmony_ci#if defined(HAVE_TIFFIO_H)
26141cc406Sopenharmony_ci#include <tiffio.h>
27141cc406Sopenharmony_ci#endif
28141cc406Sopenharmony_ci
29141cc406Sopenharmony_ci#include <array>
30141cc406Sopenharmony_ci
31141cc406Sopenharmony_cinamespace genesys {
32141cc406Sopenharmony_ci
33141cc406Sopenharmony_ciImage::Image() = default;
34141cc406Sopenharmony_ci
35141cc406Sopenharmony_ciImage::Image(std::size_t width, std::size_t height, PixelFormat format) :
36141cc406Sopenharmony_ci    width_{width},
37141cc406Sopenharmony_ci    height_{height},
38141cc406Sopenharmony_ci    format_{format},
39141cc406Sopenharmony_ci    row_bytes_{get_pixel_row_bytes(format_, width_)}
40141cc406Sopenharmony_ci{
41141cc406Sopenharmony_ci    data_.resize(get_row_bytes() * height);
42141cc406Sopenharmony_ci}
43141cc406Sopenharmony_ci
44141cc406Sopenharmony_cistd::uint8_t* Image::get_row_ptr(std::size_t y)
45141cc406Sopenharmony_ci{
46141cc406Sopenharmony_ci    return data_.data() + row_bytes_ * y;
47141cc406Sopenharmony_ci}
48141cc406Sopenharmony_ci
49141cc406Sopenharmony_ciconst std::uint8_t* Image::get_row_ptr(std::size_t y) const
50141cc406Sopenharmony_ci{
51141cc406Sopenharmony_ci    return data_.data() + row_bytes_ * y;
52141cc406Sopenharmony_ci}
53141cc406Sopenharmony_ci
54141cc406Sopenharmony_ciPixel Image::get_pixel(std::size_t x, std::size_t y) const
55141cc406Sopenharmony_ci{
56141cc406Sopenharmony_ci    return get_pixel_from_row(get_row_ptr(y), x, format_);
57141cc406Sopenharmony_ci}
58141cc406Sopenharmony_ci
59141cc406Sopenharmony_civoid Image::set_pixel(std::size_t x, std::size_t y, const Pixel& pixel)
60141cc406Sopenharmony_ci{
61141cc406Sopenharmony_ci    set_pixel_to_row(get_row_ptr(y), x, pixel, format_);
62141cc406Sopenharmony_ci}
63141cc406Sopenharmony_ci
64141cc406Sopenharmony_ciRawPixel Image::get_raw_pixel(std::size_t x, std::size_t y) const
65141cc406Sopenharmony_ci{
66141cc406Sopenharmony_ci    return get_raw_pixel_from_row(get_row_ptr(y), x, format_);
67141cc406Sopenharmony_ci}
68141cc406Sopenharmony_ci
69141cc406Sopenharmony_cistd::uint16_t Image::get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const
70141cc406Sopenharmony_ci{
71141cc406Sopenharmony_ci    return get_raw_channel_from_row(get_row_ptr(y), x, channel, format_);
72141cc406Sopenharmony_ci}
73141cc406Sopenharmony_ci
74141cc406Sopenharmony_civoid Image::set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel)
75141cc406Sopenharmony_ci{
76141cc406Sopenharmony_ci    set_raw_pixel_to_row(get_row_ptr(y), x, pixel, format_);
77141cc406Sopenharmony_ci}
78141cc406Sopenharmony_ci
79141cc406Sopenharmony_civoid Image::resize(std::size_t width, std::size_t height, PixelFormat format)
80141cc406Sopenharmony_ci{
81141cc406Sopenharmony_ci    width_ = width;
82141cc406Sopenharmony_ci    height_ = height;
83141cc406Sopenharmony_ci    format_ = format;
84141cc406Sopenharmony_ci    row_bytes_ = get_pixel_row_bytes(format_, width_);
85141cc406Sopenharmony_ci    data_.resize(get_row_bytes() * height);
86141cc406Sopenharmony_ci}
87141cc406Sopenharmony_ci
88141cc406Sopenharmony_citemplate<PixelFormat SrcFormat, PixelFormat DstFormat>
89141cc406Sopenharmony_civoid convert_pixel_row_impl2(const std::uint8_t* in_data, std::uint8_t* out_data,
90141cc406Sopenharmony_ci                             std::size_t count)
91141cc406Sopenharmony_ci{
92141cc406Sopenharmony_ci    for (std::size_t i = 0; i < count; ++i) {
93141cc406Sopenharmony_ci        Pixel pixel = get_pixel_from_row(in_data, i, SrcFormat);
94141cc406Sopenharmony_ci        set_pixel_to_row(out_data, i, pixel, DstFormat);
95141cc406Sopenharmony_ci    }
96141cc406Sopenharmony_ci}
97141cc406Sopenharmony_ci
98141cc406Sopenharmony_citemplate<PixelFormat SrcFormat>
99141cc406Sopenharmony_civoid convert_pixel_row_impl(const std::uint8_t* in_data, std::uint8_t* out_data,
100141cc406Sopenharmony_ci                            PixelFormat out_format, std::size_t count)
101141cc406Sopenharmony_ci{
102141cc406Sopenharmony_ci    switch (out_format) {
103141cc406Sopenharmony_ci        case PixelFormat::I1: {
104141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::I1>(in_data, out_data, count);
105141cc406Sopenharmony_ci            return;
106141cc406Sopenharmony_ci        }
107141cc406Sopenharmony_ci        case PixelFormat::RGB111: {
108141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB111>(in_data, out_data, count);
109141cc406Sopenharmony_ci            return;
110141cc406Sopenharmony_ci        }
111141cc406Sopenharmony_ci        case PixelFormat::I8: {
112141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::I8>(in_data, out_data, count);
113141cc406Sopenharmony_ci            return;
114141cc406Sopenharmony_ci        }
115141cc406Sopenharmony_ci        case PixelFormat::RGB888: {
116141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB888>(in_data, out_data, count);
117141cc406Sopenharmony_ci            return;
118141cc406Sopenharmony_ci        }
119141cc406Sopenharmony_ci        case PixelFormat::BGR888: {
120141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR888>(in_data, out_data, count);
121141cc406Sopenharmony_ci            return;
122141cc406Sopenharmony_ci        }
123141cc406Sopenharmony_ci        case PixelFormat::I16: {
124141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::I16>(in_data, out_data, count);
125141cc406Sopenharmony_ci            return;
126141cc406Sopenharmony_ci        }
127141cc406Sopenharmony_ci        case PixelFormat::RGB161616: {
128141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::RGB161616>(in_data, out_data, count);
129141cc406Sopenharmony_ci            return;
130141cc406Sopenharmony_ci        }
131141cc406Sopenharmony_ci        case PixelFormat::BGR161616: {
132141cc406Sopenharmony_ci            convert_pixel_row_impl2<SrcFormat, PixelFormat::BGR161616>(in_data, out_data, count);
133141cc406Sopenharmony_ci            return;
134141cc406Sopenharmony_ci        }
135141cc406Sopenharmony_ci        default:
136141cc406Sopenharmony_ci            throw SaneException("Unknown pixel format %d", static_cast<unsigned>(out_format));
137141cc406Sopenharmony_ci    }
138141cc406Sopenharmony_ci}
139141cc406Sopenharmony_civoid convert_pixel_row_format(const std::uint8_t* in_data, PixelFormat in_format,
140141cc406Sopenharmony_ci                              std::uint8_t* out_data, PixelFormat out_format, std::size_t count)
141141cc406Sopenharmony_ci{
142141cc406Sopenharmony_ci    if (in_format == out_format) {
143141cc406Sopenharmony_ci        std::memcpy(out_data, in_data, get_pixel_row_bytes(in_format, count));
144141cc406Sopenharmony_ci        return;
145141cc406Sopenharmony_ci    }
146141cc406Sopenharmony_ci
147141cc406Sopenharmony_ci    switch (in_format) {
148141cc406Sopenharmony_ci        case PixelFormat::I1: {
149141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::I1>(in_data, out_data, out_format, count);
150141cc406Sopenharmony_ci            return;
151141cc406Sopenharmony_ci        }
152141cc406Sopenharmony_ci        case PixelFormat::RGB111: {
153141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::RGB111>(in_data, out_data, out_format, count);
154141cc406Sopenharmony_ci            return;
155141cc406Sopenharmony_ci        }
156141cc406Sopenharmony_ci        case PixelFormat::I8: {
157141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::I8>(in_data, out_data, out_format, count);
158141cc406Sopenharmony_ci            return;
159141cc406Sopenharmony_ci        }
160141cc406Sopenharmony_ci        case PixelFormat::RGB888: {
161141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::RGB888>(in_data, out_data, out_format, count);
162141cc406Sopenharmony_ci            return;
163141cc406Sopenharmony_ci        }
164141cc406Sopenharmony_ci        case PixelFormat::BGR888: {
165141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::BGR888>(in_data, out_data, out_format, count);
166141cc406Sopenharmony_ci            return;
167141cc406Sopenharmony_ci        }
168141cc406Sopenharmony_ci        case PixelFormat::I16: {
169141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::I16>(in_data, out_data, out_format, count);
170141cc406Sopenharmony_ci            return;
171141cc406Sopenharmony_ci        }
172141cc406Sopenharmony_ci        case PixelFormat::RGB161616: {
173141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::RGB161616>(in_data, out_data, out_format, count);
174141cc406Sopenharmony_ci            return;
175141cc406Sopenharmony_ci        }
176141cc406Sopenharmony_ci        case PixelFormat::BGR161616: {
177141cc406Sopenharmony_ci            convert_pixel_row_impl<PixelFormat::BGR161616>(in_data, out_data, out_format, count);
178141cc406Sopenharmony_ci            return;
179141cc406Sopenharmony_ci        }
180141cc406Sopenharmony_ci        default:
181141cc406Sopenharmony_ci            throw SaneException("Unknown pixel format %d", static_cast<unsigned>(in_format));
182141cc406Sopenharmony_ci    }
183141cc406Sopenharmony_ci}
184141cc406Sopenharmony_ci
185141cc406Sopenharmony_civoid write_tiff_file(const std::string& filename, const void* data, int depth, int channels,
186141cc406Sopenharmony_ci                     int pixels_per_line, int lines)
187141cc406Sopenharmony_ci{
188141cc406Sopenharmony_ci    DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels,
189141cc406Sopenharmony_ci                    pixels_per_line, lines);
190141cc406Sopenharmony_ci#if defined(HAVE_TIFFIO_H)
191141cc406Sopenharmony_ci    auto image = TIFFOpen(filename.c_str(), "w");
192141cc406Sopenharmony_ci    if (!image) {
193141cc406Sopenharmony_ci        dbg.log(DBG_error, "Could not save debug image");
194141cc406Sopenharmony_ci        return;
195141cc406Sopenharmony_ci    }
196141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line);
197141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines);
198141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth);
199141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels);
200141cc406Sopenharmony_ci    if (channels > 1) {
201141cc406Sopenharmony_ci        TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
202141cc406Sopenharmony_ci    } else {
203141cc406Sopenharmony_ci        TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
204141cc406Sopenharmony_ci    }
205141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
206141cc406Sopenharmony_ci    TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
207141cc406Sopenharmony_ci
208141cc406Sopenharmony_ci    std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8;
209141cc406Sopenharmony_ci    const std::uint8_t* data_ptr = reinterpret_cast<const std::uint8_t*>(data);
210141cc406Sopenharmony_ci
211141cc406Sopenharmony_ci    // we don't need to handle endian because libtiff will handle that
212141cc406Sopenharmony_ci    for (int iline = 0; iline < lines; ++iline) {
213141cc406Sopenharmony_ci        const auto* line_data = data_ptr + bytes_per_line * iline;
214141cc406Sopenharmony_ci        TIFFWriteScanline(image, const_cast<std::uint8_t*>(line_data), iline, 0);
215141cc406Sopenharmony_ci    }
216141cc406Sopenharmony_ci    TIFFClose(image);
217141cc406Sopenharmony_ci
218141cc406Sopenharmony_ci#else
219141cc406Sopenharmony_ci    dbg.log(DBG_error, "Backend has been built without TIFF library support. "
220141cc406Sopenharmony_ci            "Debug images will not be saved");
221141cc406Sopenharmony_ci#endif
222141cc406Sopenharmony_ci}
223141cc406Sopenharmony_ci
224141cc406Sopenharmony_cibool is_supported_write_tiff_file_image_format(PixelFormat format)
225141cc406Sopenharmony_ci{
226141cc406Sopenharmony_ci    switch (format) {
227141cc406Sopenharmony_ci        case PixelFormat::I1:
228141cc406Sopenharmony_ci        case PixelFormat::RGB111:
229141cc406Sopenharmony_ci        case PixelFormat::I8:
230141cc406Sopenharmony_ci        case PixelFormat::RGB888:
231141cc406Sopenharmony_ci        case PixelFormat::I16:
232141cc406Sopenharmony_ci        case PixelFormat::RGB161616:
233141cc406Sopenharmony_ci            return true;
234141cc406Sopenharmony_ci        default:
235141cc406Sopenharmony_ci            return false;
236141cc406Sopenharmony_ci    }
237141cc406Sopenharmony_ci}
238141cc406Sopenharmony_ci
239141cc406Sopenharmony_civoid write_tiff_file(const std::string& filename, const Image& image)
240141cc406Sopenharmony_ci{
241141cc406Sopenharmony_ci    if (!is_supported_write_tiff_file_image_format(image.get_format())) {
242141cc406Sopenharmony_ci        throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format()));
243141cc406Sopenharmony_ci    }
244141cc406Sopenharmony_ci
245141cc406Sopenharmony_ci    write_tiff_file(filename, image.get_row_ptr(0), get_pixel_format_depth(image.get_format()),
246141cc406Sopenharmony_ci                    get_pixel_channels(image.get_format()), image.get_width(), image.get_height());
247141cc406Sopenharmony_ci}
248141cc406Sopenharmony_ci
249141cc406Sopenharmony_ci} // namespace genesys
250