/* sane - Scanner Access Now Easy. Copyright (C) 2019 Povilas Kanapickas This file is part of the SANE package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #define DEBUG_DECLARE_ONLY #include "image.h" #if defined(HAVE_TIFFIO_H) #include #endif #include namespace genesys { Image::Image() = default; Image::Image(std::size_t width, std::size_t height, PixelFormat format) : width_{width}, height_{height}, format_{format}, row_bytes_{get_pixel_row_bytes(format_, width_)} { data_.resize(get_row_bytes() * height); } std::uint8_t* Image::get_row_ptr(std::size_t y) { return data_.data() + row_bytes_ * y; } const std::uint8_t* Image::get_row_ptr(std::size_t y) const { return data_.data() + row_bytes_ * y; } Pixel Image::get_pixel(std::size_t x, std::size_t y) const { return get_pixel_from_row(get_row_ptr(y), x, format_); } void Image::set_pixel(std::size_t x, std::size_t y, const Pixel& pixel) { set_pixel_to_row(get_row_ptr(y), x, pixel, format_); } RawPixel Image::get_raw_pixel(std::size_t x, std::size_t y) const { return get_raw_pixel_from_row(get_row_ptr(y), x, format_); } std::uint16_t Image::get_raw_channel(std::size_t x, std::size_t y, unsigned channel) const { return get_raw_channel_from_row(get_row_ptr(y), x, channel, format_); } void Image::set_raw_pixel(std::size_t x, std::size_t y, const RawPixel& pixel) { set_raw_pixel_to_row(get_row_ptr(y), x, pixel, format_); } void Image::resize(std::size_t width, std::size_t height, PixelFormat format) { width_ = width; height_ = height; format_ = format; row_bytes_ = get_pixel_row_bytes(format_, width_); data_.resize(get_row_bytes() * height); } template void convert_pixel_row_impl2(const std::uint8_t* in_data, std::uint8_t* out_data, std::size_t count) { for (std::size_t i = 0; i < count; ++i) { Pixel pixel = get_pixel_from_row(in_data, i, SrcFormat); set_pixel_to_row(out_data, i, pixel, DstFormat); } } template void convert_pixel_row_impl(const std::uint8_t* in_data, std::uint8_t* out_data, PixelFormat out_format, std::size_t count) { switch (out_format) { case PixelFormat::I1: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB111: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::I8: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB888: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::BGR888: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::I16: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::RGB161616: { convert_pixel_row_impl2(in_data, out_data, count); return; } case PixelFormat::BGR161616: { convert_pixel_row_impl2(in_data, out_data, count); return; } default: throw SaneException("Unknown pixel format %d", static_cast(out_format)); } } void 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) { if (in_format == out_format) { std::memcpy(out_data, in_data, get_pixel_row_bytes(in_format, count)); return; } switch (in_format) { case PixelFormat::I1: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB111: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::I8: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB888: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::BGR888: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::I16: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::RGB161616: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } case PixelFormat::BGR161616: { convert_pixel_row_impl(in_data, out_data, out_format, count); return; } default: throw SaneException("Unknown pixel format %d", static_cast(in_format)); } } void write_tiff_file(const std::string& filename, const void* data, int depth, int channels, int pixels_per_line, int lines) { DBG_HELPER_ARGS(dbg, "depth=%d, channels=%d, ppl=%d, lines=%d", depth, channels, pixels_per_line, lines); #if defined(HAVE_TIFFIO_H) auto image = TIFFOpen(filename.c_str(), "w"); if (!image) { dbg.log(DBG_error, "Could not save debug image"); return; } TIFFSetField(image, TIFFTAG_IMAGEWIDTH, pixels_per_line); TIFFSetField(image, TIFFTAG_IMAGELENGTH, lines); TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, depth); TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, channels); if (channels > 1) { TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); } else { TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); } TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(image, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); std::size_t bytes_per_line = (pixels_per_line * channels * depth + 7) / 8; const std::uint8_t* data_ptr = reinterpret_cast(data); // we don't need to handle endian because libtiff will handle that for (int iline = 0; iline < lines; ++iline) { const auto* line_data = data_ptr + bytes_per_line * iline; TIFFWriteScanline(image, const_cast(line_data), iline, 0); } TIFFClose(image); #else dbg.log(DBG_error, "Backend has been built without TIFF library support. " "Debug images will not be saved"); #endif } bool is_supported_write_tiff_file_image_format(PixelFormat format) { switch (format) { case PixelFormat::I1: case PixelFormat::RGB111: case PixelFormat::I8: case PixelFormat::RGB888: case PixelFormat::I16: case PixelFormat::RGB161616: return true; default: return false; } } void write_tiff_file(const std::string& filename, const Image& image) { if (!is_supported_write_tiff_file_image_format(image.get_format())) { throw SaneException("Unsupported format %d", static_cast(image.get_format())); } write_tiff_file(filename, image.get_row_ptr(0), get_pixel_format_depth(image.get_format()), get_pixel_channels(image.get_format()), image.get_width(), image.get_height()); } } // namespace genesys