summaryrefslogtreecommitdiff
path: root/backend/genesys/low.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/genesys/low.cpp')
-rw-r--r--backend/genesys/low.cpp1234
1 files changed, 607 insertions, 627 deletions
diff --git a/backend/genesys/low.cpp b/backend/genesys/low.cpp
index 7937fcc..05ef46b 100644
--- a/backend/genesys/low.cpp
+++ b/backend/genesys/low.cpp
@@ -51,12 +51,23 @@
#include "gl124_registers.h"
#include "gl646_registers.h"
#include "gl841_registers.h"
+#include "gl842_registers.h"
#include "gl843_registers.h"
#include "gl846_registers.h"
#include "gl847_registers.h"
#include "gl646_registers.h"
+#include "gl124.h"
+#include "gl646.h"
+#include "gl841.h"
+#include "gl842.h"
+#include "gl843.h"
+#include "gl846.h"
+#include "gl847.h"
+#include "gl646.h"
+
#include <cstdio>
+#include <chrono>
#include <cmath>
#include <vector>
@@ -66,29 +77,17 @@
namespace genesys {
-/**
- * setup the hardware dependent functions
- */
-
-namespace gl124 { std::unique_ptr<CommandSet> create_gl124_cmd_set(); }
-namespace gl646 { std::unique_ptr<CommandSet> create_gl646_cmd_set(); }
-namespace gl841 { std::unique_ptr<CommandSet> create_gl841_cmd_set(); }
-namespace gl843 { std::unique_ptr<CommandSet> create_gl843_cmd_set(); }
-namespace gl846 { std::unique_ptr<CommandSet> create_gl846_cmd_set(); }
-namespace gl847 { std::unique_ptr<CommandSet> create_gl847_cmd_set(); }
-
-void sanei_genesys_init_cmd_set(Genesys_Device* dev)
+std::unique_ptr<CommandSet> create_cmd_set(AsicType asic_type)
{
- DBG_INIT ();
- DBG_HELPER(dbg);
- switch (dev->model->asic_type) {
- case AsicType::GL646: dev->cmd_set = gl646::create_gl646_cmd_set(); break;
- case AsicType::GL841: dev->cmd_set = gl841::create_gl841_cmd_set(); break;
- case AsicType::GL843: dev->cmd_set = gl843::create_gl843_cmd_set(); break;
+ switch (asic_type) {
+ case AsicType::GL646: return std::unique_ptr<CommandSet>(new gl646::CommandSetGl646{});
+ case AsicType::GL841: return std::unique_ptr<CommandSet>(new gl841::CommandSetGl841{});
+ case AsicType::GL842: return std::unique_ptr<CommandSet>(new gl842::CommandSetGl842{});
+ case AsicType::GL843: return std::unique_ptr<CommandSet>(new gl843::CommandSetGl843{});
case AsicType::GL845: // since only a few reg bits differs we handle both together
- case AsicType::GL846: dev->cmd_set = gl846::create_gl846_cmd_set(); break;
- case AsicType::GL847: dev->cmd_set = gl847::create_gl847_cmd_set(); break;
- case AsicType::GL124: dev->cmd_set = gl124::create_gl124_cmd_set(); break;
+ case AsicType::GL846: return std::unique_ptr<CommandSet>(new gl846::CommandSetGl846{});
+ case AsicType::GL847: return std::unique_ptr<CommandSet>(new gl847::CommandSetGl847{});
+ case AsicType::GL124: return std::unique_ptr<CommandSet>(new gl124::CommandSetGl124{});
default: throw SaneException(SANE_STATUS_INVAL, "unknown ASIC type");
}
}
@@ -108,116 +107,6 @@ void sanei_genesys_write_file(const char* filename, const std::uint8_t* data, st
std::fclose(out);
}
-// Write data to a pnm file (e.g. calibration). For debugging only
-// data is RGB or grey, with little endian byte order
-void sanei_genesys_write_pnm_file(const char* filename, const std::uint8_t* 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);
- int count;
-
- std::FILE* out = std::fopen(filename, "w");
- if (!out)
- {
- throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno));
- }
- if(depth==1)
- {
- fprintf (out, "P4\n%d\n%d\n", pixels_per_line, lines);
- }
- else
- {
- std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6', pixels_per_line, lines,
- static_cast<int>(std::pow(static_cast<double>(2),
- static_cast<double>(depth - 1))));
- }
- if (channels == 3)
- {
- for (count = 0; count < (pixels_per_line * lines * 3); count++)
- {
- if (depth == 16)
- fputc (*(data + 1), out);
- fputc (*(data++), out);
- if (depth == 16)
- data++;
- }
- }
- else
- {
- if (depth==1)
- {
- pixels_per_line/=8;
- }
- for (count = 0; count < (pixels_per_line * lines); count++)
- {
- switch (depth)
- {
- case 8:
- fputc (*(data + count), out);
- break;
- case 16:
- fputc (*(data + 1), out);
- fputc (*(data), out);
- data += 2;
- break;
- default:
- fputc(data[count], out);
- break;
- }
- }
- }
- std::fclose(out);
-}
-
-void sanei_genesys_write_pnm_file16(const char* filename, const uint16_t* data, unsigned channels,
- unsigned pixels_per_line, unsigned lines)
-{
- DBG_HELPER_ARGS(dbg, "channels=%d, ppl=%d, lines=%d", channels,
- pixels_per_line, lines);
-
- std::FILE* out = std::fopen(filename, "w");
- if (!out) {
- throw SaneException("could not open %s for writing: %s\n", filename, strerror(errno));
- }
- std::fprintf(out, "P%c\n%d\n%d\n%d\n", channels == 1 ? '5' : '6',
- pixels_per_line, lines, 256 * 256 - 1);
-
- for (unsigned count = 0; count < (pixels_per_line * lines * channels); count++) {
- fputc(*data >> 8, out);
- fputc(*data & 0xff, out);
- data++;
- }
- std::fclose(out);
-}
-
-bool is_supported_write_pnm_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 sanei_genesys_write_pnm_file(const char* filename, const Image& image)
-{
- if (!is_supported_write_pnm_file_image_format(image.get_format())) {
- throw SaneException("Unsupported format %d", static_cast<unsigned>(image.get_format()));
- }
-
- sanei_genesys_write_pnm_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());
-}
-
/* ------------------------------------------------------------------------ */
/* Read and write RAM, registers and AFE */
/* ------------------------------------------------------------------------ */
@@ -276,6 +165,7 @@ Status scanner_read_status(Genesys_Device& dev)
case AsicType::GL124: address = 0x101; break;
case AsicType::GL646:
case AsicType::GL841:
+ case AsicType::GL842:
case AsicType::GL843:
case AsicType::GL845:
case AsicType::GL846:
@@ -336,28 +226,23 @@ void debug_print_status(DebugMessageHelper& dbg, Status val)
dbg.vlog(DBG_info, "status=%s\n", str.str().c_str());
}
-#if 0
-/* returns pixels per line from register set */
-/*candidate for moving into chip specific files?*/
-static int
-genesys_pixels_per_line (Genesys_Register_Set * reg)
+void scanner_register_rw_clear_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask)
{
- int pixels_per_line;
-
- pixels_per_line = reg->get8(0x32) * 256 + reg->get8(0x33);
- pixels_per_line -= (reg->get8(0x30) * 256 + reg->get8(0x31));
+ scanner_register_rw_bits(dev, address, 0x00, mask);
+}
- return pixels_per_line;
+void scanner_register_rw_set_bits(Genesys_Device& dev, std::uint16_t address, std::uint8_t mask)
+{
+ scanner_register_rw_bits(dev, address, mask, mask);
}
-/* returns dpiset from register set */
-/*candidate for moving into chip specific files?*/
-static int
-genesys_dpiset (Genesys_Register_Set * reg)
+void scanner_register_rw_bits(Genesys_Device& dev, std::uint16_t address,
+ std::uint8_t value, std::uint8_t mask)
{
- return reg->get8(0x2c) * 256 + reg->get8(0x2d);
+ auto reg_value = dev.interface->read_register(address);
+ reg_value = (reg_value & ~mask) | (value & mask);
+ dev.interface->write_register(address, reg_value);
}
-#endif
/** read the number of valid words in scanner's RAM
* ie registers 42-43-44
@@ -481,7 +366,7 @@ void wait_until_has_valid_words(Genesys_Device* dev)
unsigned words = 0;
unsigned sleep_time_ms = 10;
- for (unsigned wait_ms = 0; wait_ms < 50000; wait_ms += sleep_time_ms) {
+ for (unsigned wait_ms = 0; wait_ms < 70000; wait_ms += sleep_time_ms) {
sanei_genesys_read_valid_words(dev, &words);
if (words != 0)
break;
@@ -516,7 +401,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&
dev->model->line_mode_color_order);
auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
- auto height = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1);
+ auto height = session.optical_line_count;
Image image(width, height, format);
@@ -525,7 +410,7 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&
throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes);
}
if (total_bytes != max_bytes) {
- DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu\n", __func__,
+ DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__,
total_bytes, max_bytes);
}
@@ -534,26 +419,138 @@ Image read_unshuffled_image_from_scanner(Genesys_Device* dev, const ScanSession&
ImagePipelineStack pipeline;
pipeline.push_first_node<ImagePipelineNodeImageSource>(image);
- if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && session.params.depth == 16) {
- dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ if (session.segment_count > 1) {
+ auto output_width = session.output_segment_pixel_group_count * session.segment_count;
+ pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order,
+ session.conseq_pixel_dist,
+ 1, 1);
}
+ if (session.params.depth == 16) {
+ unsigned num_swaps = 0;
+ if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {
+ num_swaps++;
+ }
#ifdef WORDS_BIGENDIAN
- if (depth == 16) {
- dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ num_swaps++;
+#endif
+ if (num_swaps % 2 != 0) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
}
+
+ if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
+ pipeline.push_node<ImagePipelineNodeInvert>();
+ }
+
+ if (dev->model->is_cis && session.params.channels == 3) {
+ pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
+ }
+
+ if (pipeline.get_output_format() == PixelFormat::BGR888) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
+ }
+
+ if (pipeline.get_output_format() == PixelFormat::BGR161616) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
+ }
+
+ return pipeline.get_image();
+}
+
+
+Image read_shuffled_image_from_scanner(Genesys_Device* dev, const ScanSession& session)
+{
+ DBG_HELPER(dbg);
+
+ std::size_t total_bytes = 0;
+ std::size_t pixels_per_line = 0;
+
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843 ||
+ dev->model->model_id == ModelId::CANON_5600F)
+ {
+ pixels_per_line = session.output_pixels;
+ } else {
+ // BUG: this selects incorrect pixel number
+ pixels_per_line = session.params.pixels;
+ }
+
+ // FIXME: the current calculation is likely incorrect on non-GL843 implementations,
+ // but this needs checking. Note the extra line when computing size.
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843 ||
+ dev->model->model_id == ModelId::CANON_5600F)
+ {
+ total_bytes = session.output_total_bytes_raw;
+ } else {
+ total_bytes = session.params.channels * 2 * pixels_per_line * (session.params.lines + 1);
+ }
+
+ auto format = create_pixel_format(session.params.depth,
+ dev->model->is_cis ? 1 : session.params.channels,
+ dev->model->line_mode_color_order);
+
+ // auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
+ auto width = pixels_per_line;
+ auto height = session.params.lines + 1; // BUG: incorrect
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843 ||
+ dev->model->model_id == ModelId::CANON_5600F)
+ {
+ height = session.optical_line_count;
+ }
+
+ Image image(width, height, format);
+
+ auto max_bytes = image.get_row_bytes() * height;
+ if (total_bytes > max_bytes) {
+ throw SaneException("Trying to read too much data %zu (max %zu)", total_bytes, max_bytes);
+ }
+ if (total_bytes != max_bytes) {
+ DBG(DBG_info, "WARNING %s: trying to read not enough data (%zu, full fill %zu)\n", __func__,
+ total_bytes, max_bytes);
+ }
+
+ sanei_genesys_read_data_from_scanner(dev, image.get_row_ptr(0), total_bytes);
+
+ ImagePipelineStack pipeline;
+ pipeline.push_first_node<ImagePipelineNodeImageSource>(image);
+
+ if (session.segment_count > 1) {
+ auto output_width = session.output_segment_pixel_group_count * session.segment_count;
+ pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order,
+ session.conseq_pixel_dist,
+ 1, 1);
+ }
+
+ if (session.params.depth == 16) {
+ unsigned num_swaps = 0;
+ if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {
+ num_swaps++;
+ }
+#ifdef WORDS_BIGENDIAN
+ num_swaps++;
#endif
+ if (num_swaps % 2 != 0) {
+ dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ }
+ }
+
+ if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
+ pipeline.push_node<ImagePipelineNodeInvert>();
+ }
if (dev->model->is_cis && session.params.channels == 3) {
- dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
+ pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
}
- if (dev->pipeline.get_output_format() == PixelFormat::BGR888) {
- dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
+ if (pipeline.get_output_format() == PixelFormat::BGR888) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
}
- if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) {
- dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
+ if (pipeline.get_output_format() == PixelFormat::BGR161616) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
}
return pipeline.get_image();
@@ -600,34 +597,27 @@ void sanei_genesys_set_lamp_power(Genesys_Device* dev, const Genesys_Sensor& sen
if (dev->model->asic_type == AsicType::GL843) {
regs_set_exposure(dev->model->asic_type, regs, sensor.exposure);
+ }
- // we don't actually turn on lamp on infrared scan
- if ((dev->model->model_id == ModelId::CANON_8400F ||
- dev->model->model_id == ModelId::CANON_8600F ||
- dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
- dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I) &&
- dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
- {
- regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;
- }
+ // we don't actually turn on lamp on infrared scan
+ if ((dev->model->model_id == ModelId::CANON_8400F ||
+ dev->model->model_id == ModelId::CANON_8600F ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I) &&
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;
}
} else {
regs.find_reg(0x03).value &= ~REG_0x03_LAMPPWR;
if (dev->model->asic_type == AsicType::GL841) {
- regs_set_exposure(dev->model->asic_type, regs, {0x0101, 0x0101, 0x0101});
+ regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0}));
regs.set8(0x19, 0xff);
}
-
- if (dev->model->asic_type == AsicType::GL843) {
- if (dev->model->model_id == ModelId::PANASONIC_KV_SS080 ||
- dev->model->model_id == ModelId::HP_SCANJET_4850C ||
- dev->model->model_id == ModelId::HP_SCANJET_G4010 ||
- dev->model->model_id == ModelId::HP_SCANJET_G4050)
- {
- // BUG: datasheet says we shouldn't set exposure to zero
- regs_set_exposure(dev->model->asic_type, regs, {0, 0, 0});
- }
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ regs_set_exposure(dev->model->asic_type, regs, sanei_genesys_fixup_exposure({0, 0, 0}));
}
}
regs.state.is_lamp_on = set;
@@ -786,218 +776,144 @@ void sanei_genesys_send_gamma_table(Genesys_Device* dev, const Genesys_Sensor& s
}
}
-static unsigned align_int_up(unsigned num, unsigned alignment)
-{
- unsigned mask = alignment - 1;
- if (num & mask)
- num = (num & ~mask) + alignment;
- return num;
-}
-
-void compute_session_buffer_sizes(AsicType asic, ScanSession& s)
+void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s,
+ const Genesys_Sensor& sensor)
{
- size_t line_bytes = s.output_line_bytes;
- size_t line_bytes_stagger = s.output_line_bytes;
-
- if (asic != AsicType::GL646) {
- // BUG: this is historical artifact and should be removed. Note that buffer sizes affect
- // how often we request the scanner for data and thus change the USB traffic.
- line_bytes_stagger =
- multiply_by_depth_ceil(s.optical_pixels, s.params.depth) * s.params.channels;
- }
-
- struct BufferConfig {
- size_t* result_size = nullptr;
- size_t lines = 0;
- size_t lines_mult = 0;
- size_t max_size = 0; // does not apply if 0
- size_t stagger_lines = 0;
-
- BufferConfig() = default;
- BufferConfig(std::size_t* rs, std::size_t l, std::size_t lm, std::size_t ms,
- std::size_t sl) :
- result_size{rs},
- lines{l},
- lines_mult{lm},
- max_size{ms},
- stagger_lines{sl}
- {}
- };
+ if (dev->model->asic_type == AsicType::GL646) {
+ s.pixel_startx += s.output_startx * sensor.full_resolution / s.params.xres;
+ s.pixel_endx = s.pixel_startx + s.optical_pixels * s.full_resolution / s.optical_resolution;
- std::array<BufferConfig, 4> configs;
- if (asic == AsicType::GL124 || asic == AsicType::GL843) {
- configs = { {
- { &s.buffer_size_read, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_lines, 32, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_shrink, 16, 1, 0, 0 },
- { &s.buffer_size_out, 8, 1, 0, 0 },
- } };
- } else if (asic == AsicType::GL841) {
- size_t max_buf = sanei_genesys_get_bulk_max_size(asic);
- configs = { {
- { &s.buffer_size_read, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_lines, 8, 2, max_buf, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_shrink, 8, 1, max_buf, 0 },
- { &s.buffer_size_out, 8, 1, 0, 0 },
- } };
- } else {
- configs = { {
- { &s.buffer_size_read, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_lines, 16, 1, 0, s.max_color_shift_lines + s.num_staggered_lines },
- { &s.buffer_size_shrink, 8, 1, 0, 0 },
- { &s.buffer_size_out, 8, 1, 0, 0 },
- } };
- }
-
- for (BufferConfig& config : configs) {
- size_t buf_size = line_bytes * config.lines;
- if (config.max_size > 0 && buf_size > config.max_size) {
- buf_size = (config.max_size / line_bytes) * line_bytes;
+ } else if (dev->model->asic_type == AsicType::GL841 ||
+ dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843 ||
+ dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847)
+ {
+ unsigned startx_xres = s.optical_resolution;
+ if (dev->model->model_id == ModelId::CANON_5600F ||
+ dev->model->model_id == ModelId::CANON_LIDE_90)
+ {
+ if (s.output_resolution == 1200) {
+ startx_xres /= 2;
+ }
+ if (s.output_resolution >= 2400) {
+ startx_xres /= 4;
+ }
}
- buf_size *= config.lines_mult;
- buf_size += line_bytes_stagger * config.stagger_lines;
- *config.result_size = buf_size;
- }
-}
-
-void compute_session_pipeline(const Genesys_Device* dev, ScanSession& s)
-{
- auto channels = s.params.channels;
- auto depth = s.params.depth;
+ s.pixel_startx = (s.output_startx * startx_xres) / s.params.xres;
+ s.pixel_endx = s.pixel_startx + s.optical_pixels_raw;
- s.pipeline_needs_reorder = true;
- if (channels != 3 && depth != 16) {
- s.pipeline_needs_reorder = false;
- }
-#ifndef WORDS_BIGENDIAN
- if (channels != 3 && depth == 16) {
- s.pipeline_needs_reorder = false;
- }
- if (channels == 3 && depth == 16 && !dev->model->is_cis &&
- dev->model->line_mode_color_order == ColorOrder::RGB)
+ } else if (dev->model->asic_type == AsicType::GL124)
{
- s.pipeline_needs_reorder = false;
+ s.pixel_startx = s.output_startx * sensor.full_resolution / s.params.xres;
+ s.pixel_endx = s.pixel_startx + s.optical_pixels_raw;
}
-#endif
- if (channels == 3 && depth == 8 && !dev->model->is_cis &&
- dev->model->line_mode_color_order == ColorOrder::RGB)
+
+ // align pixels to correct boundary for unstaggering
+ unsigned needed_x_alignment = std::max(s.stagger_x.size(), s.stagger_y.size());
+ unsigned aligned_pixel_startx = align_multiple_floor(s.pixel_startx, needed_x_alignment);
+ s.pixel_endx -= s.pixel_startx - aligned_pixel_startx;
+ s.pixel_startx = aligned_pixel_startx;
+
+ s.pixel_startx = sensor.pixel_count_ratio.apply(s.pixel_startx);
+ s.pixel_endx = sensor.pixel_count_ratio.apply(s.pixel_endx);
+
+ if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
{
- s.pipeline_needs_reorder = false;
+ s.pixel_startx = align_multiple_floor(s.pixel_startx, sensor.pixel_count_ratio.divisor());
+ s.pixel_endx = align_multiple_floor(s.pixel_endx, sensor.pixel_count_ratio.divisor());
}
- s.pipeline_needs_ccd = s.max_color_shift_lines + s.num_staggered_lines > 0;
- s.pipeline_needs_shrink = dev->settings.requested_pixels != s.output_pixels;
}
-void compute_session_pixel_offsets(const Genesys_Device* dev, ScanSession& s,
- const Genesys_Sensor& sensor)
+unsigned session_adjust_output_pixels(unsigned output_pixels,
+ const Genesys_Device& dev, const Genesys_Sensor& sensor,
+ unsigned output_xresolution, unsigned output_yresolution,
+ bool adjust_output_pixels)
{
- unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
-
- if (dev->model->asic_type == AsicType::GL646) {
-
- // startx cannot be below dummy pixel value
- s.pixel_startx = sensor.dummy_pixel;
- if (has_flag(s.params.flags, ScanFlag::USE_XCORRECTION) && sensor.ccd_start_xoffset > 0) {
- s.pixel_startx = sensor.ccd_start_xoffset;
- }
- s.pixel_startx += s.params.startx;
-
- if (sensor.stagger_config.stagger_at_resolution(s.params.xres, s.params.yres) > 0) {
- s.pixel_startx |= 1;
- }
-
- s.pixel_endx = s.pixel_startx + s.optical_pixels;
-
- s.pixel_startx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor;
- s.pixel_endx /= sensor.ccd_pixels_per_system_pixel() * s.ccd_size_divisor;
-
- } else if (dev->model->asic_type == AsicType::GL841) {
- s.pixel_startx = ((sensor.ccd_start_xoffset + s.params.startx) * s.optical_resolution)
- / sensor.optical_res;
+ bool adjust_optical_pixels = !adjust_output_pixels;
+ if (dev.model->model_id == ModelId::CANON_5600F) {
+ adjust_optical_pixels = true;
+ adjust_output_pixels = true;
+ }
+ if (adjust_optical_pixels) {
+ auto optical_resolution = sensor.get_optical_resolution();
- s.pixel_startx += sensor.dummy_pixel + 1;
+ // FIXME: better way would be to compute and return the required multiplier
+ unsigned optical_pixels = (output_pixels * optical_resolution) / output_xresolution;
- if (s.num_staggered_lines > 0 && (s.pixel_startx & 1) == 0) {
- s.pixel_startx++;
+ if (dev.model->asic_type == AsicType::GL841 ||
+ dev.model->asic_type == AsicType::GL842)
+ {
+ optical_pixels = align_multiple_ceil(optical_pixels, 2);
}
- /* In case of SHDAREA, we need to align start on pixel average factor, startx is
- different than 0 only when calling for function to setup for scan, where shading data
- needs to be align.
-
- NOTE: we can check the value of the register here, because we don't set this bit
- anywhere except in initialization.
- */
- const uint8_t REG_0x01_SHDAREA = 0x02;
- if ((dev->reg.find_reg(0x01).value & REG_0x01_SHDAREA) != 0) {
- unsigned average_factor = s.optical_resolution / s.params.xres;
- s.pixel_startx = align_multiple_floor(s.pixel_startx, average_factor);
+ if (dev.model->asic_type == AsicType::GL646 && output_xresolution == 400) {
+ optical_pixels = align_multiple_floor(optical_pixels, 6);
}
- s.pixel_endx = s.pixel_startx + s.optical_pixels;
-
- } else if (dev->model->asic_type == AsicType::GL843) {
-
- s.pixel_startx = (s.params.startx + sensor.dummy_pixel) / ccd_pixels_per_system_pixel;
- s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel;
-
- s.pixel_startx /= s.hwdpi_divisor;
- s.pixel_endx /= s.hwdpi_divisor;
-
- // in case of stagger we have to start at an odd coordinate
- bool stagger_starts_even = dev->model->model_id == ModelId::CANON_8400F;
- if (s.num_staggered_lines > 0) {
- if (!stagger_starts_even && (s.pixel_startx & 1) == 0) {
- s.pixel_startx++;
- s.pixel_endx++;
- } else if (stagger_starts_even && (s.pixel_startx & 1) != 0) {
- s.pixel_startx++;
- s.pixel_endx++;
+ if (dev.model->asic_type == AsicType::GL843) {
+ // ensure the number of optical pixels is divisible by 2.
+ // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number
+ optical_pixels = align_multiple_ceil(optical_pixels,
+ 2 * sensor.full_resolution / optical_resolution);
+ if (dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200 ||
+ dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
+ dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
+ dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7400 ||
+ dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I ||
+ dev.model->model_id == ModelId::PLUSTEK_OPTICFILM_8200I)
+ {
+ optical_pixels = align_multiple_ceil(optical_pixels, 16);
}
}
+ output_pixels = (optical_pixels * output_xresolution) / optical_resolution;
+ }
- } else if (dev->model->asic_type == AsicType::GL845 ||
- dev->model->asic_type == AsicType::GL846 ||
- dev->model->asic_type == AsicType::GL847)
- {
- s.pixel_startx = s.params.startx;
-
- if (s.num_staggered_lines > 0) {
- s.pixel_startx |= 1;
- }
-
- s.pixel_startx += sensor.ccd_start_xoffset * ccd_pixels_per_system_pixel;
- s.pixel_endx = s.pixel_startx + s.optical_pixels_raw;
-
- s.pixel_startx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel;
- s.pixel_endx /= s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel;
-
- } else if (dev->model->asic_type == AsicType::GL124) {
- s.pixel_startx = s.params.startx;
+ if (adjust_output_pixels) {
+ // TODO: the following may no longer be needed but were applied historically.
- if (s.num_staggered_lines > 0) {
- s.pixel_startx |= 1;
+ // we need an even pixels number
+ // TODO invert test logic or generalize behaviour across all ASICs
+ if (has_flag(dev.model->flags, ModelFlag::SIS_SENSOR) ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.model->asic_type == AsicType::GL124 ||
+ dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ if (output_xresolution <= 1200) {
+ output_pixels = align_multiple_floor(output_pixels, 4);
+ } else if (output_xresolution < output_yresolution) {
+ // BUG: this is an artifact of the fact that the resolution was twice as large than
+ // the actual resolution when scanning above the supported scanner X resolution
+ output_pixels = align_multiple_floor(output_pixels, 8);
+ } else {
+ output_pixels = align_multiple_floor(output_pixels, 16);
+ }
}
- s.pixel_startx /= ccd_pixels_per_system_pixel;
- // FIXME: should we add sensor.dummy_pxel to pixel_startx at this point?
- s.pixel_endx = s.pixel_startx + s.optical_pixels / ccd_pixels_per_system_pixel;
-
- s.pixel_startx /= s.hwdpi_divisor * s.segment_count;
- s.pixel_endx /= s.hwdpi_divisor * s.segment_count;
-
- std::uint32_t segcnt = (sensor.custom_regs.get_value(gl124::REG_SEGCNT) << 16) +
- (sensor.custom_regs.get_value(gl124::REG_SEGCNT + 1) << 8) +
- sensor.custom_regs.get_value(gl124::REG_SEGCNT + 2);
- if (s.pixel_endx == segcnt) {
- s.pixel_endx = 0;
+ // corner case for true lineart for sensor with several segments or when xres is doubled
+ // to match yres */
+ if (output_xresolution >= 1200 && (
+ dev.model->asic_type == AsicType::GL124 ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.session.params.xres < dev.session.params.yres))
+ {
+ if (output_xresolution < output_yresolution) {
+ // FIXME: this is an artifact of the fact that the resolution was twice as large than
+ // the actual resolution when scanning above the supported scanner X resolution
+ output_pixels = align_multiple_floor(output_pixels, 8);
+ } else {
+ output_pixels = align_multiple_floor(output_pixels, 16);
+ }
}
}
- s.pixel_count_multiplier = sensor.pixel_count_multiplier;
-
- s.pixel_startx *= sensor.pixel_count_multiplier;
- s.pixel_endx *= sensor.pixel_count_multiplier;
+ return output_pixels;
}
void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Sensor& sensor)
@@ -1011,68 +927,36 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
throw SaneException("Unsupported depth setting %d", s.params.depth);
}
- unsigned ccd_pixels_per_system_pixel = sensor.ccd_pixels_per_system_pixel();
-
// compute optical and output resolutions
-
- if (dev->model->asic_type == AsicType::GL843) {
- // FIXME: this may be incorrect, but need more scanners to test
- s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres);
- } else {
- s.hwdpi_divisor = sensor.get_hwdpi_divisor_for_dpi(s.params.xres * ccd_pixels_per_system_pixel);
- }
-
- s.ccd_size_divisor = sensor.get_ccd_size_divisor_for_dpi(s.params.xres);
-
- if (dev->model->asic_type == AsicType::GL646) {
- s.optical_resolution = sensor.optical_res;
- } else {
- s.optical_resolution = sensor.optical_res / s.ccd_size_divisor;
- }
+ s.full_resolution = sensor.full_resolution;
+ s.optical_resolution = sensor.get_optical_resolution();
s.output_resolution = s.params.xres;
+ s.pixel_count_ratio = sensor.pixel_count_ratio;
+
if (s.output_resolution > s.optical_resolution) {
throw std::runtime_error("output resolution higher than optical resolution");
}
- // compute the number of optical pixels that will be acquired by the chip
- s.optical_pixels = (s.params.pixels * s.optical_resolution) / s.output_resolution;
- if (s.optical_pixels * s.output_resolution < s.params.pixels * s.optical_resolution) {
- s.optical_pixels++;
- }
-
- if (dev->model->asic_type == AsicType::GL841) {
- if (s.optical_pixels & 1)
- s.optical_pixels++;
- }
-
- if (dev->model->asic_type == AsicType::GL646 && s.params.xres == 400) {
- s.optical_pixels = (s.optical_pixels / 6) * 6;
- }
+ s.output_pixels = session_adjust_output_pixels(s.params.pixels, *dev, sensor,
+ s.params.xres, s.params.yres, false);
- if (dev->model->asic_type == AsicType::GL843) {
- // ensure the number of optical pixels is divisible by 2.
- // In quarter-CCD mode optical_pixels is 4x larger than the actual physical number
- s.optical_pixels = align_int_up(s.optical_pixels, 2 * s.ccd_size_divisor);
+ // Compute the number of optical pixels that will be acquired by the chip.
+ // The necessary alignment requirements have already been computed by
+ // get_session_output_pixels_multiplier
+ s.optical_pixels = (s.output_pixels * s.optical_resolution) / s.output_resolution;
- if (dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7200I ||
- dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7300 ||
- dev->model->model_id == ModelId::PLUSTEK_OPTICFILM_7500I)
- {
- s.optical_pixels = align_int_up(s.optical_pixels, 16);
- }
- }
+ if (static_cast<int>(s.params.startx) + sensor.output_pixel_offset < 0)
+ throw SaneException("Invalid sensor.output_pixel_offset");
+ s.output_startx = static_cast<unsigned>(
+ static_cast<int>(s.params.startx) + sensor.output_pixel_offset);
- // after all adjustments on the optical pixels have been made, compute the number of pixels
- // to retrieve from the chip
- s.output_pixels = (s.optical_pixels * s.output_resolution) / s.optical_resolution;
+ s.stagger_x = sensor.stagger_x;
+ s.stagger_y = sensor.stagger_y;
- // Note: staggering is not applied for calibration. Staggering starts at 2400 dpi
s.num_staggered_lines = 0;
- if (!has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE))
- {
- s.num_staggered_lines = sensor.stagger_config.stagger_at_resolution(s.params.xres,
- s.params.yres);
+ if (!has_flag(s.params.flags, ScanFlag::IGNORE_STAGGER_OFFSET)) {
+ s.num_staggered_lines = s.stagger_y.max_shift() * s.params.yres / s.params.xres;
}
s.color_shift_lines_r = dev->model->ld_shift_r;
@@ -1091,12 +975,14 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
s.color_shift_lines_b = (s.color_shift_lines_b * s.params.yres) / dev->motor.base_ydpi;
s.max_color_shift_lines = 0;
- if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_LINE_DISTANCE)) {
+ if (s.params.channels > 1 && !has_flag(s.params.flags, ScanFlag::IGNORE_COLOR_OFFSET)) {
s.max_color_shift_lines = std::max(s.color_shift_lines_r, std::max(s.color_shift_lines_g,
s.color_shift_lines_b));
}
s.output_line_count = s.params.lines + s.max_color_shift_lines + s.num_staggered_lines;
+ s.optical_line_count = dev->model->is_cis ? s.output_line_count * s.params.channels
+ : s.output_line_count;
s.output_channel_bytes = multiply_by_depth_ceil(s.output_pixels, s.params.depth);
s.output_line_bytes = s.output_channel_bytes * s.params.channels;
@@ -1107,29 +993,62 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
s.output_line_bytes_raw = s.output_line_bytes;
s.conseq_pixel_dist = 0;
- if (dev->model->asic_type == AsicType::GL845 ||
- dev->model->asic_type == AsicType::GL846 ||
- dev->model->asic_type == AsicType::GL847)
+ // FIXME: Use ModelFlag::SIS_SENSOR
+ if ((dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847) &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_7400 &&
+ dev->model->model_id != ModelId::PLUSTEK_OPTICFILM_8200I)
{
if (s.segment_count > 1) {
s.conseq_pixel_dist = sensor.segment_size;
- // in case of multi-segments sensor, we have to add the width of the sensor crossed by
- // the scan area
- unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2);
- extra_segment_scan_area *= s.segment_count - 1;
- extra_segment_scan_area *= s.hwdpi_divisor * s.segment_count;
- extra_segment_scan_area *= ccd_pixels_per_system_pixel;
+ // in case of multi-segments sensor, we have expand the scan area to sensor boundary
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ unsigned startx_xres = s.optical_resolution;
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ if (s.output_resolution == 1200) {
+ startx_xres /= 2;
+ }
+ if (s.output_resolution >= 2400) {
+ startx_xres /= 4;
+ }
+ }
+ unsigned optical_startx = s.output_startx * startx_xres / s.params.xres;
+ unsigned optical_endx = optical_startx + s.optical_pixels;
- s.optical_pixels_raw += extra_segment_scan_area;
+ unsigned multi_segment_size_output = s.segment_count * s.conseq_pixel_dist;
+ unsigned multi_segment_size_optical =
+ (multi_segment_size_output * s.optical_resolution) / s.output_resolution;
+
+ optical_endx = align_multiple_ceil(optical_endx, multi_segment_size_optical);
+ s.optical_pixels_raw = optical_endx - optical_startx;
+ s.optical_pixels_raw = align_multiple_floor(s.optical_pixels_raw,
+ 4 * s.optical_resolution / s.output_resolution);
+ } else {
+ // BUG: the following code will likely scan too much. Use the CANON_5600F approach
+ unsigned extra_segment_scan_area = align_multiple_ceil(s.conseq_pixel_dist, 2);
+ extra_segment_scan_area *= s.segment_count - 1;
+ extra_segment_scan_area = s.pixel_count_ratio.apply_inverse(extra_segment_scan_area);
+
+ s.optical_pixels_raw += extra_segment_scan_area;
+ }
}
- s.output_line_bytes_raw = multiply_by_depth_ceil(
- (s.optical_pixels_raw * s.output_resolution) / sensor.optical_res / s.segment_count,
- s.params.depth);
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ auto output_pixels_raw = (s.optical_pixels_raw * s.output_resolution) / s.optical_resolution;
+ auto output_channel_bytes_raw = multiply_by_depth_ceil(output_pixels_raw, s.params.depth);
+ s.output_line_bytes_raw = output_channel_bytes_raw * s.params.channels;
+ } else {
+ s.output_line_bytes_raw = multiply_by_depth_ceil(
+ (s.optical_pixels_raw * s.output_resolution) / sensor.full_resolution / s.segment_count,
+ s.params.depth);
+ }
}
- if (dev->model->asic_type == AsicType::GL841) {
+ if (dev->model->asic_type == AsicType::GL841 ||
+ dev->model->asic_type == AsicType::GL842)
+ {
if (dev->model->is_cis) {
s.output_line_bytes_raw = s.output_channel_bytes;
}
@@ -1139,27 +1058,43 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
if (dev->model->is_cis) {
s.output_line_bytes_raw = s.output_channel_bytes;
}
- s.conseq_pixel_dist = s.output_pixels / s.ccd_size_divisor / s.segment_count;
+ s.conseq_pixel_dist = s.output_pixels / (s.full_resolution / s.optical_resolution) / s.segment_count;
}
- if (dev->model->asic_type == AsicType::GL843) {
- s.conseq_pixel_dist = s.output_pixels / s.segment_count;
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ if (dev->model->is_cis) {
+ if (s.segment_count > 1) {
+ s.conseq_pixel_dist = sensor.segment_size;
+ }
+ } else {
+ s.conseq_pixel_dist = s.output_pixels / s.segment_count;
+ }
}
s.output_segment_pixel_group_count = 0;
if (dev->model->asic_type == AsicType::GL124 ||
+ dev->model->asic_type == AsicType::GL842 ||
dev->model->asic_type == AsicType::GL843)
{
- s.output_segment_pixel_group_count = multiply_by_depth_ceil(
- s.output_pixels / s.ccd_size_divisor / s.segment_count, s.params.depth);
+ s.output_segment_pixel_group_count = s.output_pixels /
+ (s.full_resolution / s.optical_resolution * s.segment_count);
}
+
+ if (dev->model->model_id == ModelId::CANON_LIDE_90) {
+ s.output_segment_pixel_group_count = s.output_pixels / s.segment_count;
+ }
+
if (dev->model->asic_type == AsicType::GL845 ||
dev->model->asic_type == AsicType::GL846 ||
dev->model->asic_type == AsicType::GL847)
{
- s.output_segment_pixel_group_count = multiply_by_depth_ceil(
- s.optical_pixels / (s.hwdpi_divisor * s.segment_count * ccd_pixels_per_system_pixel),
- s.params.depth);
+ if (dev->model->model_id == ModelId::CANON_5600F) {
+ s.output_segment_pixel_group_count = s.output_pixels / s.segment_count;
+ } else {
+ s.output_segment_pixel_group_count = s.pixel_count_ratio.apply(s.optical_pixels);
+ }
}
s.output_line_bytes_requested = multiply_by_depth_ceil(
@@ -1167,11 +1102,16 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
s.output_total_bytes_raw = s.output_line_bytes_raw * s.output_line_count;
s.output_total_bytes = s.output_line_bytes * s.output_line_count;
+ if (dev->model->model_id == ModelId::CANON_LIDE_90) {
+ s.output_total_bytes_raw *= s.params.channels;
+ s.output_total_bytes *= s.params.channels;
+ }
- compute_session_buffer_sizes(dev->model->asic_type, s);
- compute_session_pipeline(dev, s);
+ s.buffer_size_read = s.output_line_bytes_raw * 64;
compute_session_pixel_offsets(dev, s, sensor);
+ s.shading_pixel_offset = sensor.shading_pixel_offset;
+
if (dev->model->asic_type == AsicType::GL124 ||
dev->model->asic_type == AsicType::GL845 ||
dev->model->asic_type == AsicType::GL846)
@@ -1179,7 +1119,10 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
s.enable_ledadd = (s.params.channels == 1 && dev->model->is_cis && dev->settings.true_gray);
}
+ s.use_host_side_calib = sensor.use_host_side_calib;
+
if (dev->model->asic_type == AsicType::GL841 ||
+ dev->model->asic_type == AsicType::GL842 ||
dev->model->asic_type == AsicType::GL843)
{
// no 16 bit gamma for this ASIC
@@ -1194,177 +1137,166 @@ void compute_session(const Genesys_Device* dev, ScanSession& s, const Genesys_Se
debug_dump(DBG_info, s);
}
-static std::size_t get_usb_buffer_read_size(AsicType asic, const ScanSession& session)
+ImagePipelineStack build_image_pipeline(const Genesys_Device& dev, const ScanSession& session,
+ unsigned pipeline_index, bool log_image_data)
{
- switch (asic) {
- case AsicType::GL646:
- // buffer not used on this chip set
- return 1;
-
- case AsicType::GL124:
- // BUG: we shouldn't multiply by channels here nor divide by ccd_size_divisor
- return session.output_line_bytes_raw / session.ccd_size_divisor * session.params.channels;
-
- case AsicType::GL845:
- case AsicType::GL846:
- case AsicType::GL847:
- // BUG: we shouldn't multiply by channels here
- return session.output_line_bytes_raw * session.params.channels;
-
- case AsicType::GL843:
- return session.output_line_bytes_raw * 2;
-
- default:
- throw SaneException("Unknown asic type");
- }
-}
-
-static FakeBufferModel get_fake_usb_buffer_model(const ScanSession& session)
-{
- FakeBufferModel model;
- model.push_step(session.buffer_size_read, 1);
-
- if (session.pipeline_needs_reorder) {
- model.push_step(session.buffer_size_lines, session.output_line_bytes);
- }
- if (session.pipeline_needs_ccd) {
- model.push_step(session.buffer_size_shrink, session.output_line_bytes);
- }
- if (session.pipeline_needs_shrink) {
- model.push_step(session.buffer_size_out, session.output_line_bytes);
- }
-
- return model;
-}
-
-void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
-{
- static unsigned s_pipeline_index = 0;
-
- s_pipeline_index++;
-
auto format = create_pixel_format(session.params.depth,
- dev->model->is_cis ? 1 : session.params.channels,
- dev->model->line_mode_color_order);
+ dev.model->is_cis ? 1 : session.params.channels,
+ dev.model->line_mode_color_order);
auto depth = get_pixel_format_depth(format);
auto width = get_pixels_from_row_bytes(format, session.output_line_bytes_raw);
- auto read_data_from_usb = [dev](std::size_t size, std::uint8_t* data)
+ auto read_data_from_usb = [&dev](std::size_t size, std::uint8_t* data)
{
- dev->interface->bulk_read_data(0x45, data, size);
+ DBG(DBG_info, "read_data_from_usb: reading %zu bytes\n", size);
+ auto begin = std::chrono::high_resolution_clock::now();
+ dev.interface->bulk_read_data(0x45, data, size);
+ auto end = std::chrono::high_resolution_clock::now();
+ float us = std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count();
+ float speed = size / us; // bytes/us == MB/s
+ DBG(DBG_info, "read_data_from_usb: reading %zu bytes finished %f MB/s\n", size, speed);
return true;
};
- auto lines = session.output_line_count * (dev->model->is_cis ? session.params.channels : 1);
+ auto debug_prefix = "gl_pipeline_" + std::to_string(pipeline_index);
- dev->pipeline.clear();
+ ImagePipelineStack pipeline;
- // FIXME: here we are complicating things for the time being to preserve the existing behaviour
- // This allows to be sure that the changes to the image pipeline have not introduced
- // regressions.
+ auto lines = session.optical_line_count;
+ auto buffer_size = session.buffer_size_read;
- if (session.segment_count > 1) {
- // BUG: we're reading one line too much
- dev->pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>(
- width, lines + 1, format,
- get_usb_buffer_read_size(dev->model->asic_type, session), read_data_from_usb);
+ // At least GL841 requires reads to be aligned to 2 bytes and will fail on some devices on
+ // certain circumstances.
+ buffer_size = align_multiple_ceil(buffer_size, 2);
+
+ auto& src_node = pipeline.push_first_node<ImagePipelineNodeBufferedCallableSource>(
+ width, lines, format, buffer_size, read_data_from_usb);
+ src_node.set_last_read_multiple(2);
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_0_from_usb.tiff");
+ }
+
+ if (session.segment_count > 1) {
auto output_width = session.output_segment_pixel_group_count * session.segment_count;
- dev->pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev->segment_order,
+ pipeline.push_node<ImagePipelineNodeDesegment>(output_width, dev.segment_order,
session.conseq_pixel_dist,
1, 1);
- } else {
- auto read_bytes_left_after_deseg = session.output_line_bytes * session.output_line_count;
- if (dev->model->asic_type == AsicType::GL646) {
- read_bytes_left_after_deseg *= dev->model->is_cis ? session.params.channels : 1;
- }
- dev->pipeline.push_first_node<ImagePipelineNodeBufferedGenesysUsb>(
- width, lines, format, read_bytes_left_after_deseg,
- get_fake_usb_buffer_model(session), read_data_from_usb);
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_1_after_desegment.tiff");
+ }
}
- if (DBG_LEVEL >= DBG_io2) {
- dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
- std::to_string(s_pipeline_index) +
- "_0_before_swap.pnm");
- }
+ if (depth == 16) {
+ unsigned num_swaps = 0;
+ if (has_flag(dev.model->flags, ModelFlag::SWAP_16BIT_DATA)) {
+ num_swaps++;
+ }
+#ifdef WORDS_BIGENDIAN
+ num_swaps++;
+#endif
+ if (num_swaps % 2 != 0) {
+ pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
- if ((dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) && depth == 16) {
- dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_2_after_swap.tiff");
+ }
+ }
}
-#ifdef WORDS_BIGENDIAN
- if (depth == 16) {
- dev->pipeline.push_node<ImagePipelineNodeSwap16BitEndian>();
+ if (has_flag(dev.model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
+ pipeline.push_node<ImagePipelineNodeInvert>();
+
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_3_after_invert.tiff");
+ }
}
-#endif
- if (DBG_LEVEL >= DBG_io2) {
- dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
- std::to_string(s_pipeline_index) +
- "_1_after_swap.pnm");
+ if (dev.model->is_cis && session.params.channels == 3) {
+ pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev.model->line_mode_color_order);
+
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_4_after_merge_mono.tiff");
+ }
}
- if (dev->model->is_cis && session.params.channels == 3) {
- dev->pipeline.push_node<ImagePipelineNodeMergeMonoLines>(dev->model->line_mode_color_order);
+ if (pipeline.get_output_format() == PixelFormat::BGR888) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
}
- if (dev->pipeline.get_output_format() == PixelFormat::BGR888) {
- dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB888);
+ if (pipeline.get_output_format() == PixelFormat::BGR161616) {
+ pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
}
- if (dev->pipeline.get_output_format() == PixelFormat::BGR161616) {
- dev->pipeline.push_node<ImagePipelineNodeFormatConvert>(PixelFormat::RGB161616);
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_5_after_format.tiff");
}
if (session.max_color_shift_lines > 0 && session.params.channels == 3) {
- dev->pipeline.push_node<ImagePipelineNodeComponentShiftLines>(
+ pipeline.push_node<ImagePipelineNodeComponentShiftLines>(
session.color_shift_lines_r,
session.color_shift_lines_g,
session.color_shift_lines_b);
+
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_6_after_color_unshift.tiff");
+ }
}
- if (DBG_LEVEL >= DBG_io2) {
- dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
- std::to_string(s_pipeline_index) +
- "_2_after_shift.pnm");
+ if (!session.stagger_x.empty()) {
+ // FIXME: the image will be scaled to requested pixel count without regard to the reduction
+ // of image size in this step.
+ pipeline.push_node<ImagePipelineNodePixelShiftColumns>(session.stagger_x.shifts());
+
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_7_after_x_unstagger.tiff");
+ }
}
if (session.num_staggered_lines > 0) {
- std::vector<std::size_t> shifts{0, session.num_staggered_lines};
- dev->pipeline.push_node<ImagePipelineNodePixelShiftLines>(shifts);
- }
+ pipeline.push_node<ImagePipelineNodePixelShiftLines>(session.stagger_y.shifts());
- if (DBG_LEVEL >= DBG_io2) {
- dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
- std::to_string(s_pipeline_index) +
- "_3_after_stagger.pnm");
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_8_after_y_unstagger.tiff");
+ }
}
- if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) &&
- !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ if (session.use_host_side_calib &&
+ !has_flag(dev.model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION) &&
+ !has_flag(session.params.flags, ScanFlag::DISABLE_SHADING))
{
- dev->pipeline.push_node<ImagePipelineNodeCalibrate>(dev->dark_average_data,
- dev->white_average_data);
+ unsigned offset_pixels = session.params.startx + dev.calib_session.shading_pixel_offset;
+ unsigned offset_bytes = offset_pixels * dev.calib_session.params.channels;
+ pipeline.push_node<ImagePipelineNodeCalibrate>(dev.dark_average_data,
+ dev.white_average_data, offset_bytes);
- if (DBG_LEVEL >= DBG_io2) {
- dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
- std::to_string(s_pipeline_index) +
- "_4_after_calibrate.pnm");
+ if (log_image_data) {
+ pipeline.push_node<ImagePipelineNodeDebug>(debug_prefix + "_9_after_calibrate.tiff");
}
}
- if (session.output_pixels != session.params.get_requested_pixels()) {
- dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());
+ if (pipeline.get_output_width() != session.params.get_requested_pixels()) {
+ pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());
}
- auto read_from_pipeline = [dev](std::size_t size, std::uint8_t* out_data)
+ return pipeline;
+}
+
+void setup_image_pipeline(Genesys_Device& dev, const ScanSession& session)
+{
+ static unsigned s_pipeline_index = 0;
+
+ s_pipeline_index++;
+
+ dev.pipeline = build_image_pipeline(dev, session, s_pipeline_index, dbg_log_image_data());
+
+ auto read_from_pipeline = [&dev](std::size_t size, std::uint8_t* out_data)
{
- (void) size; // will be always equal to dev->pipeline.get_output_row_bytes()
- return dev->pipeline.get_next_row_data(out_data);
+ (void) size; // will be always equal to dev.pipeline.get_output_row_bytes()
+ return dev.pipeline.get_next_row_data(out_data);
};
- dev->pipeline_buffer = ImageBuffer{dev->pipeline.get_output_row_bytes(),
+ dev.pipeline_buffer = ImageBuffer{dev.pipeline.get_output_row_bytes(),
read_from_pipeline};
}
@@ -1394,6 +1326,32 @@ std::uint8_t compute_frontend_gain_wolfson(float value, float target_value)
return clamp(code, 0, 255);
}
+std::uint8_t compute_frontend_gain_lide_80(float value, float target_value)
+{
+ int code = static_cast<int>((target_value / value) * 12);
+ return clamp(code, 0, 255);
+}
+
+std::uint8_t compute_frontend_gain_wolfson_gl841(float value, float target_value)
+{
+ // this code path is similar to what generic wolfson code path uses and uses similar constants,
+ // but is likely incorrect.
+ float inv_gain = target_value / value;
+ inv_gain *= 0.69f;
+ int code = static_cast<int>(283 - 208 / inv_gain);
+ return clamp(code, 0, 255);
+}
+
+std::uint8_t compute_frontend_gain_wolfson_gl846_gl847_gl124(float value, float target_value)
+{
+ // this code path is similar to what generic wolfson code path uses and uses similar constants,
+ // but is likely incorrect.
+ float inv_gain = target_value / value;
+ int code = static_cast<int>(283 - 208 / inv_gain);
+ return clamp(code, 0, 255);
+}
+
+
std::uint8_t compute_frontend_gain_analog_devices(float value, float target_value)
{
/* The flow of data through the frontend ADC is as follows (see e.g. AD9826 datasheet)
@@ -1418,13 +1376,22 @@ std::uint8_t compute_frontend_gain_analog_devices(float value, float target_valu
std::uint8_t compute_frontend_gain(float value, float target_value,
FrontendType frontend_type)
{
- if (frontend_type == FrontendType::WOLFSON) {
- return compute_frontend_gain_wolfson(value, target_value);
- }
- if (frontend_type == FrontendType::ANALOG_DEVICES) {
- return compute_frontend_gain_analog_devices(value, target_value);
+ switch (frontend_type) {
+ case FrontendType::WOLFSON:
+ return compute_frontend_gain_wolfson(value, target_value);
+ case FrontendType::ANALOG_DEVICES:
+ return compute_frontend_gain_analog_devices(value, target_value);
+ case FrontendType::CANON_LIDE_80:
+ return compute_frontend_gain_lide_80(value, target_value);
+ case FrontendType::WOLFSON_GL841:
+ return compute_frontend_gain_wolfson_gl841(value, target_value);
+ case FrontendType::WOLFSON_GL846:
+ case FrontendType::ANALOG_DEVICES_GL847:
+ case FrontendType::WOLFSON_GL124:
+ return compute_frontend_gain_wolfson_gl846_gl847_gl124(value, target_value);
+ default:
+ throw SaneException("Unknown frontend to compute gain for");
}
- throw SaneException("Unknown frontend to compute gain for");
}
/** @brief initialize device
@@ -1436,7 +1403,7 @@ std::uint8_t compute_frontend_gain(float value, float target_value,
* @param dev device to initialize
* @param max_regs umber of maximum used registers
*/
-void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)
+void sanei_genesys_asic_init(Genesys_Device* dev)
{
DBG_HELPER(dbg);
@@ -1486,8 +1453,7 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)
dev->settings.color_filter = ColorFilter::RED;
- /* duplicate initial values into calibration registers */
- dev->calib_reg = dev->reg;
+ dev->initial_regs = dev->reg;
const auto& sensor = sanei_genesys_find_sensor_any(dev);
@@ -1497,8 +1463,15 @@ void sanei_genesys_asic_init(Genesys_Device* dev, bool /*max_regs*/)
dev->already_initialized = true;
// Move to home if needed
+ if (dev->model->model_id == ModelId::CANON_8600F) {
+ if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::SECONDARY)) {
+ dev->set_head_pos_unknown(ScanHeadId::SECONDARY);
+ }
+ if (!dev->cmd_set->is_head_home(*dev, ScanHeadId::PRIMARY)) {
+ dev->set_head_pos_unknown(ScanHeadId::SECONDARY);
+ }
+ }
dev->cmd_set->move_back_home(dev, true);
- dev->set_head_pos_zero(ScanHeadId::PRIMARY);
// Set powersaving (default = 15 minutes)
dev->cmd_set->set_powersaving(dev, 15);
@@ -1510,6 +1483,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor)
switch (dev.model->asic_type) {
case AsicType::GL646:
case AsicType::GL841:
+ case AsicType::GL842:
case AsicType::GL843:
case AsicType::GL845:
case AsicType::GL846:
@@ -1527,8 +1501,7 @@ void scanner_start_action(Genesys_Device& dev, bool start_motor)
}
}
-void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& sensor,
- unsigned dpihw)
+void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, unsigned dpihw)
{
// same across GL646, GL841, GL843, GL846, GL847, GL124
const uint8_t REG_0x05_DPIHW_MASK = 0xc0;
@@ -1537,10 +1510,6 @@ void sanei_genesys_set_dpihw(Genesys_Register_Set& regs, const Genesys_Sensor& s
const uint8_t REG_0x05_DPIHW_2400 = 0x80;
const uint8_t REG_0x05_DPIHW_4800 = 0xc0;
- if (sensor.register_dpihw_override != 0) {
- dpihw = sensor.register_dpihw_override;
- }
-
uint8_t dpihw_setting;
switch (dpihw) {
case 600:
@@ -1583,6 +1552,12 @@ void regs_set_exposure(AsicType asic_type, Genesys_Register_Set& regs,
regs.set16(gl841::REG_EXPB, exposure.blue);
break;
}
+ case AsicType::GL842: {
+ regs.set16(gl842::REG_EXPR, exposure.red);
+ regs.set16(gl842::REG_EXPG, exposure.green);
+ regs.set16(gl842::REG_EXPB, exposure.blue);
+ break;
+ }
case AsicType::GL843: {
regs.set16(gl843::REG_EXPR, exposure.red);
regs.set16(gl843::REG_EXPG, exposure.green);
@@ -1619,6 +1594,10 @@ void regs_set_optical_off(AsicType asic_type, Genesys_Register_Set& regs)
regs.find_reg(gl841::REG_0x01).value &= ~gl841::REG_0x01_SCAN;
break;
}
+ case AsicType::GL842: {
+ regs.find_reg(gl842::REG_0x01).value &= ~gl842::REG_0x01_SCAN;
+ break;
+ }
case AsicType::GL843: {
regs.find_reg(gl843::REG_0x01).value &= ~gl843::REG_0x01_SCAN;
break;
@@ -1648,6 +1627,8 @@ bool get_registers_gain4_bit(AsicType asic_type, const Genesys_Register_Set& reg
return static_cast<bool>(regs.get8(gl646::REG_0x06) & gl646::REG_0x06_GAIN4);
case AsicType::GL841:
return static_cast<bool>(regs.get8(gl841::REG_0x06) & gl841::REG_0x06_GAIN4);
+ case AsicType::GL842:
+ return static_cast<bool>(regs.get8(gl842::REG_0x06) & gl842::REG_0x06_GAIN4);
case AsicType::GL843:
return static_cast<bool>(regs.get8(gl843::REG_0x06) & gl843::REG_0x06_GAIN4);
case AsicType::GL845:
@@ -1706,79 +1687,78 @@ void sanei_genesys_wait_for_home(Genesys_Device* dev)
}
}
-/** @brief motor profile
- * search for the database of motor profiles and get the best one. Each
- * profile is at full step and at a reference exposure. Use first entry
- * by default.
- * @param motors motor profile database
- * @param motor_type motor id
- * @param exposure exposure time
- * @return a pointer to a Motor_Profile struct
- */
-const Motor_Profile& sanei_genesys_get_motor_profile(const std::vector<Motor_Profile>& motors,
- MotorId motor_id, int exposure)
+const MotorProfile* get_motor_profile_ptr(const std::vector<MotorProfile>& profiles,
+ unsigned exposure,
+ const ScanSession& session)
{
- int idx;
+ int best_i = -1;
+
+ for (unsigned i = 0; i < profiles.size(); ++i) {
+ const auto& profile = profiles[i];
- idx=-1;
- for (std::size_t i = 0; i < motors.size(); ++i) {
- // exact match
- if (motors[i].motor_id == motor_id && motors[i].exposure==exposure) {
- return motors[i];
+ if (!profile.resolutions.matches(session.params.yres)) {
+ continue;
+ }
+ if (!profile.scan_methods.matches(session.params.scan_method)) {
+ continue;
}
- // closest match
- if (motors[i].motor_id == motor_id) {
- /* if profile exposure is higher than the required one,
- * the entry is a candidate for the closest match */
- if (motors[i].exposure == 0 || motors[i].exposure >= exposure)
- {
- if(idx<0)
- {
- /* no match found yet */
- idx=i;
- }
- else
- {
- /* test for better match */
- if(motors[i].exposure<motors[idx].exposure)
- {
- idx=i;
- }
+ if (profile.max_exposure == exposure) {
+ return &profile;
+ }
+
+ if (profile.max_exposure == 0 || profile.max_exposure >= exposure) {
+ if (best_i < 0) {
+ // no match found yet
+ best_i = i;
+ } else {
+ // test for better match
+ if (profiles[i].max_exposure < profiles[best_i].max_exposure) {
+ best_i = i;
}
}
}
}
- /* default fallback */
- if(idx<0)
- {
- DBG (DBG_warn,"%s: using default motor profile\n",__func__);
- idx=0;
+ if (best_i < 0) {
+ return nullptr;
+ }
+
+ return &profiles[best_i];
+}
+
+const MotorProfile& get_motor_profile(const std::vector<MotorProfile>& profiles,
+ unsigned exposure,
+ const ScanSession& session)
+{
+ const auto* profile = get_motor_profile_ptr(profiles, exposure, session);
+ if (profile == nullptr) {
+ throw SaneException("Motor slope is not configured");
}
- return motors[idx];
+ return *profile;
}
-MotorSlopeTable sanei_genesys_slope_table(AsicType asic_type, int dpi, int exposure, int base_dpi,
- unsigned step_multiplier,
- const Motor_Profile& motor_profile)
+MotorSlopeTable create_slope_table(AsicType asic_type, const Genesys_Motor& motor, unsigned ydpi,
+ unsigned exposure, unsigned step_multiplier,
+ const MotorProfile& motor_profile)
{
- unsigned target_speed_w = ((exposure * dpi) / base_dpi);
+ unsigned target_speed_w = ((exposure * ydpi) / motor.base_ydpi);
- auto table = create_slope_table(motor_profile.slope, target_speed_w, motor_profile.step_type,
- step_multiplier, 2 * step_multiplier,
- get_slope_table_max_size(asic_type));
+ auto table = create_slope_table_for_speed(motor_profile.slope, target_speed_w,
+ motor_profile.step_type,
+ step_multiplier, 2 * step_multiplier,
+ get_slope_table_max_size(asic_type));
return table;
}
MotorSlopeTable create_slope_table_fastest(AsicType asic_type, unsigned step_multiplier,
- const Motor_Profile& motor_profile)
+ const MotorProfile& motor_profile)
{
- return create_slope_table(motor_profile.slope, motor_profile.slope.max_speed_w,
- motor_profile.step_type,
- step_multiplier, 2 * step_multiplier,
- get_slope_table_max_size(asic_type));
+ return create_slope_table_for_speed(motor_profile.slope, motor_profile.slope.max_speed_w,
+ motor_profile.step_type,
+ step_multiplier, 2 * step_multiplier,
+ get_slope_table_max_size(asic_type));
}
/** @brief returns the lowest possible ydpi for the device