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_pipeline.h" 24141cc406Sopenharmony_ci#include "image.h" 25141cc406Sopenharmony_ci#include "low.h" 26141cc406Sopenharmony_ci#include <cmath> 27141cc406Sopenharmony_ci#include <numeric> 28141cc406Sopenharmony_ci 29141cc406Sopenharmony_cinamespace genesys { 30141cc406Sopenharmony_ci 31141cc406Sopenharmony_ciImagePipelineNode::~ImagePipelineNode() {} 32141cc406Sopenharmony_ci 33141cc406Sopenharmony_cibool ImagePipelineNodeCallableSource::get_next_row_data(std::uint8_t* out_data) 34141cc406Sopenharmony_ci{ 35141cc406Sopenharmony_ci bool got_data = producer_(get_row_bytes(), out_data); 36141cc406Sopenharmony_ci if (!got_data) 37141cc406Sopenharmony_ci eof_ = true; 38141cc406Sopenharmony_ci return got_data; 39141cc406Sopenharmony_ci} 40141cc406Sopenharmony_ci 41141cc406Sopenharmony_ciImagePipelineNodeBufferedCallableSource::ImagePipelineNodeBufferedCallableSource( 42141cc406Sopenharmony_ci std::size_t width, std::size_t height, PixelFormat format, std::size_t input_batch_size, 43141cc406Sopenharmony_ci ProducerCallback producer) : 44141cc406Sopenharmony_ci width_{width}, 45141cc406Sopenharmony_ci height_{height}, 46141cc406Sopenharmony_ci format_{format}, 47141cc406Sopenharmony_ci buffer_{input_batch_size, producer} 48141cc406Sopenharmony_ci{ 49141cc406Sopenharmony_ci buffer_.set_remaining_size(height_ * get_row_bytes()); 50141cc406Sopenharmony_ci} 51141cc406Sopenharmony_ci 52141cc406Sopenharmony_cibool ImagePipelineNodeBufferedCallableSource::get_next_row_data(std::uint8_t* out_data) 53141cc406Sopenharmony_ci{ 54141cc406Sopenharmony_ci if (curr_row_ >= get_height()) { 55141cc406Sopenharmony_ci DBG(DBG_warn, "%s: reading out of bounds. Row %zu, height: %zu\n", __func__, 56141cc406Sopenharmony_ci curr_row_, get_height()); 57141cc406Sopenharmony_ci eof_ = true; 58141cc406Sopenharmony_ci return false; 59141cc406Sopenharmony_ci } 60141cc406Sopenharmony_ci 61141cc406Sopenharmony_ci bool got_data = true; 62141cc406Sopenharmony_ci 63141cc406Sopenharmony_ci got_data &= buffer_.get_data(get_row_bytes(), out_data); 64141cc406Sopenharmony_ci curr_row_++; 65141cc406Sopenharmony_ci if (!got_data) { 66141cc406Sopenharmony_ci eof_ = true; 67141cc406Sopenharmony_ci } 68141cc406Sopenharmony_ci return got_data; 69141cc406Sopenharmony_ci} 70141cc406Sopenharmony_ci 71141cc406Sopenharmony_ciImagePipelineNodeArraySource::ImagePipelineNodeArraySource(std::size_t width, std::size_t height, 72141cc406Sopenharmony_ci PixelFormat format, 73141cc406Sopenharmony_ci std::vector<std::uint8_t> data) : 74141cc406Sopenharmony_ci width_{width}, 75141cc406Sopenharmony_ci height_{height}, 76141cc406Sopenharmony_ci format_{format}, 77141cc406Sopenharmony_ci data_{std::move(data)}, 78141cc406Sopenharmony_ci next_row_{0} 79141cc406Sopenharmony_ci{ 80141cc406Sopenharmony_ci auto size = get_row_bytes() * height_; 81141cc406Sopenharmony_ci if (data_.size() < size) { 82141cc406Sopenharmony_ci throw SaneException("The given array is too small (%zu bytes). Need at least %zu", 83141cc406Sopenharmony_ci data_.size(), size); 84141cc406Sopenharmony_ci } 85141cc406Sopenharmony_ci} 86141cc406Sopenharmony_ci 87141cc406Sopenharmony_cibool ImagePipelineNodeArraySource::get_next_row_data(std::uint8_t* out_data) 88141cc406Sopenharmony_ci{ 89141cc406Sopenharmony_ci if (next_row_ >= height_) { 90141cc406Sopenharmony_ci eof_ = true; 91141cc406Sopenharmony_ci return false; 92141cc406Sopenharmony_ci } 93141cc406Sopenharmony_ci 94141cc406Sopenharmony_ci auto row_bytes = get_row_bytes(); 95141cc406Sopenharmony_ci std::memcpy(out_data, data_.data() + row_bytes * next_row_, row_bytes); 96141cc406Sopenharmony_ci next_row_++; 97141cc406Sopenharmony_ci 98141cc406Sopenharmony_ci return true; 99141cc406Sopenharmony_ci} 100141cc406Sopenharmony_ci 101141cc406Sopenharmony_ci 102141cc406Sopenharmony_ciImagePipelineNodeImageSource::ImagePipelineNodeImageSource(const Image& source) : 103141cc406Sopenharmony_ci source_{source} 104141cc406Sopenharmony_ci{} 105141cc406Sopenharmony_ci 106141cc406Sopenharmony_cibool ImagePipelineNodeImageSource::get_next_row_data(std::uint8_t* out_data) 107141cc406Sopenharmony_ci{ 108141cc406Sopenharmony_ci if (next_row_ >= get_height()) { 109141cc406Sopenharmony_ci return false; 110141cc406Sopenharmony_ci } 111141cc406Sopenharmony_ci std::memcpy(out_data, source_.get_row_ptr(next_row_), get_row_bytes()); 112141cc406Sopenharmony_ci next_row_++; 113141cc406Sopenharmony_ci return true; 114141cc406Sopenharmony_ci} 115141cc406Sopenharmony_ci 116141cc406Sopenharmony_cibool ImagePipelineNodeFormatConvert::get_next_row_data(std::uint8_t* out_data) 117141cc406Sopenharmony_ci{ 118141cc406Sopenharmony_ci auto src_format = source_.get_format(); 119141cc406Sopenharmony_ci if (src_format == dst_format_) { 120141cc406Sopenharmony_ci return source_.get_next_row_data(out_data); 121141cc406Sopenharmony_ci } 122141cc406Sopenharmony_ci 123141cc406Sopenharmony_ci buffer_.clear(); 124141cc406Sopenharmony_ci buffer_.resize(source_.get_row_bytes()); 125141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(buffer_.data()); 126141cc406Sopenharmony_ci 127141cc406Sopenharmony_ci convert_pixel_row_format(buffer_.data(), src_format, out_data, dst_format_, get_width()); 128141cc406Sopenharmony_ci return got_data; 129141cc406Sopenharmony_ci} 130141cc406Sopenharmony_ci 131141cc406Sopenharmony_ciImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source, 132141cc406Sopenharmony_ci std::size_t output_width, 133141cc406Sopenharmony_ci const std::vector<unsigned>& segment_order, 134141cc406Sopenharmony_ci std::size_t segment_pixels, 135141cc406Sopenharmony_ci std::size_t interleaved_lines, 136141cc406Sopenharmony_ci std::size_t pixels_per_chunk) : 137141cc406Sopenharmony_ci source_(source), 138141cc406Sopenharmony_ci output_width_{output_width}, 139141cc406Sopenharmony_ci segment_order_{segment_order}, 140141cc406Sopenharmony_ci segment_pixels_{segment_pixels}, 141141cc406Sopenharmony_ci interleaved_lines_{interleaved_lines}, 142141cc406Sopenharmony_ci pixels_per_chunk_{pixels_per_chunk}, 143141cc406Sopenharmony_ci buffer_{source_.get_row_bytes()} 144141cc406Sopenharmony_ci{ 145141cc406Sopenharmony_ci DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, " 146141cc406Sopenharmony_ci "pixels_per_shunk=%zu", segment_order.size(), segment_pixels, 147141cc406Sopenharmony_ci interleaved_lines, pixels_per_chunk); 148141cc406Sopenharmony_ci 149141cc406Sopenharmony_ci if (source_.get_height() % interleaved_lines_ > 0) { 150141cc406Sopenharmony_ci throw SaneException("Height is not a multiple of the number of lines to interelave %zu/%zu", 151141cc406Sopenharmony_ci source_.get_height(), interleaved_lines_); 152141cc406Sopenharmony_ci } 153141cc406Sopenharmony_ci} 154141cc406Sopenharmony_ci 155141cc406Sopenharmony_ciImagePipelineNodeDesegment::ImagePipelineNodeDesegment(ImagePipelineNode& source, 156141cc406Sopenharmony_ci std::size_t output_width, 157141cc406Sopenharmony_ci std::size_t segment_count, 158141cc406Sopenharmony_ci std::size_t segment_pixels, 159141cc406Sopenharmony_ci std::size_t interleaved_lines, 160141cc406Sopenharmony_ci std::size_t pixels_per_chunk) : 161141cc406Sopenharmony_ci source_(source), 162141cc406Sopenharmony_ci output_width_{output_width}, 163141cc406Sopenharmony_ci segment_pixels_{segment_pixels}, 164141cc406Sopenharmony_ci interleaved_lines_{interleaved_lines}, 165141cc406Sopenharmony_ci pixels_per_chunk_{pixels_per_chunk}, 166141cc406Sopenharmony_ci buffer_{source_.get_row_bytes()} 167141cc406Sopenharmony_ci{ 168141cc406Sopenharmony_ci DBG_HELPER_ARGS(dbg, "segment_count=%zu, segment_size=%zu, interleaved_lines=%zu, " 169141cc406Sopenharmony_ci "pixels_per_shunk=%zu", segment_count, segment_pixels, interleaved_lines, 170141cc406Sopenharmony_ci pixels_per_chunk); 171141cc406Sopenharmony_ci 172141cc406Sopenharmony_ci segment_order_.resize(segment_count); 173141cc406Sopenharmony_ci std::iota(segment_order_.begin(), segment_order_.end(), 0); 174141cc406Sopenharmony_ci} 175141cc406Sopenharmony_ci 176141cc406Sopenharmony_cibool ImagePipelineNodeDesegment::get_next_row_data(std::uint8_t* out_data) 177141cc406Sopenharmony_ci{ 178141cc406Sopenharmony_ci bool got_data = true; 179141cc406Sopenharmony_ci 180141cc406Sopenharmony_ci buffer_.clear(); 181141cc406Sopenharmony_ci for (std::size_t i = 0; i < interleaved_lines_; ++i) { 182141cc406Sopenharmony_ci buffer_.push_back(); 183141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i)); 184141cc406Sopenharmony_ci } 185141cc406Sopenharmony_ci if (!buffer_.is_linear()) { 186141cc406Sopenharmony_ci throw SaneException("Buffer is not linear"); 187141cc406Sopenharmony_ci } 188141cc406Sopenharmony_ci 189141cc406Sopenharmony_ci auto format = get_format(); 190141cc406Sopenharmony_ci auto segment_count = segment_order_.size(); 191141cc406Sopenharmony_ci 192141cc406Sopenharmony_ci const std::uint8_t* in_data = buffer_.get_row_ptr(0); 193141cc406Sopenharmony_ci 194141cc406Sopenharmony_ci std::size_t groups_count = output_width_ / (segment_order_.size() * pixels_per_chunk_); 195141cc406Sopenharmony_ci 196141cc406Sopenharmony_ci for (std::size_t igroup = 0; igroup < groups_count; ++igroup) { 197141cc406Sopenharmony_ci for (std::size_t isegment = 0; isegment < segment_count; ++isegment) { 198141cc406Sopenharmony_ci auto input_offset = igroup * pixels_per_chunk_; 199141cc406Sopenharmony_ci input_offset += segment_pixels_ * segment_order_[isegment]; 200141cc406Sopenharmony_ci auto output_offset = (igroup * segment_count + isegment) * pixels_per_chunk_; 201141cc406Sopenharmony_ci 202141cc406Sopenharmony_ci for (std::size_t ipixel = 0; ipixel < pixels_per_chunk_; ++ipixel) { 203141cc406Sopenharmony_ci auto pixel = get_raw_pixel_from_row(in_data, input_offset + ipixel, format); 204141cc406Sopenharmony_ci set_raw_pixel_to_row(out_data, output_offset + ipixel, pixel, format); 205141cc406Sopenharmony_ci } 206141cc406Sopenharmony_ci } 207141cc406Sopenharmony_ci } 208141cc406Sopenharmony_ci return got_data; 209141cc406Sopenharmony_ci} 210141cc406Sopenharmony_ci 211141cc406Sopenharmony_ciImagePipelineNodeDeinterleaveLines::ImagePipelineNodeDeinterleaveLines( 212141cc406Sopenharmony_ci ImagePipelineNode& source, std::size_t interleaved_lines, std::size_t pixels_per_chunk) : 213141cc406Sopenharmony_ci ImagePipelineNodeDesegment(source, source.get_width() * interleaved_lines, 214141cc406Sopenharmony_ci interleaved_lines, source.get_width(), 215141cc406Sopenharmony_ci interleaved_lines, pixels_per_chunk) 216141cc406Sopenharmony_ci{} 217141cc406Sopenharmony_ci 218141cc406Sopenharmony_ciImagePipelineNodeSwap16BitEndian::ImagePipelineNodeSwap16BitEndian(ImagePipelineNode& source) : 219141cc406Sopenharmony_ci source_(source), 220141cc406Sopenharmony_ci needs_swapping_{false} 221141cc406Sopenharmony_ci{ 222141cc406Sopenharmony_ci if (get_pixel_format_depth(source_.get_format()) == 16) { 223141cc406Sopenharmony_ci needs_swapping_ = true; 224141cc406Sopenharmony_ci } else { 225141cc406Sopenharmony_ci DBG(DBG_info, "%s: this pipeline node does nothing for non 16-bit formats", __func__); 226141cc406Sopenharmony_ci } 227141cc406Sopenharmony_ci} 228141cc406Sopenharmony_ci 229141cc406Sopenharmony_cibool ImagePipelineNodeSwap16BitEndian::get_next_row_data(std::uint8_t* out_data) 230141cc406Sopenharmony_ci{ 231141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(out_data); 232141cc406Sopenharmony_ci if (needs_swapping_) { 233141cc406Sopenharmony_ci std::size_t pixels = get_row_bytes() / 2; 234141cc406Sopenharmony_ci for (std::size_t i = 0; i < pixels; ++i) { 235141cc406Sopenharmony_ci std::swap(*out_data, *(out_data + 1)); 236141cc406Sopenharmony_ci out_data += 2; 237141cc406Sopenharmony_ci } 238141cc406Sopenharmony_ci } 239141cc406Sopenharmony_ci return got_data; 240141cc406Sopenharmony_ci} 241141cc406Sopenharmony_ci 242141cc406Sopenharmony_ciImagePipelineNodeInvert::ImagePipelineNodeInvert(ImagePipelineNode& source) : 243141cc406Sopenharmony_ci source_(source) 244141cc406Sopenharmony_ci{ 245141cc406Sopenharmony_ci} 246141cc406Sopenharmony_ci 247141cc406Sopenharmony_cibool ImagePipelineNodeInvert::get_next_row_data(std::uint8_t* out_data) 248141cc406Sopenharmony_ci{ 249141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(out_data); 250141cc406Sopenharmony_ci auto num_values = get_width() * get_pixel_channels(source_.get_format()); 251141cc406Sopenharmony_ci auto depth = get_pixel_format_depth(source_.get_format()); 252141cc406Sopenharmony_ci 253141cc406Sopenharmony_ci switch (depth) { 254141cc406Sopenharmony_ci case 16: { 255141cc406Sopenharmony_ci auto* data = reinterpret_cast<std::uint16_t*>(out_data); 256141cc406Sopenharmony_ci for (std::size_t i = 0; i < num_values; ++i) { 257141cc406Sopenharmony_ci *data = 0xffff - *data; 258141cc406Sopenharmony_ci data++; 259141cc406Sopenharmony_ci } 260141cc406Sopenharmony_ci break; 261141cc406Sopenharmony_ci } 262141cc406Sopenharmony_ci case 8: { 263141cc406Sopenharmony_ci auto* data = out_data; 264141cc406Sopenharmony_ci for (std::size_t i = 0; i < num_values; ++i) { 265141cc406Sopenharmony_ci *data = 0xff - *data; 266141cc406Sopenharmony_ci data++; 267141cc406Sopenharmony_ci } 268141cc406Sopenharmony_ci break; 269141cc406Sopenharmony_ci } 270141cc406Sopenharmony_ci case 1: { 271141cc406Sopenharmony_ci auto* data = out_data; 272141cc406Sopenharmony_ci auto num_bytes = (num_values + 7) / 8; 273141cc406Sopenharmony_ci for (std::size_t i = 0; i < num_bytes; ++i) { 274141cc406Sopenharmony_ci *data = ~*data; 275141cc406Sopenharmony_ci data++; 276141cc406Sopenharmony_ci } 277141cc406Sopenharmony_ci break; 278141cc406Sopenharmony_ci } 279141cc406Sopenharmony_ci default: 280141cc406Sopenharmony_ci throw SaneException("Unsupported pixel depth"); 281141cc406Sopenharmony_ci } 282141cc406Sopenharmony_ci 283141cc406Sopenharmony_ci return got_data; 284141cc406Sopenharmony_ci} 285141cc406Sopenharmony_ci 286141cc406Sopenharmony_ciImagePipelineNodeMergeMonoLinesToColor::ImagePipelineNodeMergeMonoLinesToColor( 287141cc406Sopenharmony_ci ImagePipelineNode& source, ColorOrder color_order) : 288141cc406Sopenharmony_ci source_(source), 289141cc406Sopenharmony_ci buffer_(source_.get_row_bytes()) 290141cc406Sopenharmony_ci{ 291141cc406Sopenharmony_ci DBG_HELPER_ARGS(dbg, "color_order %d", static_cast<unsigned>(color_order)); 292141cc406Sopenharmony_ci 293141cc406Sopenharmony_ci output_format_ = get_output_format(source_.get_format(), color_order); 294141cc406Sopenharmony_ci} 295141cc406Sopenharmony_ci 296141cc406Sopenharmony_cibool ImagePipelineNodeMergeMonoLinesToColor::get_next_row_data(std::uint8_t* out_data) 297141cc406Sopenharmony_ci{ 298141cc406Sopenharmony_ci bool got_data = true; 299141cc406Sopenharmony_ci 300141cc406Sopenharmony_ci buffer_.clear(); 301141cc406Sopenharmony_ci for (unsigned i = 0; i < 3; ++i) { 302141cc406Sopenharmony_ci buffer_.push_back(); 303141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(buffer_.get_row_ptr(i)); 304141cc406Sopenharmony_ci } 305141cc406Sopenharmony_ci 306141cc406Sopenharmony_ci const auto* row0 = buffer_.get_row_ptr(0); 307141cc406Sopenharmony_ci const auto* row1 = buffer_.get_row_ptr(1); 308141cc406Sopenharmony_ci const auto* row2 = buffer_.get_row_ptr(2); 309141cc406Sopenharmony_ci 310141cc406Sopenharmony_ci auto format = source_.get_format(); 311141cc406Sopenharmony_ci 312141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width; ++x) { 313141cc406Sopenharmony_ci std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format); 314141cc406Sopenharmony_ci std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 0, format); 315141cc406Sopenharmony_ci std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 0, format); 316141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 0, ch0, output_format_); 317141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 1, ch1, output_format_); 318141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 2, ch2, output_format_); 319141cc406Sopenharmony_ci } 320141cc406Sopenharmony_ci return got_data; 321141cc406Sopenharmony_ci} 322141cc406Sopenharmony_ci 323141cc406Sopenharmony_ciPixelFormat ImagePipelineNodeMergeMonoLinesToColor::get_output_format(PixelFormat input_format, 324141cc406Sopenharmony_ci ColorOrder order) 325141cc406Sopenharmony_ci{ 326141cc406Sopenharmony_ci switch (input_format) { 327141cc406Sopenharmony_ci case PixelFormat::I1: { 328141cc406Sopenharmony_ci if (order == ColorOrder::RGB) { 329141cc406Sopenharmony_ci return PixelFormat::RGB111; 330141cc406Sopenharmony_ci } 331141cc406Sopenharmony_ci break; 332141cc406Sopenharmony_ci } 333141cc406Sopenharmony_ci case PixelFormat::I8: { 334141cc406Sopenharmony_ci if (order == ColorOrder::RGB) { 335141cc406Sopenharmony_ci return PixelFormat::RGB888; 336141cc406Sopenharmony_ci } 337141cc406Sopenharmony_ci if (order == ColorOrder::BGR) { 338141cc406Sopenharmony_ci return PixelFormat::BGR888; 339141cc406Sopenharmony_ci } 340141cc406Sopenharmony_ci break; 341141cc406Sopenharmony_ci } 342141cc406Sopenharmony_ci case PixelFormat::I16: { 343141cc406Sopenharmony_ci if (order == ColorOrder::RGB) { 344141cc406Sopenharmony_ci return PixelFormat::RGB161616; 345141cc406Sopenharmony_ci } 346141cc406Sopenharmony_ci if (order == ColorOrder::BGR) { 347141cc406Sopenharmony_ci return PixelFormat::BGR161616; 348141cc406Sopenharmony_ci } 349141cc406Sopenharmony_ci break; 350141cc406Sopenharmony_ci } 351141cc406Sopenharmony_ci default: break; 352141cc406Sopenharmony_ci } 353141cc406Sopenharmony_ci throw SaneException("Unsupported format combidation %d %d", 354141cc406Sopenharmony_ci static_cast<unsigned>(input_format), 355141cc406Sopenharmony_ci static_cast<unsigned>(order)); 356141cc406Sopenharmony_ci} 357141cc406Sopenharmony_ci 358141cc406Sopenharmony_ciImagePipelineNodeSplitMonoLines::ImagePipelineNodeSplitMonoLines(ImagePipelineNode& source) : 359141cc406Sopenharmony_ci source_(source), 360141cc406Sopenharmony_ci next_channel_{0} 361141cc406Sopenharmony_ci{ 362141cc406Sopenharmony_ci output_format_ = get_output_format(source_.get_format()); 363141cc406Sopenharmony_ci} 364141cc406Sopenharmony_ci 365141cc406Sopenharmony_cibool ImagePipelineNodeSplitMonoLines::get_next_row_data(std::uint8_t* out_data) 366141cc406Sopenharmony_ci{ 367141cc406Sopenharmony_ci bool got_data = true; 368141cc406Sopenharmony_ci 369141cc406Sopenharmony_ci if (next_channel_ == 0) { 370141cc406Sopenharmony_ci buffer_.resize(source_.get_row_bytes()); 371141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(buffer_.data()); 372141cc406Sopenharmony_ci } 373141cc406Sopenharmony_ci 374141cc406Sopenharmony_ci const auto* row = buffer_.data(); 375141cc406Sopenharmony_ci auto format = source_.get_format(); 376141cc406Sopenharmony_ci 377141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width; ++x) { 378141cc406Sopenharmony_ci std::uint16_t ch = get_raw_channel_from_row(row, x, next_channel_, format); 379141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 0, ch, output_format_); 380141cc406Sopenharmony_ci } 381141cc406Sopenharmony_ci next_channel_ = (next_channel_ + 1) % 3; 382141cc406Sopenharmony_ci 383141cc406Sopenharmony_ci return got_data; 384141cc406Sopenharmony_ci} 385141cc406Sopenharmony_ci 386141cc406Sopenharmony_ciPixelFormat ImagePipelineNodeSplitMonoLines::get_output_format(PixelFormat input_format) 387141cc406Sopenharmony_ci{ 388141cc406Sopenharmony_ci switch (input_format) { 389141cc406Sopenharmony_ci case PixelFormat::RGB111: return PixelFormat::I1; 390141cc406Sopenharmony_ci case PixelFormat::RGB888: 391141cc406Sopenharmony_ci case PixelFormat::BGR888: return PixelFormat::I8; 392141cc406Sopenharmony_ci case PixelFormat::RGB161616: 393141cc406Sopenharmony_ci case PixelFormat::BGR161616: return PixelFormat::I16; 394141cc406Sopenharmony_ci default: break; 395141cc406Sopenharmony_ci } 396141cc406Sopenharmony_ci throw SaneException("Unsupported input format %d", static_cast<unsigned>(input_format)); 397141cc406Sopenharmony_ci} 398141cc406Sopenharmony_ci 399141cc406Sopenharmony_ci 400141cc406Sopenharmony_ciImagePipelineNodeMergeColorToGray::ImagePipelineNodeMergeColorToGray(ImagePipelineNode& source) : 401141cc406Sopenharmony_ci source_(source) 402141cc406Sopenharmony_ci{ 403141cc406Sopenharmony_ci 404141cc406Sopenharmony_ci output_format_ = get_output_format(source_.get_format()); 405141cc406Sopenharmony_ci float red_mult = 0.2125f; 406141cc406Sopenharmony_ci float green_mult = 0.7154f; 407141cc406Sopenharmony_ci float blue_mult = 0.0721f; 408141cc406Sopenharmony_ci 409141cc406Sopenharmony_ci switch (get_pixel_format_color_order(source_.get_format())) { 410141cc406Sopenharmony_ci case ColorOrder::RGB: { 411141cc406Sopenharmony_ci ch0_mult_ = red_mult; 412141cc406Sopenharmony_ci ch1_mult_ = green_mult; 413141cc406Sopenharmony_ci ch2_mult_ = blue_mult; 414141cc406Sopenharmony_ci break; 415141cc406Sopenharmony_ci } 416141cc406Sopenharmony_ci case ColorOrder::BGR: { 417141cc406Sopenharmony_ci ch0_mult_ = blue_mult; 418141cc406Sopenharmony_ci ch1_mult_ = green_mult; 419141cc406Sopenharmony_ci ch2_mult_ = red_mult; 420141cc406Sopenharmony_ci break; 421141cc406Sopenharmony_ci } 422141cc406Sopenharmony_ci case ColorOrder::GBR: { 423141cc406Sopenharmony_ci ch0_mult_ = green_mult; 424141cc406Sopenharmony_ci ch1_mult_ = blue_mult; 425141cc406Sopenharmony_ci ch2_mult_ = red_mult; 426141cc406Sopenharmony_ci break; 427141cc406Sopenharmony_ci } 428141cc406Sopenharmony_ci default: 429141cc406Sopenharmony_ci throw SaneException("Unknown color order"); 430141cc406Sopenharmony_ci } 431141cc406Sopenharmony_ci temp_buffer_.resize(source_.get_row_bytes()); 432141cc406Sopenharmony_ci} 433141cc406Sopenharmony_ci 434141cc406Sopenharmony_cibool ImagePipelineNodeMergeColorToGray::get_next_row_data(std::uint8_t* out_data) 435141cc406Sopenharmony_ci{ 436141cc406Sopenharmony_ci auto* src_data = temp_buffer_.data(); 437141cc406Sopenharmony_ci 438141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(src_data); 439141cc406Sopenharmony_ci 440141cc406Sopenharmony_ci auto src_format = source_.get_format(); 441141cc406Sopenharmony_ci 442141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width; ++x) { 443141cc406Sopenharmony_ci std::uint16_t ch0 = get_raw_channel_from_row(src_data, x, 0, src_format); 444141cc406Sopenharmony_ci std::uint16_t ch1 = get_raw_channel_from_row(src_data, x, 1, src_format); 445141cc406Sopenharmony_ci std::uint16_t ch2 = get_raw_channel_from_row(src_data, x, 2, src_format); 446141cc406Sopenharmony_ci float mono = ch0 * ch0_mult_ + ch1 * ch1_mult_ + ch2 * ch2_mult_; 447141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 0, static_cast<std::uint16_t>(mono), output_format_); 448141cc406Sopenharmony_ci } 449141cc406Sopenharmony_ci return got_data; 450141cc406Sopenharmony_ci} 451141cc406Sopenharmony_ci 452141cc406Sopenharmony_ciPixelFormat ImagePipelineNodeMergeColorToGray::get_output_format(PixelFormat input_format) 453141cc406Sopenharmony_ci{ 454141cc406Sopenharmony_ci switch (input_format) { 455141cc406Sopenharmony_ci case PixelFormat::RGB111: 456141cc406Sopenharmony_ci return PixelFormat::I1; 457141cc406Sopenharmony_ci case PixelFormat::RGB888: 458141cc406Sopenharmony_ci case PixelFormat::BGR888: 459141cc406Sopenharmony_ci return PixelFormat::I8; 460141cc406Sopenharmony_ci case PixelFormat::RGB161616: 461141cc406Sopenharmony_ci case PixelFormat::BGR161616: 462141cc406Sopenharmony_ci return PixelFormat::I16; 463141cc406Sopenharmony_ci default: break; 464141cc406Sopenharmony_ci } 465141cc406Sopenharmony_ci throw SaneException("Unsupported format %d", static_cast<unsigned>(input_format)); 466141cc406Sopenharmony_ci} 467141cc406Sopenharmony_ci 468141cc406Sopenharmony_ciImagePipelineNodeComponentShiftLines::ImagePipelineNodeComponentShiftLines( 469141cc406Sopenharmony_ci ImagePipelineNode& source, unsigned shift_r, unsigned shift_g, unsigned shift_b) : 470141cc406Sopenharmony_ci source_(source), 471141cc406Sopenharmony_ci buffer_{source.get_row_bytes()} 472141cc406Sopenharmony_ci{ 473141cc406Sopenharmony_ci DBG_HELPER_ARGS(dbg, "shifts={%d, %d, %d}", shift_r, shift_g, shift_b); 474141cc406Sopenharmony_ci 475141cc406Sopenharmony_ci switch (source.get_format()) { 476141cc406Sopenharmony_ci case PixelFormat::RGB111: 477141cc406Sopenharmony_ci case PixelFormat::RGB888: 478141cc406Sopenharmony_ci case PixelFormat::RGB161616: { 479141cc406Sopenharmony_ci channel_shifts_ = { shift_r, shift_g, shift_b }; 480141cc406Sopenharmony_ci break; 481141cc406Sopenharmony_ci } 482141cc406Sopenharmony_ci case PixelFormat::BGR888: 483141cc406Sopenharmony_ci case PixelFormat::BGR161616: { 484141cc406Sopenharmony_ci channel_shifts_ = { shift_b, shift_g, shift_r }; 485141cc406Sopenharmony_ci break; 486141cc406Sopenharmony_ci } 487141cc406Sopenharmony_ci default: 488141cc406Sopenharmony_ci throw SaneException("Unsupported input format %d", 489141cc406Sopenharmony_ci static_cast<unsigned>(source.get_format())); 490141cc406Sopenharmony_ci } 491141cc406Sopenharmony_ci extra_height_ = *std::max_element(channel_shifts_.begin(), channel_shifts_.end()); 492141cc406Sopenharmony_ci height_ = source_.get_height(); 493141cc406Sopenharmony_ci if (extra_height_ > height_) { 494141cc406Sopenharmony_ci height_ = 0; 495141cc406Sopenharmony_ci } else { 496141cc406Sopenharmony_ci height_ -= extra_height_; 497141cc406Sopenharmony_ci } 498141cc406Sopenharmony_ci} 499141cc406Sopenharmony_ci 500141cc406Sopenharmony_cibool ImagePipelineNodeComponentShiftLines::get_next_row_data(std::uint8_t* out_data) 501141cc406Sopenharmony_ci{ 502141cc406Sopenharmony_ci bool got_data = true; 503141cc406Sopenharmony_ci 504141cc406Sopenharmony_ci if (!buffer_.empty()) { 505141cc406Sopenharmony_ci buffer_.pop_front(); 506141cc406Sopenharmony_ci } 507141cc406Sopenharmony_ci while (buffer_.height() < extra_height_ + 1) { 508141cc406Sopenharmony_ci buffer_.push_back(); 509141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr()); 510141cc406Sopenharmony_ci } 511141cc406Sopenharmony_ci 512141cc406Sopenharmony_ci auto format = get_format(); 513141cc406Sopenharmony_ci const auto* row0 = buffer_.get_row_ptr(channel_shifts_[0]); 514141cc406Sopenharmony_ci const auto* row1 = buffer_.get_row_ptr(channel_shifts_[1]); 515141cc406Sopenharmony_ci const auto* row2 = buffer_.get_row_ptr(channel_shifts_[2]); 516141cc406Sopenharmony_ci 517141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width; ++x) { 518141cc406Sopenharmony_ci std::uint16_t ch0 = get_raw_channel_from_row(row0, x, 0, format); 519141cc406Sopenharmony_ci std::uint16_t ch1 = get_raw_channel_from_row(row1, x, 1, format); 520141cc406Sopenharmony_ci std::uint16_t ch2 = get_raw_channel_from_row(row2, x, 2, format); 521141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 0, ch0, format); 522141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 1, ch1, format); 523141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, 2, ch2, format); 524141cc406Sopenharmony_ci } 525141cc406Sopenharmony_ci return got_data; 526141cc406Sopenharmony_ci} 527141cc406Sopenharmony_ci 528141cc406Sopenharmony_ciImagePipelineNodePixelShiftLines::ImagePipelineNodePixelShiftLines( 529141cc406Sopenharmony_ci ImagePipelineNode& source, const std::vector<std::size_t>& shifts) : 530141cc406Sopenharmony_ci source_(source), 531141cc406Sopenharmony_ci pixel_shifts_{shifts}, 532141cc406Sopenharmony_ci buffer_{get_row_bytes()} 533141cc406Sopenharmony_ci{ 534141cc406Sopenharmony_ci extra_height_ = *std::max_element(pixel_shifts_.begin(), pixel_shifts_.end()); 535141cc406Sopenharmony_ci height_ = source_.get_height(); 536141cc406Sopenharmony_ci if (extra_height_ > height_) { 537141cc406Sopenharmony_ci height_ = 0; 538141cc406Sopenharmony_ci } else { 539141cc406Sopenharmony_ci height_ -= extra_height_; 540141cc406Sopenharmony_ci } 541141cc406Sopenharmony_ci} 542141cc406Sopenharmony_ci 543141cc406Sopenharmony_cibool ImagePipelineNodePixelShiftLines::get_next_row_data(std::uint8_t* out_data) 544141cc406Sopenharmony_ci{ 545141cc406Sopenharmony_ci bool got_data = true; 546141cc406Sopenharmony_ci 547141cc406Sopenharmony_ci if (!buffer_.empty()) { 548141cc406Sopenharmony_ci buffer_.pop_front(); 549141cc406Sopenharmony_ci } 550141cc406Sopenharmony_ci while (buffer_.height() < extra_height_ + 1) { 551141cc406Sopenharmony_ci buffer_.push_back(); 552141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(buffer_.get_back_row_ptr()); 553141cc406Sopenharmony_ci } 554141cc406Sopenharmony_ci 555141cc406Sopenharmony_ci auto format = get_format(); 556141cc406Sopenharmony_ci auto shift_count = pixel_shifts_.size(); 557141cc406Sopenharmony_ci 558141cc406Sopenharmony_ci std::vector<std::uint8_t*> rows; 559141cc406Sopenharmony_ci rows.resize(shift_count, nullptr); 560141cc406Sopenharmony_ci 561141cc406Sopenharmony_ci for (std::size_t irow = 0; irow < shift_count; ++irow) { 562141cc406Sopenharmony_ci rows[irow] = buffer_.get_row_ptr(pixel_shifts_[irow]); 563141cc406Sopenharmony_ci } 564141cc406Sopenharmony_ci 565141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width;) { 566141cc406Sopenharmony_ci for (std::size_t irow = 0; irow < shift_count && x < width; irow++, x++) { 567141cc406Sopenharmony_ci RawPixel pixel = get_raw_pixel_from_row(rows[irow], x, format); 568141cc406Sopenharmony_ci set_raw_pixel_to_row(out_data, x, pixel, format); 569141cc406Sopenharmony_ci } 570141cc406Sopenharmony_ci } 571141cc406Sopenharmony_ci return got_data; 572141cc406Sopenharmony_ci} 573141cc406Sopenharmony_ci 574141cc406Sopenharmony_ciImagePipelineNodePixelShiftColumns::ImagePipelineNodePixelShiftColumns( 575141cc406Sopenharmony_ci ImagePipelineNode& source, const std::vector<std::size_t>& shifts) : 576141cc406Sopenharmony_ci source_(source), 577141cc406Sopenharmony_ci pixel_shifts_{shifts} 578141cc406Sopenharmony_ci{ 579141cc406Sopenharmony_ci width_ = source_.get_width(); 580141cc406Sopenharmony_ci extra_width_ = compute_pixel_shift_extra_width(width_, pixel_shifts_); 581141cc406Sopenharmony_ci if (extra_width_ > width_) { 582141cc406Sopenharmony_ci width_ = 0; 583141cc406Sopenharmony_ci } else { 584141cc406Sopenharmony_ci width_ -= extra_width_; 585141cc406Sopenharmony_ci } 586141cc406Sopenharmony_ci temp_buffer_.resize(source_.get_row_bytes()); 587141cc406Sopenharmony_ci} 588141cc406Sopenharmony_ci 589141cc406Sopenharmony_cibool ImagePipelineNodePixelShiftColumns::get_next_row_data(std::uint8_t* out_data) 590141cc406Sopenharmony_ci{ 591141cc406Sopenharmony_ci if (width_ == 0) { 592141cc406Sopenharmony_ci throw SaneException("Attempt to read zero-width line"); 593141cc406Sopenharmony_ci } 594141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(temp_buffer_.data()); 595141cc406Sopenharmony_ci 596141cc406Sopenharmony_ci auto format = get_format(); 597141cc406Sopenharmony_ci auto shift_count = pixel_shifts_.size(); 598141cc406Sopenharmony_ci 599141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width; x += shift_count) { 600141cc406Sopenharmony_ci for (std::size_t ishift = 0; ishift < shift_count && x + ishift < width; ishift++) { 601141cc406Sopenharmony_ci RawPixel pixel = get_raw_pixel_from_row(temp_buffer_.data(), x + pixel_shifts_[ishift], 602141cc406Sopenharmony_ci format); 603141cc406Sopenharmony_ci set_raw_pixel_to_row(out_data, x + ishift, pixel, format); 604141cc406Sopenharmony_ci } 605141cc406Sopenharmony_ci } 606141cc406Sopenharmony_ci return got_data; 607141cc406Sopenharmony_ci} 608141cc406Sopenharmony_ci 609141cc406Sopenharmony_ci 610141cc406Sopenharmony_cistd::size_t compute_pixel_shift_extra_width(std::size_t source_width, 611141cc406Sopenharmony_ci const std::vector<std::size_t>& shifts) 612141cc406Sopenharmony_ci{ 613141cc406Sopenharmony_ci // we iterate across pixel shifts and find the pixel that needs the maximum shift according to 614141cc406Sopenharmony_ci // source_width. 615141cc406Sopenharmony_ci int group_size = shifts.size(); 616141cc406Sopenharmony_ci int non_filled_group = source_width % shifts.size(); 617141cc406Sopenharmony_ci int extra_width = 0; 618141cc406Sopenharmony_ci 619141cc406Sopenharmony_ci for (int i = 0; i < group_size; ++i) { 620141cc406Sopenharmony_ci int shift_groups = shifts[i] / group_size; 621141cc406Sopenharmony_ci int shift_rem = shifts[i] % group_size; 622141cc406Sopenharmony_ci 623141cc406Sopenharmony_ci if (shift_rem < non_filled_group) { 624141cc406Sopenharmony_ci shift_groups--; 625141cc406Sopenharmony_ci } 626141cc406Sopenharmony_ci extra_width = std::max(extra_width, shift_groups * group_size + non_filled_group - i); 627141cc406Sopenharmony_ci } 628141cc406Sopenharmony_ci return extra_width; 629141cc406Sopenharmony_ci} 630141cc406Sopenharmony_ci 631141cc406Sopenharmony_ciImagePipelineNodeExtract::ImagePipelineNodeExtract(ImagePipelineNode& source, 632141cc406Sopenharmony_ci std::size_t offset_x, std::size_t offset_y, 633141cc406Sopenharmony_ci std::size_t width, std::size_t height) : 634141cc406Sopenharmony_ci source_(source), 635141cc406Sopenharmony_ci offset_x_{offset_x}, 636141cc406Sopenharmony_ci offset_y_{offset_y}, 637141cc406Sopenharmony_ci width_{width}, 638141cc406Sopenharmony_ci height_{height} 639141cc406Sopenharmony_ci{ 640141cc406Sopenharmony_ci cached_line_.resize(source_.get_row_bytes()); 641141cc406Sopenharmony_ci} 642141cc406Sopenharmony_ci 643141cc406Sopenharmony_ciImagePipelineNodeExtract::~ImagePipelineNodeExtract() {} 644141cc406Sopenharmony_ci 645141cc406Sopenharmony_ciImagePipelineNodeScaleRows::ImagePipelineNodeScaleRows(ImagePipelineNode& source, 646141cc406Sopenharmony_ci std::size_t width) : 647141cc406Sopenharmony_ci source_(source), 648141cc406Sopenharmony_ci width_{width} 649141cc406Sopenharmony_ci{ 650141cc406Sopenharmony_ci cached_line_.resize(source_.get_row_bytes()); 651141cc406Sopenharmony_ci} 652141cc406Sopenharmony_ci 653141cc406Sopenharmony_cibool ImagePipelineNodeScaleRows::get_next_row_data(std::uint8_t* out_data) 654141cc406Sopenharmony_ci{ 655141cc406Sopenharmony_ci auto src_width = source_.get_width(); 656141cc406Sopenharmony_ci auto dst_width = width_; 657141cc406Sopenharmony_ci 658141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(cached_line_.data()); 659141cc406Sopenharmony_ci 660141cc406Sopenharmony_ci const auto* src_data = cached_line_.data(); 661141cc406Sopenharmony_ci auto format = get_format(); 662141cc406Sopenharmony_ci auto channels = get_pixel_channels(format); 663141cc406Sopenharmony_ci 664141cc406Sopenharmony_ci if (src_width > dst_width) { 665141cc406Sopenharmony_ci // average 666141cc406Sopenharmony_ci std::uint32_t counter = src_width / 2; 667141cc406Sopenharmony_ci unsigned src_x = 0; 668141cc406Sopenharmony_ci for (unsigned dst_x = 0; dst_x < dst_width; dst_x++) { 669141cc406Sopenharmony_ci unsigned avg[3] = {0, 0, 0}; 670141cc406Sopenharmony_ci unsigned count = 0; 671141cc406Sopenharmony_ci while (counter < src_width && src_x < src_width) { 672141cc406Sopenharmony_ci counter += dst_width; 673141cc406Sopenharmony_ci 674141cc406Sopenharmony_ci for (unsigned c = 0; c < channels; c++) { 675141cc406Sopenharmony_ci avg[c] += get_raw_channel_from_row(src_data, src_x, c, format); 676141cc406Sopenharmony_ci } 677141cc406Sopenharmony_ci 678141cc406Sopenharmony_ci src_x++; 679141cc406Sopenharmony_ci count++; 680141cc406Sopenharmony_ci } 681141cc406Sopenharmony_ci counter -= src_width; 682141cc406Sopenharmony_ci 683141cc406Sopenharmony_ci for (unsigned c = 0; c < channels; c++) { 684141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, dst_x, c, avg[c] / count, format); 685141cc406Sopenharmony_ci } 686141cc406Sopenharmony_ci } 687141cc406Sopenharmony_ci } else { 688141cc406Sopenharmony_ci // interpolate and copy pixels 689141cc406Sopenharmony_ci std::uint32_t counter = dst_width / 2; 690141cc406Sopenharmony_ci unsigned dst_x = 0; 691141cc406Sopenharmony_ci 692141cc406Sopenharmony_ci for (unsigned src_x = 0; src_x < src_width; src_x++) { 693141cc406Sopenharmony_ci unsigned avg[3] = {0, 0, 0}; 694141cc406Sopenharmony_ci for (unsigned c = 0; c < channels; c++) { 695141cc406Sopenharmony_ci avg[c] += get_raw_channel_from_row(src_data, src_x, c, format); 696141cc406Sopenharmony_ci } 697141cc406Sopenharmony_ci while ((counter < dst_width || src_x + 1 == src_width) && dst_x < dst_width) { 698141cc406Sopenharmony_ci counter += src_width; 699141cc406Sopenharmony_ci 700141cc406Sopenharmony_ci for (unsigned c = 0; c < channels; c++) { 701141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, dst_x, c, avg[c], format); 702141cc406Sopenharmony_ci } 703141cc406Sopenharmony_ci dst_x++; 704141cc406Sopenharmony_ci } 705141cc406Sopenharmony_ci counter -= dst_width; 706141cc406Sopenharmony_ci } 707141cc406Sopenharmony_ci } 708141cc406Sopenharmony_ci return got_data; 709141cc406Sopenharmony_ci} 710141cc406Sopenharmony_ci 711141cc406Sopenharmony_cibool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data) 712141cc406Sopenharmony_ci{ 713141cc406Sopenharmony_ci bool got_data = true; 714141cc406Sopenharmony_ci 715141cc406Sopenharmony_ci while (current_line_ < offset_y_) { 716141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(cached_line_.data()); 717141cc406Sopenharmony_ci current_line_++; 718141cc406Sopenharmony_ci } 719141cc406Sopenharmony_ci if (current_line_ >= offset_y_ + source_.get_height()) { 720141cc406Sopenharmony_ci std::fill(out_data, out_data + get_row_bytes(), 0); 721141cc406Sopenharmony_ci current_line_++; 722141cc406Sopenharmony_ci return got_data; 723141cc406Sopenharmony_ci } 724141cc406Sopenharmony_ci // now we're sure that the following holds: 725141cc406Sopenharmony_ci // offset_y_ <= current_line_ < offset_y_ + source_.get_height()) 726141cc406Sopenharmony_ci got_data &= source_.get_next_row_data(cached_line_.data()); 727141cc406Sopenharmony_ci 728141cc406Sopenharmony_ci auto format = get_format(); 729141cc406Sopenharmony_ci auto x_src_width = source_.get_width() > offset_x_ ? source_.get_width() - offset_x_ : 0; 730141cc406Sopenharmony_ci x_src_width = std::min(x_src_width, width_); 731141cc406Sopenharmony_ci auto x_pad_after = width_ > x_src_width ? width_ - x_src_width : 0; 732141cc406Sopenharmony_ci 733141cc406Sopenharmony_ci if (get_pixel_format_depth(format) < 8) { 734141cc406Sopenharmony_ci // we need to copy pixels one-by-one as there's no per-bit addressing 735141cc406Sopenharmony_ci for (std::size_t i = 0; i < x_src_width; ++i) { 736141cc406Sopenharmony_ci auto pixel = get_raw_pixel_from_row(cached_line_.data(), i + offset_x_, format); 737141cc406Sopenharmony_ci set_raw_pixel_to_row(out_data, i, pixel, format); 738141cc406Sopenharmony_ci } 739141cc406Sopenharmony_ci for (std::size_t i = 0; i < x_pad_after; ++i) { 740141cc406Sopenharmony_ci set_raw_pixel_to_row(out_data, i + x_src_width, RawPixel{}, format); 741141cc406Sopenharmony_ci } 742141cc406Sopenharmony_ci } else { 743141cc406Sopenharmony_ci std::size_t bpp = get_pixel_format_depth(format) / 8; 744141cc406Sopenharmony_ci if (x_src_width > 0) { 745141cc406Sopenharmony_ci std::memcpy(out_data, cached_line_.data() + offset_x_ * bpp, 746141cc406Sopenharmony_ci x_src_width * bpp); 747141cc406Sopenharmony_ci } 748141cc406Sopenharmony_ci if (x_pad_after > 0) { 749141cc406Sopenharmony_ci std::fill(out_data + x_src_width * bpp, 750141cc406Sopenharmony_ci out_data + (x_src_width + x_pad_after) * bpp, 0); 751141cc406Sopenharmony_ci } 752141cc406Sopenharmony_ci } 753141cc406Sopenharmony_ci 754141cc406Sopenharmony_ci current_line_++; 755141cc406Sopenharmony_ci 756141cc406Sopenharmony_ci return got_data; 757141cc406Sopenharmony_ci} 758141cc406Sopenharmony_ci 759141cc406Sopenharmony_ciImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source, 760141cc406Sopenharmony_ci const std::vector<std::uint16_t>& bottom, 761141cc406Sopenharmony_ci const std::vector<std::uint16_t>& top, 762141cc406Sopenharmony_ci std::size_t x_start) : 763141cc406Sopenharmony_ci source_(source) 764141cc406Sopenharmony_ci{ 765141cc406Sopenharmony_ci std::size_t size = 0; 766141cc406Sopenharmony_ci if (bottom.size() >= x_start && top.size() >= x_start) { 767141cc406Sopenharmony_ci size = std::min(bottom.size() - x_start, top.size() - x_start); 768141cc406Sopenharmony_ci } 769141cc406Sopenharmony_ci 770141cc406Sopenharmony_ci offset_.reserve(size); 771141cc406Sopenharmony_ci multiplier_.reserve(size); 772141cc406Sopenharmony_ci 773141cc406Sopenharmony_ci for (std::size_t i = 0; i < size; ++i) { 774141cc406Sopenharmony_ci offset_.push_back(bottom[i + x_start] / 65535.0f); 775141cc406Sopenharmony_ci multiplier_.push_back(65535.0f / (top[i + x_start] - bottom[i + x_start])); 776141cc406Sopenharmony_ci } 777141cc406Sopenharmony_ci} 778141cc406Sopenharmony_ci 779141cc406Sopenharmony_cibool ImagePipelineNodeCalibrate::get_next_row_data(std::uint8_t* out_data) 780141cc406Sopenharmony_ci{ 781141cc406Sopenharmony_ci bool ret = source_.get_next_row_data(out_data); 782141cc406Sopenharmony_ci 783141cc406Sopenharmony_ci auto format = get_format(); 784141cc406Sopenharmony_ci auto depth = get_pixel_format_depth(format); 785141cc406Sopenharmony_ci std::size_t max_value = 1; 786141cc406Sopenharmony_ci switch (depth) { 787141cc406Sopenharmony_ci case 8: max_value = 255; break; 788141cc406Sopenharmony_ci case 16: max_value = 65535; break; 789141cc406Sopenharmony_ci default: 790141cc406Sopenharmony_ci throw SaneException("Unsupported depth for calibration %d", depth); 791141cc406Sopenharmony_ci } 792141cc406Sopenharmony_ci unsigned channels = get_pixel_channels(format); 793141cc406Sopenharmony_ci 794141cc406Sopenharmony_ci std::size_t max_calib_i = offset_.size(); 795141cc406Sopenharmony_ci std::size_t curr_calib_i = 0; 796141cc406Sopenharmony_ci 797141cc406Sopenharmony_ci for (std::size_t x = 0, width = get_width(); x < width && curr_calib_i < max_calib_i; ++x) { 798141cc406Sopenharmony_ci for (unsigned ch = 0; ch < channels && curr_calib_i < max_calib_i; ++ch) { 799141cc406Sopenharmony_ci std::int32_t value = get_raw_channel_from_row(out_data, x, ch, format); 800141cc406Sopenharmony_ci 801141cc406Sopenharmony_ci float value_f = static_cast<float>(value) / max_value; 802141cc406Sopenharmony_ci value_f = (value_f - offset_[curr_calib_i]) * multiplier_[curr_calib_i]; 803141cc406Sopenharmony_ci value_f = std::round(value_f * max_value); 804141cc406Sopenharmony_ci value = clamp<std::int32_t>(static_cast<std::int32_t>(value_f), 0, max_value); 805141cc406Sopenharmony_ci set_raw_channel_to_row(out_data, x, ch, value, format); 806141cc406Sopenharmony_ci 807141cc406Sopenharmony_ci curr_calib_i++; 808141cc406Sopenharmony_ci } 809141cc406Sopenharmony_ci } 810141cc406Sopenharmony_ci return ret; 811141cc406Sopenharmony_ci} 812141cc406Sopenharmony_ci 813141cc406Sopenharmony_ciImagePipelineNodeDebug::ImagePipelineNodeDebug(ImagePipelineNode& source, 814141cc406Sopenharmony_ci const std::string& path) : 815141cc406Sopenharmony_ci source_(source), 816141cc406Sopenharmony_ci path_{path}, 817141cc406Sopenharmony_ci buffer_{source_.get_row_bytes()} 818141cc406Sopenharmony_ci{} 819141cc406Sopenharmony_ci 820141cc406Sopenharmony_ciImagePipelineNodeDebug::~ImagePipelineNodeDebug() 821141cc406Sopenharmony_ci{ 822141cc406Sopenharmony_ci catch_all_exceptions(__func__, [&]() 823141cc406Sopenharmony_ci { 824141cc406Sopenharmony_ci if (buffer_.empty()) 825141cc406Sopenharmony_ci return; 826141cc406Sopenharmony_ci 827141cc406Sopenharmony_ci auto format = get_format(); 828141cc406Sopenharmony_ci buffer_.linearize(); 829141cc406Sopenharmony_ci write_tiff_file(path_, buffer_.get_front_row_ptr(), get_pixel_format_depth(format), 830141cc406Sopenharmony_ci get_pixel_channels(format), get_width(), buffer_.height()); 831141cc406Sopenharmony_ci }); 832141cc406Sopenharmony_ci} 833141cc406Sopenharmony_ci 834141cc406Sopenharmony_cibool ImagePipelineNodeDebug::get_next_row_data(std::uint8_t* out_data) 835141cc406Sopenharmony_ci{ 836141cc406Sopenharmony_ci buffer_.push_back(); 837141cc406Sopenharmony_ci bool got_data = source_.get_next_row_data(out_data); 838141cc406Sopenharmony_ci std::memcpy(buffer_.get_back_row_ptr(), out_data, get_row_bytes()); 839141cc406Sopenharmony_ci return got_data; 840141cc406Sopenharmony_ci} 841141cc406Sopenharmony_ci 842141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_input_width() const 843141cc406Sopenharmony_ci{ 844141cc406Sopenharmony_ci ensure_node_exists(); 845141cc406Sopenharmony_ci return nodes_.front()->get_width(); 846141cc406Sopenharmony_ci} 847141cc406Sopenharmony_ci 848141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_input_height() const 849141cc406Sopenharmony_ci{ 850141cc406Sopenharmony_ci ensure_node_exists(); 851141cc406Sopenharmony_ci return nodes_.front()->get_height(); 852141cc406Sopenharmony_ci} 853141cc406Sopenharmony_ci 854141cc406Sopenharmony_ciPixelFormat ImagePipelineStack::get_input_format() const 855141cc406Sopenharmony_ci{ 856141cc406Sopenharmony_ci ensure_node_exists(); 857141cc406Sopenharmony_ci return nodes_.front()->get_format(); 858141cc406Sopenharmony_ci} 859141cc406Sopenharmony_ci 860141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_input_row_bytes() const 861141cc406Sopenharmony_ci{ 862141cc406Sopenharmony_ci ensure_node_exists(); 863141cc406Sopenharmony_ci return nodes_.front()->get_row_bytes(); 864141cc406Sopenharmony_ci} 865141cc406Sopenharmony_ci 866141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_output_width() const 867141cc406Sopenharmony_ci{ 868141cc406Sopenharmony_ci ensure_node_exists(); 869141cc406Sopenharmony_ci return nodes_.back()->get_width(); 870141cc406Sopenharmony_ci} 871141cc406Sopenharmony_ci 872141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_output_height() const 873141cc406Sopenharmony_ci{ 874141cc406Sopenharmony_ci ensure_node_exists(); 875141cc406Sopenharmony_ci return nodes_.back()->get_height(); 876141cc406Sopenharmony_ci} 877141cc406Sopenharmony_ci 878141cc406Sopenharmony_ciPixelFormat ImagePipelineStack::get_output_format() const 879141cc406Sopenharmony_ci{ 880141cc406Sopenharmony_ci ensure_node_exists(); 881141cc406Sopenharmony_ci return nodes_.back()->get_format(); 882141cc406Sopenharmony_ci} 883141cc406Sopenharmony_ci 884141cc406Sopenharmony_cistd::size_t ImagePipelineStack::get_output_row_bytes() const 885141cc406Sopenharmony_ci{ 886141cc406Sopenharmony_ci ensure_node_exists(); 887141cc406Sopenharmony_ci return nodes_.back()->get_row_bytes(); 888141cc406Sopenharmony_ci} 889141cc406Sopenharmony_ci 890141cc406Sopenharmony_civoid ImagePipelineStack::ensure_node_exists() const 891141cc406Sopenharmony_ci{ 892141cc406Sopenharmony_ci if (nodes_.empty()) { 893141cc406Sopenharmony_ci throw SaneException("The pipeline does not contain any nodes"); 894141cc406Sopenharmony_ci } 895141cc406Sopenharmony_ci} 896141cc406Sopenharmony_ci 897141cc406Sopenharmony_civoid ImagePipelineStack::clear() 898141cc406Sopenharmony_ci{ 899141cc406Sopenharmony_ci // we need to destroy the nodes back to front, so that the destructors still have valid 900141cc406Sopenharmony_ci // references to sources 901141cc406Sopenharmony_ci for (auto it = nodes_.rbegin(); it != nodes_.rend(); ++it) { 902141cc406Sopenharmony_ci it->reset(); 903141cc406Sopenharmony_ci } 904141cc406Sopenharmony_ci nodes_.clear(); 905141cc406Sopenharmony_ci} 906141cc406Sopenharmony_ci 907141cc406Sopenharmony_cistd::vector<std::uint8_t> ImagePipelineStack::get_all_data() 908141cc406Sopenharmony_ci{ 909141cc406Sopenharmony_ci auto row_bytes = get_output_row_bytes(); 910141cc406Sopenharmony_ci auto height = get_output_height(); 911141cc406Sopenharmony_ci 912141cc406Sopenharmony_ci std::vector<std::uint8_t> ret; 913141cc406Sopenharmony_ci ret.resize(row_bytes * height); 914141cc406Sopenharmony_ci 915141cc406Sopenharmony_ci for (std::size_t i = 0; i < height; ++i) { 916141cc406Sopenharmony_ci get_next_row_data(ret.data() + row_bytes * i); 917141cc406Sopenharmony_ci } 918141cc406Sopenharmony_ci return ret; 919141cc406Sopenharmony_ci} 920141cc406Sopenharmony_ci 921141cc406Sopenharmony_ciImage ImagePipelineStack::get_image() 922141cc406Sopenharmony_ci{ 923141cc406Sopenharmony_ci auto height = get_output_height(); 924141cc406Sopenharmony_ci 925141cc406Sopenharmony_ci Image ret; 926141cc406Sopenharmony_ci ret.resize(get_output_width(), height, get_output_format()); 927141cc406Sopenharmony_ci 928141cc406Sopenharmony_ci for (std::size_t i = 0; i < height; ++i) { 929141cc406Sopenharmony_ci get_next_row_data(ret.get_row_ptr(i)); 930141cc406Sopenharmony_ci } 931141cc406Sopenharmony_ci return ret; 932141cc406Sopenharmony_ci} 933141cc406Sopenharmony_ci 934141cc406Sopenharmony_ci} // namespace genesys 935