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