summaryrefslogtreecommitdiff
path: root/backend/genesys/genesys.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'backend/genesys/genesys.cpp')
-rw-r--r--backend/genesys/genesys.cpp4079
1 files changed, 2185 insertions, 1894 deletions
diff --git a/backend/genesys/genesys.cpp b/backend/genesys/genesys.cpp
index 7c25168..9d80cfa 100644
--- a/backend/genesys/genesys.cpp
+++ b/backend/genesys/genesys.cpp
@@ -61,9 +61,9 @@
#define DEBUG_NOT_STATIC
#include "genesys.h"
-#include "conv.h"
#include "gl124_registers.h"
#include "gl841_registers.h"
+#include "gl842_registers.h"
#include "gl843_registers.h"
#include "gl846_registers.h"
#include "gl847_registers.h"
@@ -73,7 +73,6 @@
#include "test_scanner_interface.h"
#include "test_settings.h"
#include "../include/sane/sanei_config.h"
-#include "../include/sane/sanei_magic.h"
#include <array>
#include <cmath>
@@ -111,8 +110,8 @@ namespace {
static SANE_String_Const mode_list[] = {
SANE_VALUE_SCAN_MODE_COLOR,
SANE_VALUE_SCAN_MODE_GRAY,
- /* SANE_TITLE_HALFTONE, currently unused */
- SANE_VALUE_SCAN_MODE_LINEART,
+ // SANE_TITLE_HALFTONE, not used
+ // SANE_VALUE_SCAN_MODE_LINEART, not used
nullptr
};
@@ -131,12 +130,6 @@ static SANE_String_Const cis_color_filter_list[] = {
nullptr
};
-static SANE_Range swdespeck_range = {
- 1,
- 9,
- 1
-};
-
static SANE_Range time_range = {
0, /* minimum */
60, /* maximum */
@@ -162,15 +155,9 @@ static const SANE_Range u16_range = {
};
static const SANE_Range percentage_range = {
- SANE_FIX (0), /* minimum */
- SANE_FIX (100), /* maximum */
- SANE_FIX (1) /* quantization */
-};
-
-static const SANE_Range threshold_curve_range = {
- 0, /* minimum */
- 127, /* maximum */
- 1 /* quantization */
+ float_to_fixed(0), // minimum
+ float_to_fixed(100), // maximum
+ float_to_fixed(1) // quantization
};
/**
@@ -191,7 +178,7 @@ static const SANE_Range expiration_range = {
1 /* quantization */
};
-const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev)
+const Genesys_Sensor& sanei_genesys_find_sensor_any(const Genesys_Device* dev)
{
DBG_HELPER(dbg);
for (const auto& sensor : *s_sensors) {
@@ -202,7 +189,7 @@ const Genesys_Sensor& sanei_genesys_find_sensor_any(Genesys_Device* dev)
throw std::runtime_error("Given device does not have sensor defined");
}
-Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned channels,
+Genesys_Sensor* find_sensor_impl(const Genesys_Device* dev, unsigned dpi, unsigned channels,
ScanMethod scan_method)
{
DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
@@ -217,7 +204,7 @@ Genesys_Sensor* find_sensor_impl(Genesys_Device* dev, unsigned dpi, unsigned cha
return nullptr;
}
-bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels,
+bool sanei_genesys_has_sensor(const Genesys_Device* dev, unsigned dpi, unsigned channels,
ScanMethod scan_method)
{
DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
@@ -225,8 +212,8 @@ bool sanei_genesys_has_sensor(Genesys_Device* dev, unsigned dpi, unsigned channe
return find_sensor_impl(dev, dpi, channels, scan_method) != nullptr;
}
-const Genesys_Sensor& sanei_genesys_find_sensor(Genesys_Device* dev, unsigned dpi, unsigned channels,
- ScanMethod scan_method)
+const Genesys_Sensor& sanei_genesys_find_sensor(const Genesys_Device* dev, unsigned dpi,
+ unsigned channels, ScanMethod scan_method)
{
DBG_HELPER_ARGS(dbg, "dpi: %d, channels: %d, scan_method: %d", dpi, channels,
static_cast<unsigned>(scan_method));
@@ -250,12 +237,14 @@ Genesys_Sensor& sanei_genesys_find_sensor_for_write(Genesys_Device* dev, unsigne
std::vector<std::reference_wrapper<const Genesys_Sensor>>
- sanei_genesys_find_sensors_all(Genesys_Device* dev, ScanMethod scan_method)
+ sanei_genesys_find_sensors_all(const Genesys_Device* dev, ScanMethod scan_method)
{
DBG_HELPER_ARGS(dbg, "scan_method: %d", static_cast<unsigned>(scan_method));
std::vector<std::reference_wrapper<const Genesys_Sensor>> ret;
- for (const Genesys_Sensor& sensor : sanei_genesys_find_sensors_all_for_write(dev, scan_method)) {
- ret.push_back(sensor);
+ for (auto& sensor : *s_sensors) {
+ if (dev->model->sensor_id == sensor.sensor_id && sensor.method == scan_method) {
+ ret.push_back(sensor);
+ }
}
return ret;
}
@@ -308,6 +297,24 @@ void sanei_genesys_init_structs (Genesys_Device * dev)
}
}
+ if (dev->model->asic_type == AsicType::GL845 ||
+ dev->model->asic_type == AsicType::GL846 ||
+ dev->model->asic_type == AsicType::GL847 ||
+ dev->model->asic_type == AsicType::GL124)
+ {
+ bool memory_layout_found = false;
+ for (const auto& memory_layout : *s_memory_layout) {
+ if (memory_layout.models.matches(dev->model->model_id)) {
+ dev->memory_layout = memory_layout;
+ memory_layout_found = true;
+ break;
+ }
+ }
+ if (!memory_layout_found) {
+ throw SaneException("Could not find memory layout");
+ }
+ }
+
if (!motor_ok || !gpo_ok || !fe_ok) {
throw SaneException("bad description(s) for fe/gpo/motor=%d/%d/%d\n",
static_cast<unsigned>(dev->model->sensor_id),
@@ -316,33 +323,6 @@ void sanei_genesys_init_structs (Genesys_Device * dev)
}
}
-/* Generate slope table for motor movement */
-/**
- * This function generates a slope table using the slope from the motor struct
- * truncated at the given exposure time or step count, whichever comes first.
- * The summed time of the acceleration steps is returned, and the
- * number of accerelation steps is put into used_steps.
- *
- * @param dev Device struct
- * @param slope_table Table to write to
- * @param step_type Generate table for this step_type. 0=>full, 1=>half,
- * 2=>quarter
- * @param exposure_time Minimum exposure time of a scan line
- * @param yres Resolution of a scan line
- * @param used_steps Final number of steps is stored here
- * @return Motor slope table
- * @note all times in pixel time
- */
-MotorSlopeTable sanei_genesys_create_slope_table3(AsicType asic_type, const Genesys_Motor& motor,
- StepType step_type, int exposure_time,
- unsigned yres)
-{
- unsigned target_speed_w = (exposure_time * yres) / motor.base_ydpi;
-
- return create_slope_table(motor.get_slope(step_type), target_speed_w, step_type, 1, 1,
- get_slope_table_max_size(asic_type));
-}
-
/** @brief computes gamma table
* Generates a gamma table of the given length within 0 and the given
* maximum value
@@ -382,7 +362,7 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
int size = 0;
int max = 0;
if (dev->model->asic_type == AsicType::GL646) {
- if (dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) {
+ if (has_flag(dev->model->flags, ModelFlag::GAMMA_14BIT)) {
size = 16384;
} else {
size = 4096;
@@ -411,34 +391,30 @@ void sanei_genesys_create_default_gamma_table(Genesys_Device* dev,
Note: The enhance option of the scanners does _not_ help. It only halves
the amount of pixels transfered.
*/
-SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, float ydpi,
- StepType step_type, int endpixel, int exposure_by_led)
+SANE_Int sanei_genesys_exposure_time2(Genesys_Device * dev, const MotorProfile& profile, float ydpi,
+ int endpixel, int exposure_by_led)
{
int exposure_by_ccd = endpixel + 32;
- unsigned max_speed_motor_w = dev->motor.get_slope(step_type).max_speed_w;
+ unsigned max_speed_motor_w = profile.slope.max_speed_w;
int exposure_by_motor = static_cast<int>((max_speed_motor_w * dev->motor.base_ydpi) / ydpi);
int exposure = exposure_by_ccd;
- if (exposure < exposure_by_motor)
- exposure = exposure_by_motor;
+ if (exposure < exposure_by_motor) {
+ exposure = exposure_by_motor;
+ }
- if (exposure < exposure_by_led && dev->model->is_cis)
- exposure = exposure_by_led;
+ if (exposure < exposure_by_led && dev->model->is_cis) {
+ exposure = exposure_by_led;
+ }
- DBG(DBG_info, "%s: ydpi=%d, step=%d, endpixel=%d led=%d => exposure=%d\n", __func__,
- static_cast<int>(ydpi), static_cast<unsigned>(step_type), endpixel,
- exposure_by_led, exposure);
- return exposure;
+ return exposure;
}
/* Sends a block of shading information to the scanner.
The data is placed at address 0x0000 for color mode, gray mode and
unconditionally for the following CCD chips: HP2300, HP2400 and HP5345
- In the other cases (lineart, halftone on ccd chips not mentioned) the
- addresses are 0x2a00 for dpihw==0, 0x5500 for dpihw==1 and 0xa800 for
- dpihw==2. //Note: why this?
The data needs to be of size "size", and in little endian byte order.
*/
@@ -446,7 +422,6 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S
uint8_t* data, int size)
{
DBG_HELPER_ARGS(dbg, "(size = %d)", size);
- int dpihw;
int start_address;
/* ASIC higher than gl843 doesn't have register 2A/2B, so we route to
@@ -457,84 +432,30 @@ static void genesys_send_offset_and_shading(Genesys_Device* dev, const Genesys_S
return;
}
- /* gl646, gl84[123] case */
- dpihw = dev->reg.get8(0x05) >> 6;
-
- /* TODO invert the test so only the 2 models behaving like that are
- * tested instead of adding all the others */
- /* many scanners send coefficient for lineart/gray like in color mode */
- if ((dev->settings.scan_mode == ScanColorMode::LINEART ||
- dev->settings.scan_mode == ScanColorMode::HALFTONE)
- && dev->model->sensor_id != SensorId::CCD_PLUSTEK_OPTICBOOK_3800
- && dev->model->sensor_id != SensorId::CCD_KVSS080
- && dev->model->sensor_id != SensorId::CCD_G4050
- && dev->model->sensor_id != SensorId::CCD_HP_4850C
- && dev->model->sensor_id != SensorId::CCD_CANON_4400F
- && dev->model->sensor_id != SensorId::CCD_CANON_8400F
- && dev->model->sensor_id != SensorId::CCD_CANON_8600F
- && dev->model->sensor_id != SensorId::CCD_DSMOBILE600
- && dev->model->sensor_id != SensorId::CCD_XP300
- && dev->model->sensor_id != SensorId::CCD_DP665
- && dev->model->sensor_id != SensorId::CCD_DP685
- && dev->model->sensor_id != SensorId::CIS_CANON_LIDE_80
- && dev->model->sensor_id != SensorId::CCD_ROADWARRIOR
- && dev->model->sensor_id != SensorId::CCD_HP2300
- && dev->model->sensor_id != SensorId::CCD_HP2400
- && dev->model->sensor_id != SensorId::CCD_HP3670
- && dev->model->sensor_id != SensorId::CCD_5345) /* lineart, halftone */
- {
- if (dpihw == 0) { /* 600 dpi */
- start_address = 0x02a00;
- } else if (dpihw == 1) { /* 1200 dpi */
- start_address = 0x05500;
- } else if (dpihw == 2) { /* 2400 dpi */
- start_address = 0x0a800;
- } else { /* reserved */
- throw SaneException("unknown dpihw");
- }
- }
- else { // color
- start_address = 0x00;
- }
+ start_address = 0x00;
dev->interface->write_buffer(0x3c, start_address, data, size);
}
-// ?
void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor& sensor,
int pixels_per_line)
{
DBG_HELPER_ARGS(dbg, "pixels_per_line: %d", pixels_per_line);
- if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
- return;
- }
-
- int channels;
- int i;
-
if (dev->cmd_set->has_send_shading_data()) {
return;
}
DBG(DBG_proc, "%s (pixels_per_line = %d)\n", __func__, pixels_per_line);
- // BUG: GRAY shouldn't probably be in the if condition below. Discovered when refactoring
- if (dev->settings.scan_mode == ScanColorMode::GRAY ||
- dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- channels = 3;
- } else {
- channels = 1;
- }
+ unsigned channels = dev->settings.get_channels();
// 16 bit black, 16 bit white
std::vector<uint8_t> shading_data(pixels_per_line * 4 * channels, 0);
uint8_t* shading_data_ptr = shading_data.data();
- for (i = 0; i < pixels_per_line * channels; i++)
- {
+ for (unsigned i = 0; i < pixels_per_line * channels; i++) {
*shading_data_ptr++ = 0x00; /* dark lo */
*shading_data_ptr++ = 0x00; /* dark hi */
*shading_data_ptr++ = 0x00; /* white lo */
@@ -545,184 +466,6 @@ void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor&
pixels_per_line * 4 * channels);
}
-
-// Find the position of the reference point: takes gray level 8 bits data and find
-// first CCD usable pixel and top of scanning area
-void sanei_genesys_search_reference_point(Genesys_Device* dev, Genesys_Sensor& sensor,
- const uint8_t* src_data, int start_pixel, int dpi,
- int width, int height)
-{
- DBG_HELPER(dbg);
- int x, y;
- int current, left, top = 0;
- int size, count;
- int level = 80; /* edge threshold level */
-
- // sanity check
- if ((width < 3) || (height < 3)) {
- throw SaneException("invalid width or height");
- }
-
- /* transformed image data */
- size = width * height;
- std::vector<uint8_t> image2(size, 0);
- std::vector<uint8_t> image(size, 0);
-
- /* laplace filter to denoise picture */
- std::memcpy(image2.data(), src_data, size);
- std::memcpy(image.data(), src_data, size); // to initialize unprocessed part of the image buffer
-
- for (y = 1; y < height - 1; y++) {
- for (x = 1; x < width - 1; x++) {
- image[y * width + x] =
- (image2[(y - 1) * width + x + 1] + 2 * image2[(y - 1) * width + x] +
- image2[(y - 1) * width + x - 1] + 2 * image2[y * width + x + 1] +
- 4 * image2[y * width + x] + 2 * image2[y * width + x - 1] +
- image2[(y + 1) * width + x + 1] + 2 * image2[(y + 1) * width + x] +
- image2[(y + 1) * width + x - 1]) / 16;
- }
- }
-
- image2 = image;
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_laplace.pnm", image.data(), 8, 1, width, height);
-
- /* apply X direction sobel filter
- -1 0 1
- -2 0 2
- -1 0 1
- and finds threshold level
- */
- level = 0;
- for (y = 2; y < height - 2; y++) {
- for (x = 2; x < width - 2; x++) {
- current = image2[(y - 1) * width + x + 1] - image2[(y - 1) * width + x - 1] +
- 2 * image2[y * width + x + 1] - 2 * image2[y * width + x - 1] +
- image2[(y + 1) * width + x + 1] - image2[(y + 1) * width + x - 1];
- if (current < 0)
- current = -current;
- if (current > 255)
- current = 255;
- image[y * width + x] = current;
- if (current > level)
- level = current;
- }
- }
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_xsobel.pnm", image.data(), 8, 1, width, height);
-
- /* set up detection level */
- level = level / 3;
-
- /* find left black margin first
- todo: search top before left
- we average the result of N searches */
- left = 0;
- count = 0;
- for (y = 2; y < 11; y++)
- {
- x = 8;
- while ((x < width / 2) && (image[y * width + x] < level))
- {
- image[y * width + x] = 255;
- x++;
- }
- count++;
- left += x;
- }
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_detected-xsobel.pnm", image.data(), 8, 1, width, height);
- left = left / count;
-
- // turn it in CCD pixel at full sensor optical resolution
- sensor.ccd_start_xoffset = start_pixel + (left * sensor.optical_res) / dpi;
-
- /* find top edge by detecting black strip */
- /* apply Y direction sobel filter
- -1 -2 -1
- 0 0 0
- 1 2 1
- */
- level = 0;
- for (y = 2; y < height - 2; y++) {
- for (x = 2; x < width - 2; x++) {
- current = -image2[(y - 1) * width + x + 1] - 2 * image2[(y - 1) * width + x] -
- image2[(y - 1) * width + x - 1] + image2[(y + 1) * width + x + 1] +
- 2 * image2[(y + 1) * width + x] + image2[(y + 1) * width + x - 1];
- if (current < 0)
- current = -current;
- if (current > 255)
- current = 255;
- image[y * width + x] = current;
- if (current > level)
- level = current;
- }
- }
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_ysobel.pnm", image.data(), 8, 1, width, height);
-
- /* set up detection level */
- level = level / 3;
-
- /* search top of horizontal black stripe : TODO yet another flag */
- if (dev->model->sensor_id == SensorId::CCD_5345
- && dev->model->motor_id == MotorId::MD_5345)
- {
- top = 0;
- count = 0;
- for (x = width / 2; x < width - 1; x++)
- {
- y = 2;
- while ((y < height) && (image[x + y * width] < level))
- {
- image[y * width + x] = 255;
- y++;
- }
- count++;
- top += y;
- }
- if (DBG_LEVEL >= DBG_data)
- sanei_genesys_write_pnm_file("gl_detected-ysobel.pnm", image.data(), 8, 1, width, height);
- top = top / count;
-
- /* bottom of black stripe is of fixed witdh, this hardcoded value
- * will be moved into device struct if more such values are needed */
- top += 10;
- dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi;
- DBG(DBG_info, "%s: black stripe y_offset = %f mm \n", __func__,
- dev->model->y_offset_calib_white.value());
- }
-
- /* find white corner in dark area : TODO yet another flag */
- if ((dev->model->sensor_id == SensorId::CCD_HP2300 && dev->model->motor_id == MotorId::HP2300) ||
- (dev->model->sensor_id == SensorId::CCD_HP2400 && dev->model->motor_id == MotorId::HP2400) ||
- (dev->model->sensor_id == SensorId::CCD_HP3670 && dev->model->motor_id == MotorId::HP3670))
- {
- top = 0;
- count = 0;
- for (x = 10; x < 60; x++)
- {
- y = 2;
- while ((y < height) && (image[x + y * width] < level))
- y++;
- top += y;
- count++;
- }
- top = top / count;
- dev->model->y_offset_calib_white = (top * MM_PER_INCH) / dpi;
- DBG(DBG_info, "%s: white corner y_offset = %f mm\n", __func__,
- dev->model->y_offset_calib_white.value());
- }
-
- DBG(DBG_proc, "%s: ccd_start_xoffset = %d, left = %d, top = %d\n", __func__,
- sensor.ccd_start_xoffset, left, top);
-}
-
-namespace gl843 {
- void gl843_park_xpa_lamp(Genesys_Device* dev);
- void gl843_set_xpa_motor_power(Genesys_Device* dev, Genesys_Register_Set& regs, bool set);
-} // namespace gl843
-
namespace gl124 {
void gl124_setup_scan_gpio(Genesys_Device* dev, int resolution);
} // namespace gl124
@@ -730,6 +473,16 @@ namespace gl124 {
void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)
{
switch (dev.model->asic_type) {
+ case AsicType::GL841: {
+ dev.interface->write_register(gl841::REG_0x0D,
+ gl841::REG_0x0D_CLRLNCNT);
+ break;
+ }
+ case AsicType::GL842: {
+ dev.interface->write_register(gl842::REG_0x0D,
+ gl842::REG_0x0D_CLRLNCNT);
+ break;
+ }
case AsicType::GL843: {
dev.interface->write_register(gl843::REG_0x0D,
gl843::REG_0x0D_CLRLNCNT | gl843::REG_0x0D_CLRMCNT);
@@ -756,34 +509,107 @@ void scanner_clear_scan_and_feed_counts(Genesys_Device& dev)
}
}
-void scanner_clear_scan_and_feed_counts2(Genesys_Device& dev)
+void scanner_send_slope_table(Genesys_Device* dev, const Genesys_Sensor& sensor, unsigned table_nr,
+ const std::vector<uint16_t>& slope_table)
{
- // FIXME: switch to scanner_clear_scan_and_feed_counts when updating tests
- switch (dev.model->asic_type) {
- case AsicType::GL843: {
- dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRLNCNT);
- dev.interface->write_register(gl843::REG_0x0D, gl843::REG_0x0D_CLRMCNT);
+ DBG_HELPER_ARGS(dbg, "table_nr = %d, steps = %zu", table_nr, slope_table.size());
+
+ unsigned max_table_nr = 0;
+ switch (dev->model->asic_type) {
+ case AsicType::GL646: {
+ max_table_nr = 2;
break;
}
+ case AsicType::GL841:
+ case AsicType::GL842:
+ case AsicType::GL843:
case AsicType::GL845:
- case AsicType::GL846: {
- dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRLNCNT);
- dev.interface->write_register(gl846::REG_0x0D, gl846::REG_0x0D_CLRMCNT);
+ case AsicType::GL846:
+ case AsicType::GL847:
+ case AsicType::GL124: {
+ max_table_nr = 4;
break;
}
- case AsicType::GL847: {
- dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRLNCNT);
- dev.interface->write_register(gl847::REG_0x0D, gl847::REG_0x0D_CLRMCNT);
+ default:
+ throw SaneException("Unsupported ASIC type");
+ }
+
+ if (table_nr > max_table_nr) {
+ throw SaneException("invalid table number %d", table_nr);
+ }
+
+ std::vector<uint8_t> table;
+ table.reserve(slope_table.size() * 2);
+ for (std::size_t i = 0; i < slope_table.size(); i++) {
+ table.push_back(slope_table[i] & 0xff);
+ table.push_back(slope_table[i] >> 8);
+ }
+ if (dev->model->asic_type == AsicType::GL841 ||
+ dev->model->model_id == ModelId::CANON_LIDE_90)
+ {
+ // BUG: do this on all gl842 scanners
+ auto max_table_size = get_slope_table_max_size(dev->model->asic_type);
+ table.reserve(max_table_size * 2);
+ while (table.size() < max_table_size * 2) {
+ table.push_back(slope_table.back() & 0xff);
+ table.push_back(slope_table.back() >> 8);
+ }
+ }
+
+ if (dev->interface->is_mock()) {
+ dev->interface->record_slope_table(table_nr, slope_table);
+ }
+
+ switch (dev->model->asic_type) {
+ case AsicType::GL646: {
+ unsigned dpihw = dev->reg.find_reg(0x05).value >> 6;
+ unsigned start_address = 0;
+ if (dpihw == 0) { // 600 dpi
+ start_address = 0x08000;
+ } else if (dpihw == 1) { // 1200 dpi
+ start_address = 0x10000;
+ } else if (dpihw == 2) { // 2400 dpi
+ start_address = 0x1f800;
+ } else {
+ throw SaneException("Unexpected dpihw");
+ }
+ dev->interface->write_buffer(0x3c, start_address + table_nr * 0x100, table.data(),
+ table.size());
break;
}
+ case AsicType::GL841:
+ case AsicType::GL842: {
+ unsigned start_address = 0;
+ switch (sensor.register_dpihw) {
+ case 600: start_address = 0x08000; break;
+ case 1200: start_address = 0x10000; break;
+ case 2400: start_address = 0x20000; break;
+ default: throw SaneException("Unexpected dpihw");
+ }
+ dev->interface->write_buffer(0x3c, start_address + table_nr * 0x200, table.data(),
+ table.size());
+ break;
+ }
+ case AsicType::GL843: {
+ // slope table addresses are fixed : 0x40000, 0x48000, 0x50000, 0x58000, 0x60000
+ // XXX STEF XXX USB 1.1 ? sanei_genesys_write_0x8c (dev, 0x0f, 0x14);
+ dev->interface->write_gamma(0x28, 0x40000 + 0x8000 * table_nr, table.data(),
+ table.size());
+ break;
+ }
+ case AsicType::GL845:
+ case AsicType::GL846:
+ case AsicType::GL847:
case AsicType::GL124: {
- dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRLNCNT);
- dev.interface->write_register(gl124::REG_0x0D, gl124::REG_0x0D_CLRMCNT);
+ // slope table addresses are fixed
+ dev->interface->write_ahb(0x10000000 + 0x4000 * table_nr, table.size(),
+ table.data());
break;
}
default:
- throw SaneException("Unsupported asic type");
+ throw SaneException("Unsupported ASIC type");
}
+
}
bool scanner_is_motor_stopped(Genesys_Device& dev)
@@ -794,9 +620,18 @@ bool scanner_is_motor_stopped(Genesys_Device& dev)
return !status.is_motor_enabled && status.is_feeding_finished;
}
case AsicType::GL841: {
+ auto status = scanner_read_status(dev);
auto reg = dev.interface->read_register(gl841::REG_0x40);
- return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG));
+ return (!(reg & gl841::REG_0x40_DATAENB) && !(reg & gl841::REG_0x40_MOTMFLG) &&
+ !status.is_motor_enabled);
+ }
+ case AsicType::GL842: {
+ auto status = scanner_read_status(dev);
+ auto reg = dev.interface->read_register(gl842::REG_0x40);
+
+ return (!(reg & gl842::REG_0x40_DATAENB) && !(reg & gl842::REG_0x40_MOTMFLG) &&
+ !status.is_motor_enabled);
}
case AsicType::GL843: {
auto status = scanner_read_status(dev);
@@ -832,11 +667,31 @@ bool scanner_is_motor_stopped(Genesys_Device& dev)
}
}
+void scanner_setup_sensor(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+
+ for (const auto& custom_reg : sensor.custom_regs) {
+ regs.set8(custom_reg.address, custom_reg.value);
+ }
+
+ if (dev.model->asic_type != AsicType::GL841 &&
+ dev.model->asic_type != AsicType::GL843)
+ {
+ regs_set_exposure(dev.model->asic_type, regs, sensor.exposure);
+ }
+
+ dev.segment_order = sensor.segment_order;
+}
+
void scanner_stop_action(Genesys_Device& dev)
{
DBG_HELPER(dbg);
switch (dev.model->asic_type) {
+ case AsicType::GL841:
+ case AsicType::GL842:
case AsicType::GL843:
case AsicType::GL845:
case AsicType::GL846:
@@ -847,9 +702,7 @@ void scanner_stop_action(Genesys_Device& dev)
throw SaneException("Unsupported asic type");
}
- if (dev.cmd_set->needs_update_home_sensor_gpio()) {
- dev.cmd_set->update_home_sensor_gpio(dev);
- }
+ dev.cmd_set->update_home_sensor_gpio(dev);
if (scanner_is_motor_stopped(dev)) {
DBG(DBG_info, "%s: already stopped\n", __func__);
@@ -878,6 +731,7 @@ void scanner_stop_action_no_move(Genesys_Device& dev, genesys::Genesys_Register_
switch (dev.model->asic_type) {
case AsicType::GL646:
case AsicType::GL841:
+ case AsicType::GL842:
case AsicType::GL843:
case AsicType::GL845:
case AsicType::GL846:
@@ -908,7 +762,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 3, scan_method);
bool uses_secondary_head = (scan_method == ScanMethod::TRANSPARENCY ||
- scan_method == ScanMethod::TRANSPARENCY_INFRARED);
+ scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
+ (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR));
+
bool uses_secondary_pos = uses_secondary_head &&
dev.model->default_method == ScanMethod::FLATBED;
@@ -934,21 +790,19 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
session.params.yres = resolution;
session.params.startx = 0;
session.params.starty = steps;
- session.params.pixels = 100;
+ session.params.pixels = 50;
session.params.lines = 3;
session.params.depth = 8;
- session.params.channels = 3;
+ session.params.channels = 1;
session.params.scan_method = scan_method;
- session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
- if (dev.model->asic_type == AsicType::GL843) {
- session.params.color_filter = ColorFilter::RED;
- } else {
- session.params.color_filter = dev.settings.color_filter;
- }
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
ScanFlag::FEEDING |
- ScanFlag::IGNORE_LINE_DISTANCE;
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET;
if (dev.model->asic_type == AsicType::GL124) {
session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
@@ -963,20 +817,21 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
if (dev.model->asic_type != AsicType::GL843) {
- regs_set_exposure(dev.model->asic_type, local_reg, {0, 0, 0});
+ regs_set_exposure(dev.model->asic_type, local_reg,
+ sanei_genesys_fixup_exposure({0, 0, 0}));
}
- scanner_clear_scan_and_feed_counts2(dev);
+ scanner_clear_scan_and_feed_counts(dev);
dev.interface->write_registers(local_reg);
if (uses_secondary_head) {
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, true);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY_AND_SECONDARY);
}
try {
scanner_start_action(dev, true);
} catch (...) {
catch_all_exceptions(__func__, [&]() {
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
});
catch_all_exceptions(__func__, [&]() { scanner_stop_action(dev); });
// restore original registers
@@ -992,17 +847,18 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
dev.advance_head_pos_by_steps(ScanHeadId::SECONDARY, direction, steps);
}
- // FIXME: why don't we stop the scanner like on other ASICs
- if (dev.model->asic_type != AsicType::GL843) {
- scanner_stop_action(dev);
- }
+ scanner_stop_action(dev);
if (uses_secondary_head) {
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
}
return;
}
// wait until feed count reaches the required value
+ if (dev.model->model_id == ModelId::CANON_LIDE_700F) {
+ dev.cmd_set->update_home_sensor_gpio(dev);
+ }
+
// FIXME: should porbably wait for some timeout
Status status;
for (unsigned i = 0;; ++i) {
@@ -1015,12 +871,9 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
dev.interface->sleep_ms(10);
}
- // FIXME: why don't we stop the scanner like on other ASICs
- if (dev.model->asic_type != AsicType::GL843) {
- scanner_stop_action(dev);
- }
+ scanner_stop_action(dev);
if (uses_secondary_head) {
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
}
dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, direction, steps);
@@ -1032,11 +885,22 @@ void scanner_move(Genesys_Device& dev, ScanMethod scan_method, unsigned steps, D
dev.interface->sleep_ms(100);
}
+void scanner_move_to_ta(Genesys_Device& dev)
+{
+ DBG_HELPER(dbg);
+
+ unsigned feed = static_cast<unsigned>((dev.model->y_offset_sensor_to_ta * dev.motor.base_ydpi) /
+ MM_PER_INCH);
+ scanner_move(dev, dev.model->default_method, feed, Direction::FORWARD);
+}
+
void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
{
DBG_HELPER_ARGS(dbg, "wait_until_home = %d", wait_until_home);
switch (dev.model->asic_type) {
+ case AsicType::GL841:
+ case AsicType::GL842:
case AsicType::GL843:
case AsicType::GL845:
case AsicType::GL846:
@@ -1047,11 +911,17 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
throw SaneException("Unsupported asic type");
}
+ if (dev.model->is_sheetfed) {
+ dbg.vlog(DBG_proc, "sheetfed scanner, skipping going back home");
+ return;
+ }
+
// FIXME: also check whether the scanner actually has a secondary head
- if (!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
+ if ((!dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
dev.head_pos(ScanHeadId::SECONDARY) > 0 ||
dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
- dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) &&
+ (!has_flag(dev.model->flags, ModelFlag::UTA_NO_SECONDARY_MOTOR)))
{
scanner_move_back_home_ta(dev);
}
@@ -1064,9 +934,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
Direction::BACKWARD);
}
- if (dev.cmd_set->needs_update_home_sensor_gpio()) {
- dev.cmd_set->update_home_sensor_gpio(dev);
- }
+ dev.cmd_set->update_home_sensor_gpio(dev);
auto status = scanner_read_reliable_status(dev);
@@ -1076,15 +944,6 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
return;
}
- if (dev.model->model_id == ModelId::CANON_LIDE_210) {
- // move the head back a little first
- if (dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
- dev.head_pos(ScanHeadId::PRIMARY) > 30)
- {
- scanner_move(dev, dev.model->default_method, 20, Direction::BACKWARD);
- }
- }
-
Genesys_Register_Set local_reg = dev.reg;
unsigned resolution = sanei_genesys_get_lowest_ydpi(&dev);
@@ -1093,28 +952,22 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
ScanSession session;
session.params.xres = resolution;
session.params.yres = resolution;
- session.params.startx = 100;
- if (dev.model->asic_type == AsicType::GL843) {
- session.params.starty = 40000;
- } else {
- session.params.starty = 30000;
- }
- session.params.pixels = 100;
- session.params.lines = 100;
+ session.params.startx = 0;
+ session.params.starty = 40000;
+ session.params.pixels = 50;
+ session.params.lines = 3;
session.params.depth = 8;
session.params.channels = 1;
session.params.scan_method = dev.settings.scan_method;
- if (dev.model->asic_type == AsicType::GL843) {
- session.params.scan_mode = ScanColorMode::LINEART;
- session.params.color_filter = dev.settings.color_filter;
- } else {
- session.params.scan_mode = ScanColorMode::GRAY;
- session.params.color_filter = ColorFilter::RED;
- }
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::GREEN;
+
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
- ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET |
ScanFlag::REVERSE;
+
if (dev.model->asic_type == AsicType::GL843) {
session.params.flags |= ScanFlag::DISABLE_BUFFER_FULL_MOVE;
}
@@ -1143,9 +996,7 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
throw;
}
- if (dev.cmd_set->needs_update_home_sensor_gpio()) {
- dev.cmd_set->update_home_sensor_gpio(dev);
- }
+ dev.cmd_set->update_home_sensor_gpio(dev);
if (is_testing_mode()) {
dev.interface->test_checkpoint("move_back_home");
@@ -1174,18 +1025,49 @@ void scanner_move_back_home(Genesys_Device& dev, bool wait_until_home)
// when we come here then the scanner needed too much time for this, so we better stop
// the motor
catch_all_exceptions(__func__, [&](){ scanner_stop_action(dev); });
- dev.set_head_pos_unknown();
+ dev.set_head_pos_unknown(ScanHeadId::PRIMARY | ScanHeadId::SECONDARY);
throw SaneException(SANE_STATUS_IO_ERROR, "timeout while waiting for scanhead to go home");
}
dbg.log(DBG_info, "scanhead is still moving");
}
+namespace {
+ bool should_use_secondary_motor_mode(Genesys_Device& dev)
+ {
+ bool should_use = !dev.is_head_pos_known(ScanHeadId::SECONDARY) ||
+ !dev.is_head_pos_known(ScanHeadId::PRIMARY) ||
+ dev.head_pos(ScanHeadId::SECONDARY) > dev.head_pos(ScanHeadId::PRIMARY);
+ bool supports = dev.model->model_id == ModelId::CANON_8600F;
+ return should_use && supports;
+ }
+
+ void handle_motor_position_after_move_back_home_ta(Genesys_Device& dev, MotorMode motor_mode)
+ {
+ if (motor_mode == MotorMode::SECONDARY) {
+ dev.set_head_pos_zero(ScanHeadId::SECONDARY);
+ return;
+ }
+
+ if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
+ if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
+ dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
+ dev.head_pos(ScanHeadId::SECONDARY));
+ } else {
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+ }
+ dev.set_head_pos_zero(ScanHeadId::SECONDARY);
+ }
+ }
+} // namespace
+
void scanner_move_back_home_ta(Genesys_Device& dev)
{
DBG_HELPER(dbg);
switch (dev.model->asic_type) {
+ case AsicType::GL842:
case AsicType::GL843:
+ case AsicType::GL845:
break;
default:
throw SaneException("Unsupported asic type");
@@ -1199,7 +1081,9 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
const auto& sensor = sanei_genesys_find_sensor(&dev, resolution, 1, scan_method);
if (dev.is_head_pos_known(ScanHeadId::SECONDARY) &&
- dev.head_pos(ScanHeadId::SECONDARY) > 1000)
+ dev.is_head_pos_known(ScanHeadId::PRIMARY) &&
+ dev.head_pos(ScanHeadId::SECONDARY) > 1000 &&
+ dev.head_pos(ScanHeadId::SECONDARY) <= dev.head_pos(ScanHeadId::PRIMARY))
{
// leave 500 steps for regular slow back home
scanner_move(dev, scan_method, dev.head_pos(ScanHeadId::SECONDARY) - 500,
@@ -1209,18 +1093,20 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
ScanSession session;
session.params.xres = resolution;
session.params.yres = resolution;
- session.params.startx = 100;
- session.params.starty = 30000;
- session.params.pixels = 100;
- session.params.lines = 100;
+ session.params.startx = 0;
+ session.params.starty = 40000;
+ session.params.pixels = 50;
+ session.params.lines = 3;
session.params.depth = 8;
session.params.channels = 1;
session.params.scan_method = scan_method;
session.params.scan_mode = ScanColorMode::GRAY;
- session.params.color_filter = ColorFilter::RED;
+ session.params.color_filter = ColorFilter::GREEN;
+
session.params.flags = ScanFlag::DISABLE_SHADING |
ScanFlag::DISABLE_GAMMA |
- ScanFlag::IGNORE_LINE_DISTANCE |
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET |
ScanFlag::REVERSE;
compute_session(&dev, session, sensor);
@@ -1230,7 +1116,11 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
scanner_clear_scan_and_feed_counts(dev);
dev.interface->write_registers(local_reg);
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, true);
+
+ auto motor_mode = should_use_secondary_motor_mode(dev) ? MotorMode::SECONDARY
+ : MotorMode::PRIMARY_AND_SECONDARY;
+
+ dev.cmd_set->set_motor_mode(dev, local_reg, motor_mode);
try {
scanner_start_action(dev, true);
@@ -1244,18 +1134,10 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
if (is_testing_mode()) {
dev.interface->test_checkpoint("move_back_home_ta");
- if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
- if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
- dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
- dev.head_pos(ScanHeadId::SECONDARY));
- } else {
- dev.set_head_pos_zero(ScanHeadId::PRIMARY);
- }
- dev.set_head_pos_zero(ScanHeadId::SECONDARY);
- }
+ handle_motor_position_after_move_back_home_ta(dev, motor_mode);
scanner_stop_action(dev);
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
return;
}
@@ -1266,18 +1148,10 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
if (status.is_at_home) {
dbg.log(DBG_info, "TA reached home position");
- if (dev.is_head_pos_known(ScanHeadId::PRIMARY)) {
- if (dev.head_pos(ScanHeadId::PRIMARY) > dev.head_pos(ScanHeadId::SECONDARY)) {
- dev.advance_head_pos_by_steps(ScanHeadId::PRIMARY, Direction::BACKWARD,
- dev.head_pos(ScanHeadId::SECONDARY));
- } else {
- dev.set_head_pos_zero(ScanHeadId::PRIMARY);
- }
- dev.set_head_pos_zero(ScanHeadId::SECONDARY);
- }
+ handle_motor_position_after_move_back_home_ta(dev, motor_mode);
scanner_stop_action(dev);
- gl843::gl843_set_xpa_motor_power(&dev, local_reg, false);
+ dev.cmd_set->set_motor_mode(dev, local_reg, MotorMode::PRIMARY);
return;
}
@@ -1287,325 +1161,1148 @@ void scanner_move_back_home_ta(Genesys_Device& dev)
throw SaneException("Timeout waiting for XPA lamp to park");
}
-void sanei_genesys_calculate_zmod(bool two_table,
- uint32_t exposure_time,
- const std::vector<uint16_t>& slope_table,
- unsigned acceleration_steps,
- unsigned move_steps,
- unsigned buffer_acceleration_steps,
- uint32_t* out_z1, uint32_t* out_z2)
+void scanner_search_strip(Genesys_Device& dev, bool forward, bool black)
{
- DBG(DBG_info, "%s: two_table=%d\n", __func__, two_table);
+ DBG_HELPER_ARGS(dbg, "%s %s", black ? "black" : "white", forward ? "forward" : "reverse");
- // acceleration total time
- unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps,
- 0, std::plus<unsigned>());
+ if (dev.model->asic_type == AsicType::GL841 && !black && forward) {
+ dev.frontend.set_gain(0, 0xff);
+ dev.frontend.set_gain(1, 0xff);
+ dev.frontend.set_gain(2, 0xff);
+ }
- /* Z1MOD:
- c = sum(slope_table; reg_stepno)
- d = reg_fwdstep * <cruising speed>
- Z1MOD = (c+d) % exposure_time
- */
- *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time;
+ // set up for a gray scan at lowest dpi
+ const auto& resolution_settings = dev.model->get_resolution_settings(dev.settings.scan_method);
+ unsigned dpi = resolution_settings.get_min_resolution_x();
+ unsigned channels = 1;
- /* Z2MOD:
- a = sum(slope_table; reg_stepno)
- b = move_steps or 1 if 2 tables
- Z1MOD = (a+b) % exposure_time
- */
- if (!two_table) {
- sum = sum + (move_steps * slope_table[acceleration_steps - 1]);
+ auto& sensor = sanei_genesys_find_sensor(&dev, dpi, channels, dev.settings.scan_method);
+ dev.cmd_set->set_fe(&dev, sensor, AFE_SET);
+ scanner_stop_action(dev);
+
+
+ // shading calibration is done with dev.motor.base_ydpi
+ unsigned lines = static_cast<unsigned>(dev.model->y_size_calib_mm * dpi / MM_PER_INCH);
+ if (dev.model->asic_type == AsicType::GL841) {
+ lines = 10; // TODO: use dev.model->search_lines
+ lines = static_cast<unsigned>((lines * dpi) / MM_PER_INCH);
+ }
+
+ unsigned pixels = dev.model->x_size_calib_mm * dpi / MM_PER_INCH;
+
+ dev.set_head_pos_zero(ScanHeadId::PRIMARY);
+
+ unsigned length = 20;
+ if (dev.model->asic_type == AsicType::GL841) {
+ // 20 cm max length for calibration sheet
+ length = static_cast<unsigned>(((200 * dpi) / MM_PER_INCH) / lines);
+ }
+
+ auto local_reg = dev.reg;
+
+ ScanSession session;
+ session.params.xres = dpi;
+ session.params.yres = dpi;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev.settings.scan_method;
+ session.params.scan_mode = ScanColorMode::GRAY;
+ session.params.color_filter = ColorFilter::RED;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA;
+ if (dev.model->asic_type != AsicType::GL841 && !forward) {
+ session.params.flags |= ScanFlag::REVERSE;
+ }
+ compute_session(&dev, session, sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &local_reg, session);
+
+ dev.interface->write_registers(local_reg);
+
+ dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("search_strip");
+ scanner_stop_action(dev);
+ return;
+ }
+
+ wait_until_buffer_non_empty(&dev);
+
+ // now we're on target, we can read data
+ auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
+
+ scanner_stop_action(dev);
+
+ unsigned pass = 0;
+ if (dbg_log_image_data()) {
+ char title[80];
+ std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
+ black ? "black" : "white", forward ? "fwd" : "bwd", pass);
+ write_tiff_file(title, image);
+ }
+
+ // loop until strip is found or maximum pass number done
+ bool found = false;
+ while (pass < length && !found) {
+ dev.interface->write_registers(local_reg);
+
+ // now start scan
+ dev.cmd_set->begin_scan(&dev, sensor, &local_reg, true);
+
+ wait_until_buffer_non_empty(&dev);
+
+ // now we're on target, we can read data
+ image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
+
+ scanner_stop_action(dev);
+
+ if (dbg_log_image_data()) {
+ char title[80];
+ std::sprintf(title, "gl_search_strip_%s_%s%02d.tiff",
+ black ? "black" : "white",
+ forward ? "fwd" : "bwd", static_cast<int>(pass));
+ write_tiff_file(title, image);
+ }
+
+ unsigned white_level = 90;
+ unsigned black_level = 60;
+
+ std::size_t count = 0;
+ // Search data to find black strip
+ // When searching forward, we only need one line of the searched color since we
+ // will scan forward. But when doing backward search, we need all the area of the ame color
+ if (forward) {
+
+ for (std::size_t y = 0; y < image.get_height() && !found; y++) {
+ count = 0;
+
+ // count of white/black pixels depending on the color searched
+ for (std::size_t x = 0; x < image.get_width(); x++) {
+
+ // when searching for black, detect white pixels
+ if (black && image.get_raw_channel(x, y, 0) > white_level) {
+ count++;
+ }
+
+ // when searching for white, detect black pixels
+ if (!black && image.get_raw_channel(x, y, 0) < black_level) {
+ count++;
+ }
+ }
+
+ // at end of line, if count >= 3%, line is not fully of the desired color
+ // so we must go to next line of the buffer */
+ // count*100/pixels < 3
+
+ auto found_percentage = (count * 100 / image.get_width());
+ if (found_percentage < 3) {
+ found = 1;
+ DBG(DBG_data, "%s: strip found forward during pass %d at line %zu\n", __func__,
+ pass, y);
+ } else {
+ DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__,
+ image.get_width(), count, found_percentage);
+ }
+ }
+ } else {
+ /* since calibration scans are done forward, we need the whole area
+ to be of the required color when searching backward
+ */
+ count = 0;
+ for (std::size_t y = 0; y < image.get_height(); y++) {
+ // count of white/black pixels depending on the color searched
+ for (std::size_t x = 0; x < image.get_width(); x++) {
+ // when searching for black, detect white pixels
+ if (black && image.get_raw_channel(x, y, 0) > white_level) {
+ count++;
+ }
+ // when searching for white, detect black pixels
+ if (!black && image.get_raw_channel(x, y, 0) < black_level) {
+ count++;
+ }
+ }
+ }
+
+ // at end of area, if count >= 3%, area is not fully of the desired color
+ // so we must go to next buffer
+ auto found_percentage = count * 100 / (image.get_width() * image.get_height());
+ if (found_percentage < 3) {
+ found = 1;
+ DBG(DBG_data, "%s: strip found backward during pass %d \n", __func__, pass);
+ } else {
+ DBG(DBG_data, "%s: pixels=%zu, count=%zu (%zu%%)\n", __func__, image.get_width(),
+ count, found_percentage);
+ }
+ }
+ pass++;
+ }
+
+ if (found) {
+ DBG(DBG_info, "%s: %s strip found\n", __func__, black ? "black" : "white");
} else {
- sum = sum + slope_table[acceleration_steps - 1];
+ throw SaneException(SANE_STATUS_UNSUPPORTED, "%s strip not found",
+ black ? "black" : "white");
}
- *out_z2 = sum % exposure_time;
}
-static uint8_t genesys_adjust_gain(double* applied_multi, double multi, uint8_t gain)
+static int dark_average_channel(const Image& image, unsigned black, unsigned channel)
+{
+ auto channels = get_pixel_channels(image.get_format());
+
+ unsigned avg[3];
+
+ // computes average values on black margin
+ for (unsigned ch = 0; ch < channels; ch++) {
+ avg[ch] = 0;
+ unsigned count = 0;
+ // FIXME: start with the second line because the black pixels often have noise on the first
+ // line; the cause is probably incorrectly cleaned up previous scan
+ for (std::size_t y = 1; y < image.get_height(); y++) {
+ for (unsigned j = 0; j < black; j++) {
+ avg[ch] += image.get_raw_channel(j, y, ch);
+ count++;
+ }
+ }
+ if (count > 0) {
+ avg[ch] /= count;
+ }
+ DBG(DBG_info, "%s: avg[%d] = %d\n", __func__, ch, avg[ch]);
+ }
+ DBG(DBG_info, "%s: average = %d\n", __func__, avg[channel]);
+ return avg[channel];
+}
+
+bool should_calibrate_only_active_area(const Genesys_Device& dev,
+ const Genesys_Settings& settings)
{
- double voltage, original_voltage;
- uint8_t new_gain = 0;
+ if (settings.scan_method == ScanMethod::TRANSPARENCY ||
+ settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ if (dev.model->model_id == ModelId::CANON_4400F && settings.xres >= 4800) {
+ return true;
+ }
+ if (dev.model->model_id == ModelId::CANON_8600F && settings.xres == 4800) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void scanner_offset_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+
+ if (dev.model->asic_type == AsicType::GL842 &&
+ dev.frontend.layout.type != FrontendType::WOLFSON)
+ {
+ return;
+ }
+
+ if (dev.model->asic_type == AsicType::GL843 &&
+ dev.frontend.layout.type != FrontendType::WOLFSON)
+ {
+ return;
+ }
+
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846)
+ {
+ // no gain nor offset for AKM AFE
+ std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
+ if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
+ return;
+ }
+ }
+ if (dev.model->asic_type == AsicType::GL847) {
+ // no gain nor offset for AKM AFE
+ std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
+ if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
+ return;
+ }
+ }
- DBG(DBG_proc, "%s: multi=%f, gain=%d\n", __func__, multi, gain);
+ if (dev.model->asic_type == AsicType::GL124) {
+ std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
+ if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
+ return;
+ }
+ }
- voltage = 0.5 + gain * 0.25;
- original_voltage = voltage;
+ unsigned target_pixels = dev.model->x_size_calib_mm * sensor.full_resolution / MM_PER_INCH;
+ unsigned start_pixel = 0;
+ unsigned black_pixels = (sensor.black_pixels * sensor.full_resolution) / sensor.full_resolution;
- voltage *= multi;
+ unsigned channels = 3;
+ unsigned lines = 1;
+ unsigned resolution = sensor.full_resolution;
- new_gain = static_cast<std::uint8_t>((voltage - 0.5) * 4);
- if (new_gain > 0x0e)
- new_gain = 0x0e;
+ const Genesys_Sensor* calib_sensor = &sensor;
+ if (dev.model->asic_type == AsicType::GL843) {
+ lines = 8;
- voltage = 0.5 + (new_gain) * 0.25;
+ // compute divider factor to compute final pixels number
+ const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
+ dev.settings.scan_method);
+ resolution = dpihw_sensor.shading_resolution;
+ unsigned factor = sensor.full_resolution / resolution;
- *applied_multi = voltage / original_voltage;
+ calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
+ dev.settings.scan_method);
- DBG(DBG_proc, "%s: orig voltage=%.2f, new voltage=%.2f, *applied_multi=%f, new_gain=%d\n",
- __func__, original_voltage, voltage, *applied_multi, new_gain);
+ target_pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
+ black_pixels = calib_sensor->black_pixels / factor;
- return new_gain;
-}
+ if (should_calibrate_only_active_area(dev, dev.settings)) {
+ float offset = dev.model->x_offset_ta;
+ start_pixel = static_cast<int>((offset * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
+ float size = dev.model->x_size_ta;
+ target_pixels = static_cast<int>((size * calib_sensor->get_optical_resolution()) / MM_PER_INCH);
+ }
-// todo: is return status necessary (unchecked?)
-static void genesys_average_white(Genesys_Device* dev, Genesys_Sensor& sensor, int channels,
- int channel, uint8_t* data, int size, int *max_average)
-{
+ if (dev.model->model_id == ModelId::CANON_4400F &&
+ dev.settings.scan_method == ScanMethod::FLATBED)
+ {
+ return;
+ }
+ }
- DBG_HELPER_ARGS(dbg, "channels=%d, channel=%d, size=%d", channels, channel, size);
- int gain_white_ref, sum, range;
- int average;
- int i;
+ if (dev.model->model_id == ModelId::CANON_5600F) {
+ // FIXME: use same approach as for GL843 scanners
+ lines = 8;
+ }
- range = size / 50;
+ if (dev.model->asic_type == AsicType::GL847) {
+ calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
+ dev.settings.scan_method);
+ }
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
- dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET;
+
+ if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- gain_white_ref = sensor.fau_gain_white_ref * 256;
+ flags |= ScanFlag::USE_XPA;
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = resolution;
+ session.params.startx = start_pixel;
+ session.params.starty = 0;
+ session.params.pixels = target_pixels;
+ session.params.lines = lines;
+ session.params.depth = 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev.settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev.model->asic_type == AsicType::GL843 ? ColorFilter::RED
+ : dev.settings.color_filter;
+ session.params.flags = flags;
+ compute_session(&dev, session, *calib_sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
+
+ unsigned output_pixels = session.output_pixels;
+
+ sanei_genesys_set_motor_power(regs, false);
+
+ int top[3], bottom[3];
+ int topavg[3], bottomavg[3], avg[3];
+
+ // init gain and offset
+ for (unsigned ch = 0; ch < 3; ch++)
+ {
+ bottom[ch] = 10;
+ dev.frontend.set_offset(ch, bottom[ch]);
+ dev.frontend.set_gain(ch, 0);
+ }
+ dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
+
+ // scan with bottom AFE settings
+ dev.interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting first line reading\n", __func__);
+
+ dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("offset_calibration");
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ scanner_stop_action_no_move(dev, regs);
+ }
+ return;
+ }
+
+ Image first_line;
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ first_line = read_unshuffled_image_from_scanner(&dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(dev, regs);
} else {
- gain_white_ref = sensor.gain_white_ref * 256;
+ first_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
+
+ if (dev.model->model_id == ModelId::CANON_5600F) {
+ scanner_stop_action_no_move(dev, regs);
+ }
+ }
+
+ if (dbg_log_image_data()) {
+ char fn[40];
+ std::snprintf(fn, 40, "gl843_bottom_offset_%03d_%03d_%03d.tiff",
+ bottom[0], bottom[1], bottom[2]);
+ write_tiff_file(fn, first_line);
}
- if (range < 1)
- range = 1;
+ for (unsigned ch = 0; ch < 3; ch++) {
+ bottomavg[ch] = dark_average_channel(first_line, black_pixels, ch);
+ DBG(DBG_info, "%s: bottom avg %d=%d\n", __func__, ch, bottomavg[ch]);
+ }
- size = size / (2 * range * channels);
+ // now top value
+ for (unsigned ch = 0; ch < 3; ch++) {
+ top[ch] = 255;
+ dev.frontend.set_offset(ch, top[ch]);
+ }
+ dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
- data += (channel * 2);
+ // scan with top AFE values
+ dev.interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
- *max_average = 0;
+ dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
- while (size--)
+ Image second_line;
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
{
- sum = 0;
- for (i = 0; i < range; i++)
- {
- sum += (*data);
- sum += *(data + 1) * 256;
- data += (2 * channels); /* byte based */
- }
+ second_line = read_unshuffled_image_from_scanner(&dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(dev, regs);
+ } else {
+ second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
- average = (sum / range);
- if (average > *max_average)
- *max_average = average;
+ if (dev.model->model_id == ModelId::CANON_5600F) {
+ scanner_stop_action_no_move(dev, regs);
+ }
}
- DBG(DBG_proc, "%s: max_average=%d, gain_white_ref = %d, finished\n", __func__, *max_average,
- gain_white_ref);
+ for (unsigned ch = 0; ch < 3; ch++){
+ topavg[ch] = dark_average_channel(second_line, black_pixels, ch);
+ DBG(DBG_info, "%s: top avg %d=%d\n", __func__, ch, topavg[ch]);
+ }
+
+ unsigned pass = 0;
+
+ std::vector<std::uint8_t> debug_image;
+ std::size_t debug_image_lines = 0;
+ std::string debug_image_info;
- if (*max_average >= gain_white_ref)
- throw SaneException(SANE_STATUS_INVAL);
+ // loop until acceptable level
+ while ((pass < 32) && ((top[0] - bottom[0] > 1) ||
+ (top[1] - bottom[1] > 1) ||
+ (top[2] - bottom[2] > 1)))
+ {
+ pass++;
+
+ for (unsigned ch = 0; ch < 3; ch++) {
+ if (top[ch] - bottom[ch] > 1) {
+ dev.frontend.set_offset(ch, (top[ch] + bottom[ch]) / 2);
+ }
+ }
+ dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
+
+ // scan with no move
+ dev.interface->write_registers(regs);
+ DBG(DBG_info, "%s: starting second line reading\n", __func__);
+ dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
+
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ second_line = read_unshuffled_image_from_scanner(&dev, session,
+ session.output_total_bytes_raw);
+ scanner_stop_action_no_move(dev, regs);
+ } else {
+ second_line = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
+
+ if (dev.model->model_id == ModelId::CANON_5600F) {
+ scanner_stop_action_no_move(dev, regs);
+ }
+ }
+
+ if (dbg_log_image_data()) {
+ char title[100];
+ std::snprintf(title, 100, "lines: %d pixels_per_line: %d offsets[0..2]: %d %d %d\n",
+ lines, output_pixels,
+ dev.frontend.get_offset(0),
+ dev.frontend.get_offset(1),
+ dev.frontend.get_offset(2));
+ debug_image_info += title;
+ std::copy(second_line.get_row_ptr(0),
+ second_line.get_row_ptr(0) + second_line.get_row_bytes() * second_line.get_height(),
+ std::back_inserter(debug_image));
+ debug_image_lines += lines;
+ }
+
+ for (unsigned ch = 0; ch < 3; ch++) {
+ avg[ch] = dark_average_channel(second_line, black_pixels, ch);
+ DBG(DBG_info, "%s: avg[%d]=%d offset=%d\n", __func__, ch, avg[ch],
+ dev.frontend.get_offset(ch));
+ }
+
+ // compute new boundaries
+ for (unsigned ch = 0; ch < 3; ch++) {
+ if (topavg[ch] >= avg[ch]) {
+ topavg[ch] = avg[ch];
+ top[ch] = dev.frontend.get_offset(ch);
+ } else {
+ bottomavg[ch] = avg[ch];
+ bottom[ch] = dev.frontend.get_offset(ch);
+ }
+ }
+ }
+
+ if (dbg_log_image_data()) {
+ sanei_genesys_write_file("gl_offset_all_desc.txt",
+ reinterpret_cast<const std::uint8_t*>(debug_image_info.data()),
+ debug_image_info.size());
+ write_tiff_file("gl_offset_all.tiff", debug_image.data(), session.params.depth, channels,
+ output_pixels, debug_image_lines);
+ }
+
+ DBG(DBG_info, "%s: offset=(%d,%d,%d)\n", __func__,
+ dev.frontend.get_offset(0),
+ dev.frontend.get_offset(1),
+ dev.frontend.get_offset(2));
}
-/* todo: understand, values are too high */
-static int
-genesys_average_black (Genesys_Device * dev, int channel,
- uint8_t * data, int pixels)
+/* With offset and coarse calibration we only want to get our input range into
+ a reasonable shape. the fine calibration of the upper and lower bounds will
+ be done with shading.
+*/
+void scanner_coarse_gain_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs, unsigned dpi)
{
- int i;
- int sum;
- int pixel_step;
+ DBG_HELPER_ARGS(dbg, "dpi = %d", dpi);
- DBG(DBG_proc, "%s: channel=%d, pixels=%d\n", __func__, channel, pixels);
+ if (dev.model->asic_type == AsicType::GL842 &&
+ dev.frontend.layout.type != FrontendType::WOLFSON)
+ {
+ return;
+ }
- sum = 0;
+ if (dev.model->asic_type == AsicType::GL843 &&
+ dev.frontend.layout.type != FrontendType::WOLFSON)
+ {
+ return;
+ }
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846)
{
- data += (channel * 2);
- pixel_step = 3 * 2;
+ // no gain nor offset for AKM AFE
+ std::uint8_t reg04 = dev.interface->read_register(gl846::REG_0x04);
+ if ((reg04 & gl846::REG_0x04_FESET) == 0x02) {
+ return;
+ }
}
- else
+
+ if (dev.model->asic_type == AsicType::GL847) {
+ // no gain nor offset for AKM AFE
+ std::uint8_t reg04 = dev.interface->read_register(gl847::REG_0x04);
+ if ((reg04 & gl847::REG_0x04_FESET) == 0x02) {
+ return;
+ }
+ }
+
+ if (dev.model->asic_type == AsicType::GL124) {
+ // no gain nor offset for TI AFE
+ std::uint8_t reg0a = dev.interface->read_register(gl124::REG_0x0A);
+ if (((reg0a & gl124::REG_0x0A_SIFSEL) >> gl124::REG_0x0AS_SIFSEL) == 3) {
+ return;
+ }
+ }
+
+ if (dev.model->asic_type == AsicType::GL841) {
+ // feed to white strip if needed
+ if (dev.model->y_offset_calib_white > 0) {
+ unsigned move = static_cast<unsigned>(
+ (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH);
+ scanner_move(dev, dev.model->default_method, move, Direction::FORWARD);
+ }
+ }
+
+ // coarse gain calibration is always done in color mode
+ unsigned channels = 3;
+
+ unsigned resolution = sensor.full_resolution;
+ if (dev.model->asic_type == AsicType::GL841) {
+ const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dev.settings.xres, channels,
+ dev.settings.scan_method);
+ resolution = dpihw_sensor.shading_resolution;
+ }
+
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
{
- pixel_step = 2;
+ const auto& dpihw_sensor = sanei_genesys_find_sensor(&dev, dpi, channels,
+ dev.settings.scan_method);
+ resolution = dpihw_sensor.shading_resolution;
}
- for (i = 0; i < pixels; i++)
+ float coeff = 1;
+
+ // Follow CKSEL
+ if (dev.model->sensor_id == SensorId::CCD_KVSS080 ||
+ dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.model->asic_type == AsicType::GL124)
{
- sum += *data;
- sum += *(data + 1) * 256;
+ if (dev.settings.xres < sensor.full_resolution) {
+ coeff = 0.9f;
+ }
+ }
- data += pixel_step;
+ unsigned lines = 10;
+ if (dev.model->asic_type == AsicType::GL841) {
+ lines = 1;
}
- DBG(DBG_proc, "%s = %d\n", __func__, sum / pixels);
+ const Genesys_Sensor* calib_sensor = &sensor;
+ if (dev.model->asic_type == AsicType::GL841 ||
+ dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843 ||
+ dev.model->asic_type == AsicType::GL847)
+ {
+ calib_sensor = &sanei_genesys_find_sensor(&dev, resolution, channels,
+ dev.settings.scan_method);
+ }
- return sum / pixels;
+ ScanFlag flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET;
+
+ if (dev.settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ flags |= ScanFlag::USE_XPA;
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = dev.model->asic_type == AsicType::GL841 ? dev.settings.yres : resolution;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
+ session.params.lines = lines;
+ session.params.depth = dev.model->asic_type == AsicType::GL841 ? 16 : 8;
+ session.params.channels = channels;
+ session.params.scan_method = dev.settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev.settings.color_filter;
+ session.params.flags = flags;
+ compute_session(&dev, session, *calib_sensor);
+
+ std::size_t pixels = session.output_pixels;
+
+ try {
+ dev.cmd_set->init_regs_for_scan_session(&dev, *calib_sensor, &regs, session);
+ } catch (...) {
+ if (dev.model->asic_type != AsicType::GL841) {
+ catch_all_exceptions(__func__, [&](){ sanei_genesys_set_motor_power(regs, false); });
+ }
+ throw;
+ }
+
+ if (dev.model->asic_type != AsicType::GL841) {
+ sanei_genesys_set_motor_power(regs, false);
+ }
+
+ dev.interface->write_registers(regs);
+
+ if (dev.model->asic_type != AsicType::GL841) {
+ dev.cmd_set->set_fe(&dev, *calib_sensor, AFE_SET);
+ }
+ dev.cmd_set->begin_scan(&dev, *calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("coarse_gain_calibration");
+ scanner_stop_action(dev);
+ dev.cmd_set->move_back_home(&dev, true);
+ return;
+ }
+
+ Image image;
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
+ } else if (dev.model->asic_type == AsicType::GL124) {
+ // BUG: we probably want to read whole image, not just first line
+ image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
+ } else {
+ image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes);
+ }
+
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ scanner_stop_action_no_move(dev, regs);
+ }
+
+ if (dbg_log_image_data()) {
+ write_tiff_file("gl_coarse_gain.tiff", image);
+ }
+
+ for (unsigned ch = 0; ch < channels; ch++) {
+ float curr_output = 0;
+ float target_value = 0;
+
+ if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ std::vector<uint16_t> values;
+ // FIXME: start from the second line because the first line often has artifacts. Probably
+ // caused by unclean cleanup of previous scan
+ for (std::size_t x = pixels / 4; x < (pixels * 3 / 4); x++) {
+ values.push_back(image.get_raw_channel(x, 1, ch));
+ }
+
+ // pick target value at 95th percentile of all values. There may be a lot of black values
+ // in transparency scans for example
+ std::sort(values.begin(), values.end());
+ curr_output = static_cast<float>(values[unsigned((values.size() - 1) * 0.95)]);
+ target_value = calib_sensor->gain_white_ref * coeff;
+
+ } else if (dev.model->asic_type == AsicType::GL841) {
+ // FIXME: use the GL843 approach
+ unsigned max = 0;
+ for (std::size_t x = 0; x < image.get_width(); x++) {
+ auto value = image.get_raw_channel(x, 0, ch);
+ if (value > max) {
+ max = value;
+ }
+ }
+
+ curr_output = max;
+ target_value = 65535.0f;
+ } else {
+ // FIXME: use the GL843 approach
+ auto width = image.get_width();
+
+ std::uint64_t total = 0;
+ for (std::size_t x = width / 4; x < (width * 3 / 4); x++) {
+ total += image.get_raw_channel(x, 0, ch);
+ }
+
+ curr_output = total / (width / 2);
+ target_value = calib_sensor->gain_white_ref * coeff;
+ }
+
+ std::uint8_t out_gain = compute_frontend_gain(curr_output, target_value,
+ dev.frontend.layout.type);
+ dev.frontend.set_gain(ch, out_gain);
+
+ DBG(DBG_proc, "%s: channel %d, curr=%f, target=%f, out_gain:%d\n", __func__, ch,
+ curr_output, target_value, out_gain);
+
+ if (dev.model->asic_type == AsicType::GL841 &&
+ target_value / curr_output > 30)
+ {
+ DBG(DBG_error0, "****************************************\n");
+ DBG(DBG_error0, "* *\n");
+ DBG(DBG_error0, "* Extremely low Brightness detected. *\n");
+ DBG(DBG_error0, "* Check the scanning head is *\n");
+ DBG(DBG_error0, "* unlocked and moving. *\n");
+ DBG(DBG_error0, "* *\n");
+ DBG(DBG_error0, "****************************************\n");
+ throw SaneException(SANE_STATUS_JAMMED, "scanning head is locked");
+ }
+
+ dbg.vlog(DBG_info, "gain=(%d, %d, %d)", dev.frontend.get_gain(0), dev.frontend.get_gain(1),
+ dev.frontend.get_gain(2));
+ }
+
+ if (dev.model->is_cis) {
+ std::uint8_t min_gain = std::min({dev.frontend.get_gain(0),
+ dev.frontend.get_gain(1),
+ dev.frontend.get_gain(2)});
+
+ dev.frontend.set_gain(0, min_gain);
+ dev.frontend.set_gain(1, min_gain);
+ dev.frontend.set_gain(2, min_gain);
+ }
+
+ dbg.vlog(DBG_info, "final gain=(%d, %d, %d)", dev.frontend.get_gain(0),
+ dev.frontend.get_gain(1), dev.frontend.get_gain(2));
+
+ scanner_stop_action(dev);
+
+ dev.cmd_set->move_back_home(&dev, true);
}
+namespace gl124 {
+ void move_to_calibration_area(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs);
+} // namespace gl124
-// todo: check; it works but the lines 1, 2, and 3 are too dark even with the
-// same offset and gain settings?
-static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
+SensorExposure scanner_led_calibration(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
{
- DBG_HELPER_ARGS(dbg, "scan_mode = %d", static_cast<unsigned>(dev->settings.scan_mode));
- int black_pixels;
- int white_average;
- uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 }; /* first value isn't used */
- uint16_t white[12], dark[12];
- int i, j;
+ DBG_HELPER(dbg);
- black_pixels = sensor.black_pixels
- * dev->settings.xres / sensor.optical_res;
+ float move = 0;
- unsigned channels = dev->settings.get_channels();
+ if (dev.model->asic_type == AsicType::GL841) {
+ if (dev.model->y_offset_calib_white > 0) {
+ move = (dev.model->y_offset_calib_white * (dev.motor.base_ydpi)) / MM_PER_INCH;
+ scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
+ Direction::FORWARD);
+ }
+ } else if (dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ // do nothing
+ } else if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847)
+ {
+ move = dev.model->y_offset_calib_white;
+ move = static_cast<float>((move * (dev.motor.base_ydpi / 4)) / MM_PER_INCH);
+ if (move > 20) {
+ scanner_move(dev, dev.model->default_method, static_cast<unsigned>(move),
+ Direction::FORWARD);
+ }
+ } else if (dev.model->asic_type == AsicType::GL124) {
+ gl124::move_to_calibration_area(&dev, sensor, regs);
+ }
- DBG(DBG_info, "channels %d y_size %f xres %d\n", channels, dev->model->y_size.value(),
- dev->settings.xres);
- unsigned size = static_cast<unsigned>(channels * 2 * dev->model->y_size * dev->settings.xres /
- MM_PER_INCH);
- /* 1 1 mm 1/inch inch/mm */
- std::vector<uint8_t> calibration_data(size);
- std::vector<uint8_t> all_data(size * 4, 1);
+ unsigned channels = 3;
+ unsigned resolution = sensor.shading_resolution;
+ const auto& calib_sensor = sanei_genesys_find_sensor(&dev, resolution, channels,
+ dev.settings.scan_method);
+
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.model->asic_type == AsicType::GL124)
+ {
+ regs = dev.reg; // FIXME: apply this to all ASICs
+ }
+
+ unsigned yres = resolution;
+ if (dev.model->asic_type == AsicType::GL841) {
+ yres = dev.settings.yres; // FIXME: remove this
+ }
+
+ ScanSession session;
+ session.params.xres = resolution;
+ session.params.yres = yres;
+ session.params.startx = 0;
+ session.params.starty = 0;
+ session.params.pixels = dev.model->x_size_calib_mm * resolution / MM_PER_INCH;
+ session.params.lines = 1;
+ session.params.depth = 16;
+ session.params.channels = channels;
+ session.params.scan_method = dev.settings.scan_method;
+ session.params.scan_mode = ScanColorMode::COLOR_SINGLE_PASS;
+ session.params.color_filter = dev.settings.color_filter;
+ session.params.flags = ScanFlag::DISABLE_SHADING |
+ ScanFlag::DISABLE_GAMMA |
+ ScanFlag::SINGLE_LINE |
+ ScanFlag::IGNORE_STAGGER_OFFSET |
+ ScanFlag::IGNORE_COLOR_OFFSET;
+ compute_session(&dev, session, calib_sensor);
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, calib_sensor, &regs, session);
+
+ if (dev.model->asic_type == AsicType::GL841) {
+ dev.interface->write_registers(regs); // FIXME: remove this
+ }
+
+ std::uint16_t exp[3];
- dev->cmd_set->set_fe(dev, sensor, AFE_INIT);
+ if (dev.model->asic_type == AsicType::GL841) {
+ exp[0] = sensor.exposure.red;
+ exp[1] = sensor.exposure.green;
+ exp[2] = sensor.exposure.blue;
+ } else {
+ exp[0] = calib_sensor.exposure.red;
+ exp[1] = calib_sensor.exposure.green;
+ exp[2] = calib_sensor.exposure.blue;
+ }
+
+ std::uint16_t target = sensor.gain_white_ref * 256;
- dev->frontend.set_gain(0, 2);
- dev->frontend.set_gain(1, 2);
- dev->frontend.set_gain(2, 2); // TODO: ? was 2
- dev->frontend.set_offset(0, offset[0]);
- dev->frontend.set_offset(1, offset[0]);
- dev->frontend.set_offset(2, offset[0]);
+ std::uint16_t min_exposure = 500; // only gl841
+ std::uint16_t max_exposure = ((exp[0] + exp[1] + exp[2]) / 3) * 2; // only gl841
- for (i = 0; i < 4; i++) /* read 4 lines */
+ std::uint16_t top[3] = {};
+ std::uint16_t bottom[3] = {};
+
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846)
{
- if (i < 3) /* first 3 lines */
- {
- dev->frontend.set_offset(0, offset[i]);
- dev->frontend.set_offset(1, offset[i]);
- dev->frontend.set_offset(2, offset[i]);
+ bottom[0] = 29000;
+ bottom[1] = 29000;
+ bottom[2] = 29000;
+
+ top[0] = 41000;
+ top[1] = 51000;
+ top[2] = 51000;
+ } else if (dev.model->asic_type == AsicType::GL847) {
+ bottom[0] = 28000;
+ bottom[1] = 28000;
+ bottom[2] = 28000;
+
+ top[0] = 32000;
+ top[1] = 32000;
+ top[2] = 32000;
+ }
+
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.model->asic_type == AsicType::GL124)
+ {
+ sanei_genesys_set_motor_power(regs, false);
+ }
+
+ bool acceptable = false;
+ for (unsigned i_test = 0; i_test < 100 && !acceptable; ++i_test) {
+ regs_set_exposure(dev.model->asic_type, regs, { exp[0], exp[1], exp[2] });
+
+ if (dev.model->asic_type == AsicType::GL841) {
+ // FIXME: remove
+ dev.interface->write_register(0x10, (exp[0] >> 8) & 0xff);
+ dev.interface->write_register(0x11, exp[0] & 0xff);
+ dev.interface->write_register(0x12, (exp[1] >> 8) & 0xff);
+ dev.interface->write_register(0x13, exp[1] & 0xff);
+ dev.interface->write_register(0x14, (exp[2] >> 8) & 0xff);
+ dev.interface->write_register(0x15, exp[2] & 0xff);
}
- if (i == 1) /* second line */
- {
- double applied_multi;
- double gain_white_ref;
+ dev.interface->write_registers(regs);
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
- dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
- {
- gain_white_ref = sensor.fau_gain_white_ref * 256;
+ dbg.log(DBG_info, "starting line reading");
+ dev.cmd_set->begin_scan(&dev, calib_sensor, &regs, true);
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint("led_calibration");
+ if (dev.model->asic_type == AsicType::GL841) {
+ scanner_stop_action(dev);
+ dev.cmd_set->move_back_home(&dev, true);
+ return { exp[0], exp[1], exp[2] };
+ } else if (dev.model->asic_type == AsicType::GL124) {
+ scanner_stop_action(dev);
+ return calib_sensor.exposure;
} else {
- gain_white_ref = sensor.gain_white_ref * 256;
+ scanner_stop_action(dev);
+ dev.cmd_set->move_back_home(&dev, true);
+ return calib_sensor.exposure;
}
+ }
- // white and black are defined downwards
-
- uint8_t gain0 = genesys_adjust_gain(&applied_multi,
- gain_white_ref / (white[0] - dark[0]),
- dev->frontend.get_gain(0));
- uint8_t gain1 = genesys_adjust_gain(&applied_multi,
- gain_white_ref / (white[1] - dark[1]),
- dev->frontend.get_gain(1));
- uint8_t gain2 = genesys_adjust_gain(&applied_multi,
- gain_white_ref / (white[2] - dark[2]),
- dev->frontend.get_gain(2));
- // FIXME: looks like overwritten data. Are the above calculations doing
- // anything at all?
- dev->frontend.set_gain(0, gain0);
- dev->frontend.set_gain(1, gain1);
- dev->frontend.set_gain(2, gain2);
- dev->frontend.set_gain(0, 2);
- dev->frontend.set_gain(1, 2);
- dev->frontend.set_gain(2, 2);
-
- dev->interface->write_fe_register(0x28, dev->frontend.get_gain(0));
- dev->interface->write_fe_register(0x29, dev->frontend.get_gain(1));
- dev->interface->write_fe_register(0x2a, dev->frontend.get_gain(2));
- }
+ auto image = read_unshuffled_image_from_scanner(&dev, session, session.output_line_bytes);
- if (i == 3) /* last line */
- {
- double x, y, rate;
+ scanner_stop_action(dev);
- for (j = 0; j < 3; j++)
- {
+ if (dbg_log_image_data()) {
+ char fn[30];
+ std::snprintf(fn, 30, "gl_led_%02d.tiff", i_test);
+ write_tiff_file(fn, image);
+ }
- x = static_cast<double>(dark[(i - 2) * 3 + j] -
- dark[(i - 1) * 3 + j]) * 254 / (offset[i - 1] / 2 -
- offset[i - 2] / 2);
- y = x - x * (offset[i - 1] / 2) / 254 - dark[(i - 1) * 3 + j];
- rate = (x - DARK_VALUE - y) * 254 / x + 0.5;
+ int avg[3];
+ for (unsigned ch = 0; ch < channels; ch++) {
+ avg[ch] = 0;
+ for (std::size_t x = 0; x < image.get_width(); x++) {
+ avg[ch] += image.get_raw_channel(x, 0, ch);
+ }
+ avg[ch] /= image.get_width();
+ }
- uint8_t curr_offset = static_cast<uint8_t>(rate);
+ dbg.vlog(DBG_info, "average: %d, %d, %d", avg[0], avg[1], avg[2]);
- if (curr_offset > 0x7f) {
- curr_offset = 0x7f;
- }
- curr_offset <<= 1;
- dev->frontend.set_offset(j, curr_offset);
- }
- }
- dev->interface->write_fe_register(0x20, dev->frontend.get_offset(0));
- dev->interface->write_fe_register(0x21, dev->frontend.get_offset(1));
- dev->interface->write_fe_register(0x22, dev->frontend.get_offset(2));
+ acceptable = true;
- DBG(DBG_info,
- "%s: doing scan: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__,
- dev->frontend.get_gain(0),
- dev->frontend.get_gain(1),
- dev->frontend.get_gain(2),
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
+ if (dev.model->asic_type == AsicType::GL841) {
+ if (avg[0] < avg[1] * 0.95 || avg[1] < avg[0] * 0.95 ||
+ avg[0] < avg[2] * 0.95 || avg[2] < avg[0] * 0.95 ||
+ avg[1] < avg[2] * 0.95 || avg[2] < avg[1] * 0.95)
+ {
+ acceptable = false;
+ }
+ // led exposure is not acceptable if white level is too low.
+ // ~80 hardcoded value for white level
+ if (avg[0] < 20000 || avg[1] < 20000 || avg[2] < 20000) {
+ acceptable = false;
+ }
- dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false);
+ // for scanners using target value
+ if (target > 0) {
+ acceptable = true;
+ for (unsigned i = 0; i < 3; i++) {
+ // we accept +- 2% delta from target
+ if (std::abs(avg[i] - target) > target / 50) {
+ exp[i] = (exp[i] * target) / avg[i];
+ acceptable = false;
+ }
+ }
+ } else {
+ if (!acceptable) {
+ unsigned avga = (avg[0] + avg[1] + avg[2]) / 3;
+ exp[0] = (exp[0] * avga) / avg[0];
+ exp[1] = (exp[1] * avga) / avg[1];
+ exp[2] = (exp[2] * avga) / avg[2];
+ /* Keep the resulting exposures below this value. Too long exposure drives
+ the ccd into saturation. We may fix this by relying on the fact that
+ we get a striped scan without shading, by means of statistical calculation
+ */
+ unsigned avge = (exp[0] + exp[1] + exp[2]) / 3;
+
+ if (avge > max_exposure) {
+ exp[0] = (exp[0] * max_exposure) / avge;
+ exp[1] = (exp[1] * max_exposure) / avge;
+ exp[2] = (exp[2] * max_exposure) / avge;
+ }
+ if (avge < min_exposure) {
+ exp[0] = (exp[0] * min_exposure) / avge;
+ exp[1] = (exp[1] * min_exposure) / avge;
+ exp[2] = (exp[2] * min_exposure) / avge;
+ }
- if (is_testing_mode()) {
- dev->interface->test_checkpoint("coarse_calibration");
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
- return;
+ }
+ }
+ } else if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846)
+ {
+ for (unsigned i = 0; i < 3; i++) {
+ if (avg[i] < bottom[i]) {
+ if (avg[i] != 0) {
+ exp[i] = (exp[i] * bottom[i]) / avg[i];
+ } else {
+ exp[i] *= 10;
+ }
+ acceptable = false;
+ }
+ if (avg[i] > top[i]) {
+ if (avg[i] != 0) {
+ exp[i] = (exp[i] * top[i]) / avg[i];
+ } else {
+ exp[i] *= 10;
+ }
+ acceptable = false;
+ }
+ }
+ } else if (dev.model->asic_type == AsicType::GL847) {
+ for (unsigned i = 0; i < 3; i++) {
+ if (avg[i] < bottom[i] || avg[i] > top[i]) {
+ auto target = (bottom[i] + top[i]) / 2;
+ if (avg[i] != 0) {
+ exp[i] = (exp[i] * target) / avg[i];
+ } else {
+ exp[i] *= 10;
+ }
+
+ acceptable = false;
+ }
+ }
+ } else if (dev.model->asic_type == AsicType::GL124) {
+ for (unsigned i = 0; i < 3; i++) {
+ // we accept +- 2% delta from target
+ if (std::abs(avg[i] - target) > target / 50) {
+ float prev_weight = 0.5;
+ if (avg[i] != 0) {
+ exp[i] = exp[i] * prev_weight + ((exp[i] * target) / avg[i]) * (1 - prev_weight);
+ } else {
+ exp[i] = exp[i] * prev_weight + (exp[i] * 10) * (1 - prev_weight);
+ }
+ acceptable = false;
+ }
+ }
}
+ }
- sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
- std::memcpy(all_data.data() + i * size, calibration_data.data(), size);
- if (i == 3) /* last line */
- {
- std::vector<uint8_t> all_data_8(size * 4 / 2);
- unsigned int count;
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847 ||
+ dev.model->asic_type == AsicType::GL124)
+ {
+ // set these values as final ones for scan
+ regs_set_exposure(dev.model->asic_type, dev.reg, { exp[0], exp[1], exp[2] });
+ }
- for (count = 0; count < static_cast<unsigned>(size * 4 / 2); count++) {
- all_data_8[count] = all_data[count * 2 + 1];
+ if (dev.model->asic_type == AsicType::GL841 ||
+ dev.model->asic_type == AsicType::GL842 ||
+ dev.model->asic_type == AsicType::GL843)
+ {
+ dev.cmd_set->move_back_home(&dev, true);
+ }
+
+ if (dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847)
+ {
+ if (move > 20) {
+ dev.cmd_set->move_back_home(&dev, true);
}
- sanei_genesys_write_pnm_file("gl_coarse.pnm", all_data_8.data(), 8, channels, size / 6, 4);
- }
+ }
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ dbg.vlog(DBG_info,"acceptable exposure: %d, %d, %d\n", exp[0], exp[1], exp[2]);
- if (dev->settings.scan_mode == ScanColorMode::COLOR_SINGLE_PASS)
- {
- for (j = 0; j < 3; j++)
- {
- genesys_average_white(dev, sensor, 3, j, calibration_data.data(), size, &white_average);
- white[i * 3 + j] = white_average;
- dark[i * 3 + j] =
- genesys_average_black (dev, j, calibration_data.data(),
- black_pixels);
- DBG(DBG_info, "%s: white[%d]=%d, black[%d]=%d\n", __func__,
- i * 3 + j, white[i * 3 + j], i * 3 + j, dark[i * 3 + j]);
- }
- }
- else /* one color-component modes */
- {
- genesys_average_white(dev, sensor, 1, 0, calibration_data.data(), size, &white_average);
- white[i * 3 + 0] = white[i * 3 + 1] = white[i * 3 + 2] =
- white_average;
- dark[i * 3 + 0] = dark[i * 3 + 1] = dark[i * 3 + 2] =
- genesys_average_black (dev, 0, calibration_data.data(), black_pixels);
- }
- } /* for (i = 0; i < 4; i++) */
-
- DBG(DBG_info, "%s: final: gain: %d/%d/%d, offset: %d/%d/%d\n", __func__,
- dev->frontend.get_gain(0),
- dev->frontend.get_gain(1),
- dev->frontend.get_gain(2),
- dev->frontend.get_offset(0),
- dev->frontend.get_offset(1),
- dev->frontend.get_offset(2));
+ return { exp[0], exp[1], exp[2] };
+}
+
+void sanei_genesys_calculate_zmod(bool two_table,
+ uint32_t exposure_time,
+ const std::vector<uint16_t>& slope_table,
+ unsigned acceleration_steps,
+ unsigned move_steps,
+ unsigned buffer_acceleration_steps,
+ uint32_t* out_z1, uint32_t* out_z2)
+{
+ // acceleration total time
+ unsigned sum = std::accumulate(slope_table.begin(), slope_table.begin() + acceleration_steps,
+ 0, std::plus<unsigned>());
+
+ /* Z1MOD:
+ c = sum(slope_table; reg_stepno)
+ d = reg_fwdstep * <cruising speed>
+ Z1MOD = (c+d) % exposure_time
+ */
+ *out_z1 = (sum + buffer_acceleration_steps * slope_table[acceleration_steps - 1]) % exposure_time;
+
+ /* Z2MOD:
+ a = sum(slope_table; reg_stepno)
+ b = move_steps or 1 if 2 tables
+ Z1MOD = (a+b) % exposure_time
+ */
+ if (!two_table) {
+ sum = sum + (move_steps * slope_table[acceleration_steps - 1]);
+ } else {
+ sum = sum + slope_table[acceleration_steps - 1];
+ }
+ *out_z2 = sum % exposure_time;
}
/**
@@ -1614,22 +2311,41 @@ static void genesys_coarse_calibration(Genesys_Device* dev, Genesys_Sensor& sens
* @param dev scanner's device
*/
static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& local_reg,
std::vector<std::uint16_t>& out_average_data,
bool is_dark, const std::string& log_filename_prefix)
{
DBG_HELPER(dbg);
+ if (dev->model->asic_type == AsicType::GL646) {
+ dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
+ local_reg = dev->reg;
+ } else {
+ local_reg = dev->reg;
+ dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
+ dev->interface->write_registers(local_reg);
+ }
+
debug_dump(DBG_info, dev->calib_session);
size_t size;
uint32_t pixels_per_line;
- uint8_t channels;
- /* end pixel - start pixel */
- pixels_per_line = dev->calib_pixels;
- channels = dev->calib_channels;
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843 ||
+ dev->model->model_id == ModelId::CANON_5600F)
+ {
+ pixels_per_line = dev->calib_session.output_pixels;
+ } else {
+ // BUG: this selects incorrect pixel number
+ pixels_per_line = dev->calib_session.params.pixels;
+ }
+ unsigned channels = dev->calib_session.params.channels;
- uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
+ // BUG: we are using wrong pixel number here
+ unsigned start_offset =
+ dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
+ unsigned out_pixels_per_line = pixels_per_line + start_offset;
// FIXME: we set this during both dark and white calibration. A cleaner approach should
// probably be used
@@ -1644,61 +2360,55 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_
}
// FIXME: the current calculation is likely incorrect on non-GL843 implementations,
- // but this needs checking
- if (dev->calib_total_bytes_to_read > 0) {
- size = dev->calib_total_bytes_to_read;
- } else if (dev->model->asic_type == AsicType::GL843) {
- size = channels * 2 * pixels_per_line * dev->calib_lines;
+ // 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)
+ {
+ size = dev->calib_session.output_total_bytes_raw;
} else {
- size = channels * 2 * pixels_per_line * (dev->calib_lines + 1);
+ size = channels * 2 * pixels_per_line * (dev->calib_session.params.lines + 1);
}
std::vector<uint16_t> calibration_data(size / 2);
- bool motor = true;
- if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
- {
- motor = false;
- }
-
// turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
// because they have a calibration sheet with a sufficient black strip
if (is_dark && !dev->model->is_sheetfed) {
- sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, false);
- sanei_genesys_set_motor_power(dev->calib_reg, motor);
+ sanei_genesys_set_lamp_power(dev, sensor, local_reg, false);
} else {
- sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true);
- sanei_genesys_set_motor_power(dev->calib_reg, motor);
+ sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
}
+ sanei_genesys_set_motor_power(local_reg, true);
- dev->interface->write_registers(dev->calib_reg);
+ dev->interface->write_registers(local_reg);
if (is_dark) {
// wait some time to let lamp to get dark
dev->interface->sleep_ms(200);
- } else if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) {
+ } else if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
// make sure lamp is bright again
// FIXME: what about scanners that take a long time to warm the lamp?
dev->interface->sleep_ms(500);
}
bool start_motor = !is_dark;
- dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, start_motor);
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, start_motor);
if (is_testing_mode()) {
dev->interface->test_checkpoint(is_dark ? "dark_shading_calibration"
: "white_shading_calibration");
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ dev->cmd_set->end_scan(dev, &local_reg, true);
return;
}
sanei_genesys_read_data_from_scanner(dev, reinterpret_cast<std::uint8_t*>(calibration_data.data()),
size);
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ dev->cmd_set->end_scan(dev, &local_reg, true);
- if (dev->model->flags & GENESYS_FLAG_16BIT_DATA_INVERTED) {
+ if (has_flag(dev->model->flags, ModelFlag::SWAP_16BIT_DATA)) {
for (std::size_t i = 0; i < size / 2; ++i) {
auto value = calibration_data[i];
value = ((value >> 8) & 0xff) | ((value << 8) & 0xff00);
@@ -1706,30 +2416,29 @@ static void genesys_shading_calibration_impl(Genesys_Device* dev, const Genesys_
}
}
+ if (has_flag(dev->model->flags, ModelFlag::INVERT_PIXEL_DATA)) {
+ for (std::size_t i = 0; i < size / 2; ++i) {
+ calibration_data[i] = 0xffff - calibration_data[i];
+ }
+ }
+
std::fill(out_average_data.begin(),
- out_average_data.begin() + dev->calib_pixels_offset * channels, 0);
+ out_average_data.begin() + start_offset * channels, 0);
- compute_array_percentile_approx(out_average_data.data() + dev->calib_pixels_offset * channels,
+ compute_array_percentile_approx(out_average_data.data() +
+ start_offset * channels,
calibration_data.data(),
- dev->calib_lines, pixels_per_line * channels,
+ dev->calib_session.params.lines, pixels_per_line * channels,
0.5f);
- if (DBG_LEVEL >= DBG_data) {
- sanei_genesys_write_pnm_file16((log_filename_prefix + "_shading.pnm").c_str(),
- calibration_data.data(),
- channels, pixels_per_line, dev->calib_lines);
- sanei_genesys_write_pnm_file16((log_filename_prefix + "_average.pnm").c_str(),
- out_average_data.data(),
- channels, out_pixels_per_line, 1);
+ if (dbg_log_image_data()) {
+ write_tiff_file(log_filename_prefix + "_shading.tiff", calibration_data.data(), 16,
+ channels, pixels_per_line, dev->calib_session.params.lines);
+ write_tiff_file(log_filename_prefix + "_average.tiff", out_average_data.data(), 16,
+ channels, out_pixels_per_line, 1);
}
}
-
-static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
-{
- DBG_HELPER(dbg);
- genesys_shading_calibration_impl(dev, sensor, dev->dark_average_data, true, "gl_black_");
-}
/*
* this function builds dummy dark calibration data so that we can
* compute shading coefficient in a clean way
@@ -1737,18 +2446,28 @@ static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_
* can be computed from previous calibration data (when doing offset
* calibration ?)
*/
-static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor& sensor)
+static void genesys_dark_shading_by_dummy_pixel(Genesys_Device* dev, const Genesys_Sensor& sensor)
{
DBG_HELPER(dbg);
uint32_t pixels_per_line;
- uint8_t channels;
uint32_t skip, xend;
int dummy1, dummy2, dummy3; /* dummy black average per channel */
- pixels_per_line = dev->calib_pixels;
- channels = dev->calib_channels;
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ pixels_per_line = dev->calib_session.output_pixels;
+ } else {
+ pixels_per_line = dev->calib_session.params.pixels;
+ }
+
+ unsigned channels = dev->calib_session.params.channels;
+
+ // BUG: we are using wrong pixel number here
+ unsigned start_offset =
+ dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
- uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
+ unsigned out_pixels_per_line = pixels_per_line + start_offset;
dev->average_size = channels * out_pixels_per_line;
dev->dark_average_data.clear();
@@ -1756,8 +2475,7 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor
/* we average values on 'the left' where CCD pixels are under casing and
give darkest values. We then use these as dummy dark calibration */
- if (dev->settings.xres <= sensor.optical_res / 2)
- {
+ if (dev->settings.xres <= sensor.full_resolution / 2) {
skip = 4;
xend = 36;
}
@@ -1807,17 +2525,22 @@ static void genesys_dummy_dark_shading(Genesys_Device* dev, const Genesys_Sensor
}
}
+static void genesys_dark_shading_by_constant(Genesys_Device& dev)
+{
+ dev.dark_average_data.clear();
+ dev.dark_average_data.resize(dev.average_size, 0x0101);
+}
static void genesys_repark_sensor_before_shading(Genesys_Device* dev)
{
DBG_HELPER(dbg);
- if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) {
+ if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
dev->cmd_set->move_back_home(dev, true);
if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- dev->cmd_set->move_to_ta(dev);
+ scanner_move_to_ta(*dev);
}
}
}
@@ -1825,34 +2548,153 @@ static void genesys_repark_sensor_before_shading(Genesys_Device* dev)
static void genesys_repark_sensor_after_white_shading(Genesys_Device* dev)
{
DBG_HELPER(dbg);
- if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) {
+ if (has_flag(dev->model->flags, ModelFlag::SHADING_REPARK)) {
dev->cmd_set->move_back_home(dev, true);
}
}
-static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor)
+static void genesys_host_shading_calibration_impl(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ std::vector<std::uint16_t>& out_average_data,
+ bool is_dark,
+ const std::string& log_filename_prefix)
{
DBG_HELPER(dbg);
- genesys_shading_calibration_impl(dev, sensor, dev->white_average_data, false, "gl_white_");
+
+ if (is_dark && dev.settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED) {
+ // FIXME: dark shading currently not supported on infrared transparency scans
+ return;
+ }
+
+ auto local_reg = dev.reg;
+ dev.cmd_set->init_regs_for_shading(&dev, sensor, local_reg);
+
+ auto& session = dev.calib_session;
+ debug_dump(DBG_info, session);
+
+ // turn off motor and lamp power for flatbed scanners, but not for sheetfed scanners
+ // because they have a calibration sheet with a sufficient black strip
+ if (is_dark && !dev.model->is_sheetfed) {
+ sanei_genesys_set_lamp_power(&dev, sensor, local_reg, false);
+ } else {
+ sanei_genesys_set_lamp_power(&dev, sensor, local_reg, true);
+ }
+ sanei_genesys_set_motor_power(local_reg, true);
+
+ dev.interface->write_registers(local_reg);
+
+ if (is_dark) {
+ // wait some time to let lamp to get dark
+ dev.interface->sleep_ms(200);
+ } else if (has_flag(dev.model->flags, ModelFlag::DARK_CALIBRATION)) {
+ // make sure lamp is bright again
+ // FIXME: what about scanners that take a long time to warm the lamp?
+ dev.interface->sleep_ms(500);
+ }
+
+ bool start_motor = !is_dark;
+ dev.cmd_set->begin_scan(&dev, sensor, &local_reg, start_motor);
+
+ if (is_testing_mode()) {
+ dev.interface->test_checkpoint(is_dark ? "host_dark_shading_calibration"
+ : "host_white_shading_calibration");
+ dev.cmd_set->end_scan(&dev, &local_reg, true);
+ return;
+ }
+
+ Image image = read_unshuffled_image_from_scanner(&dev, session, session.output_total_bytes_raw);
+ scanner_stop_action(dev);
+
+ auto start_offset = session.params.startx;
+ auto out_pixels_per_line = start_offset + session.output_pixels;
+
+ // FIXME: we set this during both dark and white calibration. A cleaner approach should
+ // probably be used
+ dev.average_size = session.params.channels * out_pixels_per_line;
+
+ out_average_data.clear();
+ out_average_data.resize(dev.average_size);
+
+ std::fill(out_average_data.begin(),
+ out_average_data.begin() + start_offset * session.params.channels, 0);
+
+ compute_array_percentile_approx(out_average_data.data() +
+ start_offset * session.params.channels,
+ reinterpret_cast<std::uint16_t*>(image.get_row_ptr(0)),
+ session.params.lines,
+ session.output_pixels * session.params.channels,
+ 0.5f);
+
+ if (dbg_log_image_data()) {
+ write_tiff_file(log_filename_prefix + "_host_shading.tiff", image);
+ write_tiff_file(log_filename_prefix + "_host_average.tiff", out_average_data.data(), 16,
+ session.params.channels, out_pixels_per_line, 1);
+ }
+}
+
+static void genesys_dark_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& local_reg)
+{
+ DBG_HELPER(dbg);
+ if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
+ genesys_host_shading_calibration_impl(*dev, sensor, dev->dark_average_data, true,
+ "gl_black");
+ } else {
+ genesys_shading_calibration_impl(dev, sensor, local_reg, dev->dark_average_data, true,
+ "gl_black");
+ }
+}
+
+static void genesys_white_shading_calibration(Genesys_Device* dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& local_reg)
+{
+ DBG_HELPER(dbg);
+ if (has_flag(dev->model->flags, ModelFlag::HOST_SIDE_CALIBRATION_COMPLETE_SCAN)) {
+ genesys_host_shading_calibration_impl(*dev, sensor, dev->white_average_data, false,
+ "gl_white");
+ } else {
+ genesys_shading_calibration_impl(dev, sensor, local_reg, dev->white_average_data, false,
+ "gl_white");
+ }
}
// This calibration uses a scan over the calibration target, comprising a black and a white strip.
// (So the motor must be on.)
static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
- const Genesys_Sensor& sensor)
+ const Genesys_Sensor& sensor,
+ Genesys_Register_Set& local_reg)
{
- DBG_HELPER_ARGS(dbg, "lines = %zu", dev->calib_lines);
+ DBG_HELPER(dbg);
+
+ if (dev->model->asic_type == AsicType::GL646) {
+ dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
+ local_reg = dev->reg;
+ } else {
+ local_reg = dev->reg;
+ dev->cmd_set->init_regs_for_shading(dev, sensor, local_reg);
+ dev->interface->write_registers(local_reg);
+ }
+
size_t size;
uint32_t pixels_per_line;
- uint8_t channels;
unsigned int x;
uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col,
dif;
- pixels_per_line = dev->calib_pixels;
- channels = dev->calib_channels;
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ pixels_per_line = dev->calib_session.output_pixels;
+ } else {
+ pixels_per_line = dev->calib_session.params.pixels;
+ }
- uint32_t out_pixels_per_line = pixels_per_line + dev->calib_pixels_offset;
+ unsigned channels = dev->calib_session.params.channels;
+
+ // BUG: we are using wrong pixel number here
+ unsigned start_offset =
+ dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
+
+ unsigned out_pixels_per_line = pixels_per_line + start_offset;
dev->average_size = channels * out_pixels_per_line;
@@ -1862,68 +2704,65 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
dev->dark_average_data.clear();
dev->dark_average_data.resize(dev->average_size);
- if (dev->calib_total_bytes_to_read > 0)
- size = dev->calib_total_bytes_to_read;
- else
- size = channels * 2 * pixels_per_line * dev->calib_lines;
-
- std::vector<uint8_t> calibration_data(size);
-
- bool motor = true;
- if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE)
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843)
{
- motor = false;
+ size = dev->calib_session.output_total_bytes_raw;
+ } else {
+ // FIXME: on GL841 this is different than dev->calib_session.output_total_bytes_raw,
+ // needs checking
+ size = channels * 2 * pixels_per_line * dev->calib_session.params.lines;
}
+ std::vector<uint8_t> calibration_data(size);
+
// turn on motor and lamp power
- sanei_genesys_set_lamp_power(dev, sensor, dev->calib_reg, true);
- sanei_genesys_set_motor_power(dev->calib_reg, motor);
+ sanei_genesys_set_lamp_power(dev, sensor, local_reg, true);
+ sanei_genesys_set_motor_power(local_reg, true);
- dev->interface->write_registers(dev->calib_reg);
+ dev->interface->write_registers(local_reg);
- dev->cmd_set->begin_scan(dev, sensor, &dev->calib_reg, false);
+ dev->cmd_set->begin_scan(dev, sensor, &local_reg, false);
if (is_testing_mode()) {
dev->interface->test_checkpoint("dark_white_shading_calibration");
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ dev->cmd_set->end_scan(dev, &local_reg, true);
return;
}
sanei_genesys_read_data_from_scanner(dev, calibration_data.data(), size);
- dev->cmd_set->end_scan(dev, &dev->calib_reg, true);
+ dev->cmd_set->end_scan(dev, &local_reg, true);
- if (DBG_LEVEL >= DBG_data)
- {
- if (dev->model->is_cis)
- {
- sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(),
- 16, 1, pixels_per_line*channels,
- dev->calib_lines);
- }
- else
- {
- sanei_genesys_write_pnm_file("gl_black_white_shading.pnm", calibration_data.data(),
- 16, channels, pixels_per_line,
- dev->calib_lines);
+ if (dbg_log_image_data()) {
+ if (dev->model->is_cis) {
+ write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
+ 16, 1, pixels_per_line*channels,
+ dev->calib_session.params.lines);
+ } else {
+ write_tiff_file("gl_black_white_shading.tiff", calibration_data.data(),
+ 16, channels, pixels_per_line,
+ dev->calib_session.params.lines);
}
}
std::fill(dev->dark_average_data.begin(),
- dev->dark_average_data.begin() + dev->calib_pixels_offset * channels, 0);
+ dev->dark_average_data.begin() + start_offset * channels, 0);
std::fill(dev->white_average_data.begin(),
- dev->white_average_data.begin() + dev->calib_pixels_offset * channels, 0);
+ dev->white_average_data.begin() + start_offset * channels, 0);
- uint16_t* average_white = dev->white_average_data.data() + dev->calib_pixels_offset * channels;
- uint16_t* average_dark = dev->dark_average_data.data() + dev->calib_pixels_offset * channels;
+ uint16_t* average_white = dev->white_average_data.data() +
+ start_offset * channels;
+ uint16_t* average_dark = dev->dark_average_data.data() +
+ start_offset * channels;
for (x = 0; x < pixels_per_line * channels; x++)
{
dark = 0xffff;
white = 0;
- for (std::size_t y = 0; y < dev->calib_lines; y++)
+ for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
{
col = calibration_data[(x + y * pixels_per_line * channels) * 2];
col |=
@@ -1947,7 +2786,7 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
white_count = 0;
white_sum = 0;
- for (std::size_t y = 0; y < dev->calib_lines; y++)
+ for (std::size_t y = 0; y < dev->calib_session.params.lines; y++)
{
col = calibration_data[(x + y * pixels_per_line * channels) * 2];
col |=
@@ -1974,11 +2813,11 @@ static void genesys_dark_white_shading_calibration(Genesys_Device* dev,
*average_white++ = white_sum;
}
- if (DBG_LEVEL >= DBG_data) {
- sanei_genesys_write_pnm_file16("gl_white_average.pnm", dev->white_average_data.data(),
- channels, out_pixels_per_line, 1);
- sanei_genesys_write_pnm_file16("gl_dark_average.pnm", dev->dark_average_data.data(),
- channels, out_pixels_per_line, 1);
+ if (dbg_log_image_data()) {
+ write_tiff_file("gl_white_average.tiff", dev->white_average_data.data(), 16, channels,
+ out_pixels_per_line, 1);
+ write_tiff_file("gl_dark_average.tiff", dev->dark_average_data.data(), 16, channels,
+ out_pixels_per_line, 1);
}
}
@@ -2085,13 +2924,12 @@ compute_averaged_planar (Genesys_Device * dev, const Genesys_Sensor& sensor,
*/
res = dev->settings.xres;
- if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1)
- {
+ if (sensor.full_resolution > sensor.get_optical_resolution()) {
res *= 2;
}
- /* this should be evenly dividable */
- basepixels = sensor.optical_res / res;
+ // this should be evenly dividable
+ basepixels = sensor.full_resolution / res;
/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
if (basepixels < 1)
@@ -2376,9 +3214,10 @@ compute_shifted_coefficients (Genesys_Device * dev,
auto cmat = color_order_to_cmat(color_order);
x = dev->settings.xres;
- if (sensor.get_ccd_size_divisor_for_dpi(dev->settings.xres) > 1)
- x *= 2; /* scanner is using half-ccd mode */
- basepixels = sensor.optical_res / x; /*this should be evenly dividable */
+ if (sensor.full_resolution > sensor.get_optical_resolution()) {
+ x *= 2; // scanner is using half-ccd mode
+ }
+ basepixels = sensor.full_resolution / x; // this should be evenly dividable
/* gl841 supports 1/1 1/2 1/3 1/4 1/5 1/6 1/8 1/10 1/12 1/15 averaging */
if (basepixels < 1)
@@ -2451,19 +3290,30 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
{
DBG_HELPER(dbg);
- if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
+ if (sensor.use_host_side_calib) {
return;
}
uint32_t pixels_per_line;
- uint8_t channels;
int o;
unsigned int length; /**> number of shading calibration data words */
unsigned int factor;
unsigned int coeff, target_code, words_per_color = 0;
- pixels_per_line = dev->calib_pixels + dev->calib_pixels_offset;
- channels = dev->calib_channels;
+
+ // BUG: we are using wrong pixel number here
+ unsigned start_offset =
+ dev->calib_session.params.startx * sensor.full_resolution / dev->calib_session.params.xres;
+
+ if (dev->model->asic_type == AsicType::GL842 ||
+ dev->model->asic_type == AsicType::GL843)
+ {
+ pixels_per_line = dev->calib_session.output_pixels + start_offset;
+ } else {
+ pixels_per_line = dev->calib_session.params.pixels + start_offset;
+ }
+
+ unsigned channels = dev->calib_session.params.channels;
/* we always build data for three channels, even for gray
* we make the shading data such that each color channel data line is contiguous
@@ -2504,25 +3354,27 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
// contains 16bit words in little endian
std::vector<uint8_t> shading_data(length, 0);
+ if (!dev->calib_session.computed) {
+ genesys_send_offset_and_shading(dev, sensor, shading_data.data(), length);
+ return;
+ }
+
/* TARGET/(Wn-Dn) = white gain -> ~1.xxx then it is multiplied by 0x2000
or 0x4000 to give an integer
Wn = white average for column n
Dn = dark average for column n
*/
- if (get_registers_gain4_bit(dev->model->asic_type, dev->calib_reg)) {
+ if (get_registers_gain4_bit(dev->model->asic_type, dev->reg)) {
coeff = 0x4000;
} else {
coeff = 0x2000;
}
/* compute avg factor */
- if(dev->settings.xres>sensor.optical_res)
- {
- factor=1;
- }
- else
- {
- factor=sensor.optical_res/dev->settings.xres;
+ if (dev->settings.xres > sensor.full_resolution) {
+ factor = 1;
+ } else {
+ factor = sensor.full_resolution / dev->settings.xres;
}
/* for GL646, shading data is planar if REG_0x01_FASTMOD is set and
@@ -2536,6 +3388,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
switch (dev->model->sensor_id)
{
case SensorId::CCD_XP300:
+ case SensorId::CCD_DOCKETPORT_487:
case SensorId::CCD_ROADWARRIOR:
case SensorId::CCD_DP665:
case SensorId::CCD_DP685:
@@ -2570,10 +3423,9 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
case SensorId::CCD_HP2300:
target_code = 0xdc00;
o = 2;
- if(dev->settings.xres<=sensor.optical_res/2)
- {
- o = o - sensor.dummy_pixel / 2;
- }
+ if (dev->settings.xres <= sensor.full_resolution / 2) {
+ o = o - sensor.dummy_pixel / 2;
+ }
compute_coefficients (dev,
shading_data.data(),
pixels_per_line,
@@ -2586,7 +3438,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
case SensorId::CCD_5345:
target_code = 0xe000;
o = 4;
- if(dev->settings.xres<=sensor.optical_res/2)
+ if(dev->settings.xres<=sensor.full_resolution/2)
{
o = o - sensor.dummy_pixel;
}
@@ -2633,9 +3485,12 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
case SensorId::CCD_CANON_4400F:
case SensorId::CCD_CANON_8400F:
case SensorId::CCD_CANON_8600F:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_7200:
case SensorId::CCD_PLUSTEK_OPTICFILM_7200I:
case SensorId::CCD_PLUSTEK_OPTICFILM_7300:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_7400:
case SensorId::CCD_PLUSTEK_OPTICFILM_7500I:
+ case SensorId::CCD_PLUSTEK_OPTICFILM_8200I:
target_code = 0xe000;
o = 0;
compute_coefficients (dev,
@@ -2654,6 +3509,7 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
case SensorId::CIS_CANON_LIDE_120:
case SensorId::CIS_CANON_LIDE_210:
case SensorId::CIS_CANON_LIDE_220:
+ case SensorId::CCD_CANON_5600F:
/* TODO store this in a data struct so we avoid
* growing this switch */
switch(dev->model->sensor_id)
@@ -2684,6 +3540,8 @@ static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_
target_code);
break;
case SensorId::CIS_CANON_LIDE_35:
+ case SensorId::CIS_CANON_LIDE_60:
+ case SensorId::CIS_CANON_LIDE_90:
compute_averaged_planar (dev, sensor,
shading_data.data(),
pixels_per_line,
@@ -2756,9 +3614,8 @@ genesys_restore_calibration(Genesys_Device * dev, Genesys_Sensor& sensor)
/* we don't restore the gamma fields */
sensor.exposure = cache.sensor.exposure;
+ dev->calib_session = cache.session;
dev->average_size = cache.average_size;
- dev->calib_pixels = cache.calib_pixels;
- dev->calib_channels = cache.calib_channels;
dev->dark_average_data = cache.dark_average_data;
dev->white_average_data = cache.white_average_data;
@@ -2812,8 +3669,7 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor&
found_cache_it->frontend = dev->frontend;
found_cache_it->sensor = sensor;
- found_cache_it->calib_pixels = dev->calib_pixels;
- found_cache_it->calib_channels = dev->calib_channels;
+ found_cache_it->session = dev->calib_session;
#ifdef HAVE_SYS_TIME_H
gettimeofday(&time, nullptr);
@@ -2821,20 +3677,13 @@ static void genesys_save_calibration(Genesys_Device* dev, const Genesys_Sensor&
#endif
}
-/**
- * does the calibration process for a flatbed scanner
- * - offset calibration
- * - gain calibration
- * - shading calibration
- * @param dev device to calibrate
- */
static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sensor)
{
DBG_HELPER(dbg);
- uint32_t pixels_per_line;
+ uint32_t pixels_per_line;
- unsigned coarse_res = sensor.optical_res;
- if (dev->settings.yres <= sensor.optical_res / 2) {
+ unsigned coarse_res = sensor.full_resolution;
+ if (dev->settings.yres <= sensor.full_resolution / 2) {
coarse_res /= 2;
}
@@ -2848,35 +3697,29 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen
coarse_res = 1200;
}
- /* do offset calibration if needed */
- if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
- {
+ auto local_reg = dev->initial_regs;
+
+ if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
+ // do ADC calibration first.
dev->interface->record_progress_message("offset_calibration");
- dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
+ dev->cmd_set->offset_calibration(dev, sensor, local_reg);
- /* since all the registers are set up correctly, just use them */
dev->interface->record_progress_message("coarse_gain_calibration");
- dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res);
- } else {
- /* since we have 2 gain calibration proc, skip second if first one was
- used. */
- dev->interface->record_progress_message("init_regs_for_coarse_calibration");
- dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
-
- dev->interface->record_progress_message("genesys_coarse_calibration");
- genesys_coarse_calibration(dev, sensor);
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
}
- if (dev->model->is_cis)
+ if (dev->model->is_cis &&
+ !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
{
- /* the afe now sends valid data for doing led calibration */
+ // ADC now sends correct data, we can configure the exposure for the LEDs
dev->interface->record_progress_message("led_calibration");
switch (dev->model->asic_type) {
case AsicType::GL124:
+ case AsicType::GL841:
case AsicType::GL845:
case AsicType::GL846:
case AsicType::GL847: {
- auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
+ auto calib_exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
for (auto& sensor_update :
sanei_genesys_find_sensors_all_for_write(dev, sensor.method)) {
sensor_update.get().exposure = calib_exposure;
@@ -2885,80 +3728,66 @@ static void genesys_flatbed_calibration(Genesys_Device* dev, Genesys_Sensor& sen
break;
}
default: {
- sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
+ sensor.exposure = dev->cmd_set->led_calibration(dev, sensor, local_reg);
}
}
-
- /* calibrate afe again to match new exposure */
- if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) {
+ if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
+ // recalibrate ADC again for the new LED exposure
dev->interface->record_progress_message("offset_calibration");
- dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
-
- // since all the registers are set up correctly, just use them
+ dev->cmd_set->offset_calibration(dev, sensor, local_reg);
dev->interface->record_progress_message("coarse_gain_calibration");
- dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, coarse_res);
- } else {
- // since we have 2 gain calibration proc, skip second if first one was used
- dev->interface->record_progress_message("init_regs_for_coarse_calibration");
- dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
-
- dev->interface->record_progress_message("genesys_coarse_calibration");
- genesys_coarse_calibration(dev, sensor);
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
}
}
/* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */
- if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR))
- {
+ if (!has_flag(dev->model->flags, ModelFlag::SIS_SENSOR)) {
pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size * dev->settings.xres) /
MM_PER_INCH);
- }
- else
- {
- pixels_per_line = sensor.sensor_pixels;
+ } else {
+ pixels_per_line = static_cast<std::uint32_t>((dev->model->x_size_calib_mm * dev->settings.xres)
+ / MM_PER_INCH);
}
// send default shading data
dev->interface->record_progress_message("sanei_genesys_init_shading_data");
sanei_genesys_init_shading_data(dev, sensor, pixels_per_line);
- if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
- dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
- {
- dev->cmd_set->move_to_ta(dev);
- }
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ scanner_move_to_ta(*dev);
+ }
// shading calibration
- if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION) {
- dev->interface->record_progress_message("init_regs_for_shading");
- dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
-
- dev->interface->record_progress_message("genesys_dark_white_shading_calibration");
- genesys_dark_white_shading_calibration(dev, sensor);
- } else {
- DBG(DBG_proc, "%s : genesys_dark_shading_calibration dev->calib_reg ", __func__);
- debug_dump(DBG_proc, dev->calib_reg);
-
- if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) {
- dev->interface->record_progress_message("init_regs_for_shading");
- dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
+ if (!has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION)) {
+ if (has_flag(dev->model->flags, ModelFlag::DARK_WHITE_CALIBRATION)) {
+ dev->interface->record_progress_message("genesys_dark_white_shading_calibration");
+ genesys_dark_white_shading_calibration(dev, sensor, local_reg);
+ } else {
+ DBG(DBG_proc, "%s : genesys_dark_shading_calibration local_reg ", __func__);
+ debug_dump(DBG_proc, local_reg);
- dev->interface->record_progress_message("genesys_dark_shading_calibration");
- genesys_dark_shading_calibration(dev, sensor);
- genesys_repark_sensor_before_shading(dev);
- }
+ if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
+ dev->interface->record_progress_message("genesys_dark_shading_calibration");
+ genesys_dark_shading_calibration(dev, sensor, local_reg);
+ genesys_repark_sensor_before_shading(dev);
+ }
- dev->interface->record_progress_message("init_regs_for_shading2");
- dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
+ dev->interface->record_progress_message("genesys_white_shading_calibration");
+ genesys_white_shading_calibration(dev, sensor, local_reg);
- dev->interface->record_progress_message("genesys_white_shading_calibration");
- genesys_white_shading_calibration(dev, sensor);
- genesys_repark_sensor_after_white_shading(dev);
+ genesys_repark_sensor_after_white_shading(dev);
- if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) {
- genesys_dummy_dark_shading(dev, sensor);
+ if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
+ if (has_flag(dev->model->flags, ModelFlag::USE_CONSTANT_FOR_DARK_CALIBRATION)) {
+ genesys_dark_shading_by_constant(*dev);
+ } else {
+ genesys_dark_shading_by_dummy_pixel(dev, sensor);
+ }
+ }
}
}
@@ -2982,68 +3811,62 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se
DBG_HELPER(dbg);
bool forward = true;
+ auto local_reg = dev->initial_regs;
+
// first step, load document
dev->cmd_set->load_document(dev);
- /* led, offset and gain calibration are influenced by scan
- * settings. So we set it to sensor resolution */
- dev->settings.xres = sensor.optical_res;
- /* XP200 needs to calibrate a full and half sensor's resolution */
- if (dev->model->sensor_id == SensorId::CIS_XP200 &&
- dev->settings.xres <= sensor.optical_res / 2)
- {
- dev->settings.xres /= 2;
- }
+ unsigned coarse_res = sensor.full_resolution;
/* the afe needs to sends valid data even before calibration */
/* go to a white area */
try {
- dev->cmd_set->search_strip(dev, sensor, forward, false);
+ scanner_search_strip(*dev, forward, false);
} catch (...) {
catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- if (dev->model->is_cis)
- {
- dev->cmd_set->led_calibration(dev, sensor, dev->calib_reg);
+ if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
+ // do ADC calibration first.
+ dev->interface->record_progress_message("offset_calibration");
+ dev->cmd_set->offset_calibration(dev, sensor, local_reg);
+
+ dev->interface->record_progress_message("coarse_gain_calibration");
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
}
- /* calibrate afe */
- if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION)
+ if (dev->model->is_cis &&
+ !has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION))
{
- dev->cmd_set->offset_calibration(dev, sensor, dev->calib_reg);
-
- /* since all the registers are set up correctly, just use them */
+ // ADC now sends correct data, we can configure the exposure for the LEDs
+ dev->interface->record_progress_message("led_calibration");
+ dev->cmd_set->led_calibration(dev, sensor, local_reg);
- dev->cmd_set->coarse_gain_calibration(dev, sensor, dev->calib_reg, sensor.optical_res);
- }
- else
- /* since we have 2 gain calibration proc, skip second if first one was
- used. */
- {
- dev->cmd_set->init_regs_for_coarse_calibration(dev, sensor, dev->calib_reg);
+ if (!has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION)) {
+ // recalibrate ADC again for the new LED exposure
+ dev->interface->record_progress_message("offset_calibration");
+ dev->cmd_set->offset_calibration(dev, sensor, local_reg);
- genesys_coarse_calibration(dev, sensor);
+ dev->interface->record_progress_message("coarse_gain_calibration");
+ dev->cmd_set->coarse_gain_calibration(dev, sensor, local_reg, coarse_res);
+ }
}
/* search for a full width black strip and then do a 16 bit scan to
* gather black shading data */
- if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)
- {
- /* seek black/white reverse/forward */
+ if (has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
+ // seek black/white reverse/forward
try {
- dev->cmd_set->search_strip(dev, sensor, forward, true);
+ scanner_search_strip(*dev, forward, true);
} catch (...) {
catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
}
- dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
-
try {
- genesys_dark_shading_calibration(dev, sensor);
+ genesys_dark_shading_calibration(dev, sensor, local_reg);
} catch (...) {
catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
@@ -3054,7 +3877,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se
/* go to a white area */
try {
- dev->cmd_set->search_strip(dev, sensor, forward, false);
+ scanner_search_strip(*dev, forward, false);
} catch (...) {
catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
throw;
@@ -3062,10 +3885,8 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se
genesys_repark_sensor_before_shading(dev);
- dev->cmd_set->init_regs_for_shading(dev, sensor, dev->calib_reg);
-
try {
- genesys_white_shading_calibration(dev, sensor);
+ genesys_white_shading_calibration(dev, sensor, local_reg);
genesys_repark_sensor_after_white_shading(dev);
} catch (...) {
catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
@@ -3073,17 +3894,9 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se
}
// in case we haven't black shading data, build it from black pixels of white calibration
- // FIXME: shouldn't we use genesys_dummy_dark_shading() ?
- if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) {
- dev->dark_average_data.clear();
- dev->dark_average_data.resize(dev->average_size, 0x0f0f);
- /* XXX STEF XXX
- * with black point in white shading, build an average black
- * pixel and use it to fill the dark_average
- * dev->calib_pixels
- (sensor.sensor_pixels * dev->settings.xres) / sensor.optical_res,
- dev->calib_lines,
- */
+ // FIXME: shouldn't we use genesys_dark_shading_by_dummy_pixel() ?
+ if (!has_flag(dev->model->flags, ModelFlag::DARK_CALIBRATION)) {
+ genesys_dark_shading_by_constant(*dev);
}
/* send the shading coefficient when doing whole line shading
@@ -3099,7 +3912,7 @@ static void genesys_sheetfed_calibration(Genesys_Device* dev, Genesys_Sensor& se
dev->cmd_set->eject_document(dev);
// restore settings
- dev->settings.xres = sensor.optical_res;
+ dev->settings.xres = sensor.full_resolution;
}
/**
@@ -3129,22 +3942,23 @@ static void genesys_warmup_lamp(Genesys_Device* dev)
{
DBG_HELPER(dbg);
unsigned seconds = 0;
- int pixel;
- int channels, total_size;
- double first_average = 0;
- double second_average = 0;
- int difference = 255;
- int lines = 3;
const auto& sensor = sanei_genesys_find_sensor_any(dev);
- dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg, &channels, &total_size);
+ dev->cmd_set->init_regs_for_warmup(dev, sensor, &dev->reg);
+ dev->interface->write_registers(dev->reg);
+
+ auto total_pixels = dev->session.output_pixels;
+ auto total_size = dev->session.output_line_bytes;
+ auto channels = dev->session.params.channels;
+ auto lines = dev->session.output_line_count;
+
std::vector<uint8_t> first_line(total_size);
std::vector<uint8_t> second_line(total_size);
- do
- {
- DBG(DBG_info, "%s: one more loop\n", __func__);
+ do {
+ first_line = second_line;
+
dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
if (is_testing_mode()) {
@@ -3155,72 +3969,44 @@ static void genesys_warmup_lamp(Genesys_Device* dev)
wait_until_buffer_non_empty(dev);
- try {
- sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
- } catch (...) {
- // FIXME: document why this retry is here
- sanei_genesys_read_data_from_scanner(dev, first_line.data(), total_size);
- }
-
+ sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
dev->cmd_set->end_scan(dev, &dev->reg, true);
- dev->interface->sleep_ms(1000);
- seconds++;
+ // compute difference between the two scans
+ double first_average = 0;
+ double second_average = 0;
+ for (unsigned pixel = 0; pixel < total_size; pixel++) {
+ // 16 bit data
+ if (dev->session.params.depth == 16) {
+ first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
+ second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
+ pixel++;
+ } else {
+ first_average += first_line[pixel];
+ second_average += second_line[pixel];
+ }
+ }
- dev->cmd_set->begin_scan(dev, sensor, &dev->reg, false);
+ first_average /= total_pixels;
+ second_average /= total_pixels;
- wait_until_buffer_non_empty(dev);
+ if (dbg_log_image_data()) {
+ write_tiff_file("gl_warmup1.tiff", first_line.data(), dev->session.params.depth,
+ channels, total_size / (lines * channels), lines);
+ write_tiff_file("gl_warmup2.tiff", second_line.data(), dev->session.params.depth,
+ channels, total_size / (lines * channels), lines);
+ }
- sanei_genesys_read_data_from_scanner(dev, second_line.data(), total_size);
- dev->cmd_set->end_scan(dev, &dev->reg, true);
+ DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average,
+ second_average);
- /* compute difference between the two scans */
- for (pixel = 0; pixel < total_size; pixel++)
- {
- // 16 bit data
- if (dev->session.params.depth == 16) {
- first_average += (first_line[pixel] + first_line[pixel + 1] * 256);
- second_average += (second_line[pixel] + second_line[pixel + 1] * 256);
- pixel++;
- }
- else
- {
- first_average += first_line[pixel];
- second_average += second_line[pixel];
- }
- }
- if (dev->session.params.depth == 16) {
- first_average /= pixel;
- second_average /= pixel;
- difference = static_cast<int>(std::fabs(first_average - second_average));
- DBG(DBG_info, "%s: average = %.2f, diff = %.3f\n", __func__,
- 100 * ((second_average) / (256 * 256)),
- 100 * (difference / second_average));
-
- if (second_average > (100 * 256)
- && (difference / second_average) < 0.002)
- break;
- }
- else
- {
- first_average /= pixel;
- second_average /= pixel;
- if (DBG_LEVEL >= DBG_data)
- {
- sanei_genesys_write_pnm_file("gl_warmup1.pnm", first_line.data(), 8, channels,
- total_size / (lines * channels), lines);
- sanei_genesys_write_pnm_file("gl_warmup2.pnm", second_line.data(), 8, channels,
- total_size / (lines * channels), lines);
- }
- DBG(DBG_info, "%s: average 1 = %.2f, average 2 = %.2f\n", __func__, first_average,
- second_average);
- /* if delta below 15/255 ~= 5.8%, lamp is considred warm enough */
- if (fabs (first_average - second_average) < 15
- && second_average > 55)
- break;
- }
+ float average_difference = std::fabs(first_average - second_average) / second_average;
+ if (second_average > 0 && average_difference < 0.005)
+ {
+ dbg.vlog(DBG_info, "difference: %f, exiting", average_difference);
+ break;
+ }
- /* sleep another second before next loop */
dev->interface->sleep_ms(1000);
seconds++;
} while (seconds < WARMUP_TIME);
@@ -3236,6 +4022,37 @@ static void genesys_warmup_lamp(Genesys_Device* dev)
}
}
+static void init_regs_for_scan(Genesys_Device& dev, const Genesys_Sensor& sensor,
+ Genesys_Register_Set& regs)
+{
+ DBG_HELPER(dbg);
+ debug_dump(DBG_info, dev.settings);
+
+ auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, dev.settings);
+
+ if (dev.model->asic_type == AsicType::GL124 ||
+ dev.model->asic_type == AsicType::GL845 ||
+ dev.model->asic_type == AsicType::GL846 ||
+ dev.model->asic_type == AsicType::GL847)
+ {
+ /* Fast move to scan area:
+
+ We don't move fast the whole distance since it would involve computing
+ acceleration/deceleration distance for scan resolution. So leave a remainder for it so
+ scan makes the final move tuning
+ */
+
+ if (dev.settings.get_channels() * dev.settings.yres >= 600 && session.params.starty > 700) {
+ scanner_move(dev, dev.model->default_method,
+ static_cast<unsigned>(session.params.starty - 500),
+ Direction::FORWARD);
+ session.params.starty = 500;
+ }
+ compute_session(&dev, session, sensor);
+ }
+
+ dev.cmd_set->init_regs_for_scan_session(&dev, sensor, &regs, session);
+}
// High-level start of scanning
static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
@@ -3243,6 +4060,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
DBG_HELPER(dbg);
unsigned int steps, expected;
+
/* since not all scanners are set ot wait for head to park
* we check we are not still parking before starting a new scan */
if (dev->parking) {
@@ -3254,38 +4072,30 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
/* wait for lamp warmup : until a warmup for TRANSPARENCY is designed, skip
* it when scanning from XPA. */
- if (!(dev->model->flags & GENESYS_FLAG_SKIP_WARMUP)
- && (dev->settings.scan_method == ScanMethod::FLATBED))
+ if (has_flag(dev->model->flags, ModelFlag::WARMUP) &&
+ (dev->settings.scan_method != ScanMethod::TRANSPARENCY_INFRARED))
{
+ if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
+ dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
+ {
+ scanner_move_to_ta(*dev);
+ }
+
genesys_warmup_lamp(dev);
}
/* set top left x and y values by scanning the internals if flatbed scanners */
if (!dev->model->is_sheetfed) {
- /* do the geometry detection only once */
- if ((dev->model->flags & GENESYS_FLAG_SEARCH_START)
- && (dev->model->y_offset_calib_white == 0))
- {
- dev->cmd_set->search_start_position (dev);
-
- dev->parking = false;
- dev->cmd_set->move_back_home(dev, true);
- }
- else
- {
- /* Go home */
- /* TODO: check we can drop this since we cannot have the
- scanner's head wandering here */
- dev->parking = false;
- dev->cmd_set->move_back_home(dev, true);
- }
+ // TODO: check we can drop this since we cannot have the scanner's head wandering here
+ dev->parking = false;
+ dev->cmd_set->move_back_home(dev, true);
}
/* move to calibration area for transparency adapter */
if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- dev->cmd_set->move_to_ta(dev);
+ scanner_move_to_ta(*dev);
}
/* load document if needed (for sheetfed scanner for instance) */
@@ -3304,22 +4114,18 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
/* try to use cached calibration first */
if (!genesys_restore_calibration (dev, sensor))
{
- /* calibration : sheetfed scanners can't calibrate before each scan */
- /* and also those who have the NO_CALIBRATION flag */
- if (!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION) && !dev->model->is_sheetfed) {
+ // calibration : sheetfed scanners can't calibrate before each scan.
+ // also don't run calibration for those scanners where all passes are disabled
+ bool shading_disabled =
+ has_flag(dev->model->flags, ModelFlag::DISABLE_ADC_CALIBRATION) &&
+ has_flag(dev->model->flags, ModelFlag::DISABLE_EXPOSURE_CALIBRATION) &&
+ has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION);
+ if (!shading_disabled && !dev->model->is_sheetfed) {
genesys_scanner_calibration(dev, sensor);
- genesys_save_calibration (dev, sensor);
- }
- else
- {
+ genesys_save_calibration(dev, sensor);
+ } else {
DBG(DBG_warn, "%s: no calibration done\n", __func__);
- }
- }
-
- /* build look up table for dynamic lineart */
- if (dev->settings.scan_mode == ScanColorMode::LINEART) {
- sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205, dev->settings.threshold_curve,
- dev->settings.threshold-127);
+ }
}
dev->cmd_set->wait_for_motor_stop(dev);
@@ -3331,10 +4137,10 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
if (dev->settings.scan_method == ScanMethod::TRANSPARENCY ||
dev->settings.scan_method == ScanMethod::TRANSPARENCY_INFRARED)
{
- dev->cmd_set->move_to_ta(dev);
+ scanner_move_to_ta(*dev);
}
- dev->cmd_set->init_regs_for_scan(dev, sensor);
+ init_regs_for_scan(*dev, sensor, dev->reg);
/* no lamp during scan */
if (lamp_off) {
@@ -3344,7 +4150,7 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
/* GL124 is using SHDAREA, so we have to wait for scan to be set up before
* sending shading data */
if (dev->cmd_set->has_send_shading_data() &&
- !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
+ !has_flag(dev->model->flags, ModelFlag::DISABLE_SHADING_CALIBRATION))
{
genesys_send_shading_coefficient(dev, sensor);
}
@@ -3386,33 +4192,6 @@ static void genesys_start_scan(Genesys_Device* dev, bool lamp_off)
}
}
-static void genesys_fill_read_buffer(Genesys_Device* dev)
-{
- DBG_HELPER(dbg);
-
- /* for sheetfed scanner, we must check is document is shorter than
- * the requested scan */
- if (dev->model->is_sheetfed) {
- dev->cmd_set->detect_document_end(dev);
- }
-
- std::size_t size = dev->read_buffer.size() - dev->read_buffer.avail();
-
- /* due to sensors and motors, not all data can be directly used. It
- * may have to be read from another intermediate buffer and then processed.
- * There are currently 3 intermediate stages:
- * - handling of odd/even sensors
- * - handling of line interpolation for motors that can't have low
- * enough dpi
- * - handling of multi-segments sensors
- *
- * This is also the place where full duplex data will be handled.
- */
- dev->pipeline_buffer.get_data(size, dev->read_buffer.get_write_pos(size));
-
- dev->read_buffer.produce(size);
-}
-
/* this function does the effective data read in a manner that suits
the scanner. It does data reordering and resizing if need.
It also manages EOF and I/O errors, and line distance correction.
@@ -3422,8 +4201,6 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio
{
DBG_HELPER(dbg);
size_t bytes = 0;
- uint8_t *work_buffer_src;
- Genesys_Buffer *src_buffer;
if (!dev->read_active) {
*len = 0;
@@ -3439,7 +4216,7 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio
{
/* issue park command immediatly in case scanner can handle it
* so we save time */
- if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) &&
+ if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
!dev->parking)
{
dev->cmd_set->move_back_home(dev, false);
@@ -3448,61 +4225,22 @@ static void genesys_read_ordered_data(Genesys_Device* dev, SANE_Byte* destinatio
throw SaneException(SANE_STATUS_EOF, "nothing more to scan: EOF");
}
-/* convert data */
-/*
- 0. fill_read_buffer
--------------- read_buffer ----------------------
- 1a). (opt)uncis (assumes color components to be laid out
- planar)
- 1b). (opt)reverse_RGB (assumes pixels to be BGR or BBGGRR))
--------------- lines_buffer ----------------------
- 2a). (opt)line_distance_correction (assumes RGB or RRGGBB)
- 2b). (opt)unstagger (assumes pixels to be depth*channels/8
- bytes long, unshrinked)
-------------- shrink_buffer ---------------------
- 3. (opt)shrink_lines (assumes component separation in pixels)
--------------- out_buffer -----------------------
- 4. memcpy to destination (for lineart with bit reversal)
-*/
-/*FIXME: for lineart we need sub byte addressing in buffers, or conversion to
- bytes at 0. and back to bits at 4.
-Problems with the first approach:
- - its not clear how to check if we need to output an incomplete byte
- because it is the last one.
- */
-/*FIXME: add lineart support for gl646. in the meantime add logic to convert
- from gray to lineart at the end? would suffer the above problem,
- total_bytes_to_read and total_bytes_read help in that case.
- */
-
if (is_testing_mode()) {
if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
*len = dev->total_bytes_to_read - dev->total_bytes_read;
}
dev->total_bytes_read += *len;
} else {
- genesys_fill_read_buffer(dev);
-
- src_buffer = &(dev->read_buffer);
-
- /* move data to destination */
- bytes = std::min(src_buffer->avail(), *len);
-
- work_buffer_src = src_buffer->get_read_pos();
-
- std::memcpy(destination, work_buffer_src, bytes);
- *len = bytes;
+ if (dev->model->is_sheetfed) {
+ dev->cmd_set->detect_document_end(dev);
+ }
- /* avoid signaling some extra data because we have treated a full block
- * on the last block */
if (dev->total_bytes_read + *len > dev->total_bytes_to_read) {
*len = dev->total_bytes_to_read - dev->total_bytes_read;
}
- /* count bytes sent to frontend */
+ dev->pipeline_buffer.get_data(*len, destination);
dev->total_bytes_read += *len;
-
- src_buffer->consume(bytes);
}
/* end scan if all needed data have been read */
@@ -3576,181 +4314,113 @@ static unsigned pick_resolution(const std::vector<unsigned>& resolutions, unsign
return best_res;
}
-static void calc_parameters(Genesys_Scanner* s)
+static Genesys_Settings calculate_scan_settings(Genesys_Scanner* s)
{
DBG_HELPER(dbg);
- double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0;
- tl_x = SANE_UNFIX(s->pos_top_left_x);
- tl_y = SANE_UNFIX(s->pos_top_left_y);
- br_x = SANE_UNFIX(s->pos_bottom_right_x);
- br_y = SANE_UNFIX(s->pos_bottom_right_y);
+ const auto* dev = s->dev;
+ Genesys_Settings settings;
+ settings.scan_method = s->scan_method;
+ settings.scan_mode = option_string_to_scan_color_mode(s->mode);
- s->params.last_frame = true; /* only single pass scanning supported */
+ settings.depth = s->bit_depth;
- if (s->mode == SANE_VALUE_SCAN_MODE_GRAY || s->mode == SANE_VALUE_SCAN_MODE_LINEART) {
- s->params.format = SANE_FRAME_GRAY;
- } else {
- s->params.format = SANE_FRAME_RGB;
+ if (settings.depth > 8) {
+ settings.depth = 16;
+ } else if (settings.depth < 8) {
+ settings.depth = 1;
}
- if (s->mode == SANE_VALUE_SCAN_MODE_LINEART) {
- s->params.depth = 1;
- } else {
- s->params.depth = s->bit_depth;
- }
-
- s->dev->settings.scan_method = s->scan_method;
- const auto& resolutions = s->dev->model->get_resolution_settings(s->dev->settings.scan_method);
-
- s->dev->settings.depth = s->bit_depth;
+ const auto& resolutions = dev->model->get_resolution_settings(settings.scan_method);
- /* interpolation */
- s->dev->settings.disable_interpolation = s->disable_interpolation;
+ settings.xres = pick_resolution(resolutions.resolutions_x, s->resolution, "X");
+ settings.yres = pick_resolution(resolutions.resolutions_y, s->resolution, "Y");
- // FIXME: use correct sensor
- const auto& sensor = sanei_genesys_find_sensor_any(s->dev);
-
- // hardware settings
- if (static_cast<unsigned>(s->resolution) > sensor.optical_res &&
- s->dev->settings.disable_interpolation)
- {
- s->dev->settings.xres = sensor.optical_res;
- } else {
- s->dev->settings.xres = s->resolution;
- }
- s->dev->settings.yres = s->resolution;
+ settings.tl_x = fixed_to_float(s->pos_top_left_x);
+ settings.tl_y = fixed_to_float(s->pos_top_left_y);
+ float br_x = fixed_to_float(s->pos_bottom_right_x);
+ float br_y = fixed_to_float(s->pos_bottom_right_y);
- s->dev->settings.xres = pick_resolution(resolutions.resolutions_x, s->dev->settings.xres, "X");
- s->dev->settings.yres = pick_resolution(resolutions.resolutions_y, s->dev->settings.yres, "Y");
-
- s->params.lines = static_cast<unsigned>(((br_y - tl_y) * s->dev->settings.yres) /
+ settings.lines = static_cast<unsigned>(((br_y - settings.tl_y) * settings.yres) /
MM_PER_INCH);
- unsigned pixels_per_line = static_cast<unsigned>(((br_x - tl_x) * s->dev->settings.xres) /
- MM_PER_INCH);
-
- /* we need an even pixels number
- * TODO invert test logic or generalize behaviour across all ASICs */
- if ((s->dev->model->flags & GENESYS_FLAG_SIS_SENSOR) ||
- s->dev->model->asic_type == AsicType::GL847 ||
- s->dev->model->asic_type == AsicType::GL124 ||
- s->dev->model->asic_type == AsicType::GL845 ||
- s->dev->model->asic_type == AsicType::GL846 ||
- s->dev->model->asic_type == AsicType::GL843)
- {
- if (s->dev->settings.xres <= 1200) {
- pixels_per_line = (pixels_per_line / 4) * 4;
- } else if (s->dev->settings.xres < s->dev->settings.yres) {
- // 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
- pixels_per_line = (pixels_per_line / 8) * 8;
- } else {
- pixels_per_line = (pixels_per_line / 16) * 16;
- }
- }
-
- /* corner case for true lineart for sensor with several segments
- * or when xres is doubled to match yres */
- if (s->dev->settings.xres >= 1200 && (
- s->dev->model->asic_type == AsicType::GL124 ||
- s->dev->model->asic_type == AsicType::GL847 ||
- s->dev->session.params.xres < s->dev->session.params.yres))
- {
- if (s->dev->settings.xres < s->dev->settings.yres) {
- // 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
- pixels_per_line = (pixels_per_line / 8) * 8;
- } else {
- pixels_per_line = (pixels_per_line / 16) * 16;
- }
- }
-
- unsigned xres_factor = s->resolution / s->dev->settings.xres;
- unsigned bytes_per_line = 0;
-
- if (s->params.depth > 8)
- {
- s->params.depth = 16;
- bytes_per_line = 2 * pixels_per_line;
- }
- else if (s->params.depth == 1)
- {
- // round down pixel number. This will is lossy operation, at most 7 pixels will be lost
- pixels_per_line = (pixels_per_line / 8) * 8;
- bytes_per_line = pixels_per_line / 8;
- } else {
- bytes_per_line = pixels_per_line;
- }
- if (s->params.format == SANE_FRAME_RGB) {
- bytes_per_line *= 3;
- }
+ unsigned pixels_per_line = static_cast<unsigned>(((br_x - settings.tl_x) * settings.xres) /
+ MM_PER_INCH);
- s->dev->settings.scan_mode = option_string_to_scan_color_mode(s->mode);
+ const auto& sensor = sanei_genesys_find_sensor(dev, settings.xres, settings.get_channels(),
+ settings.scan_method);
- s->dev->settings.lines = s->params.lines;
- s->dev->settings.pixels = pixels_per_line;
- s->dev->settings.requested_pixels = pixels_per_line * xres_factor;
- s->params.pixels_per_line = pixels_per_line * xres_factor;
- s->params.bytes_per_line = bytes_per_line * xres_factor;
- s->dev->settings.tl_x = tl_x;
- s->dev->settings.tl_y = tl_y;
+ pixels_per_line = session_adjust_output_pixels(pixels_per_line, *dev, sensor,
+ settings.xres, settings.yres, true);
- // threshold setting
- s->dev->settings.threshold = static_cast<int>(2.55 * (SANE_UNFIX(s->threshold)));
+ unsigned xres_factor = s->resolution / settings.xres;
+ settings.pixels = pixels_per_line;
+ settings.requested_pixels = pixels_per_line * xres_factor;
- // color filter
if (s->color_filter == "Red") {
- s->dev->settings.color_filter = ColorFilter::RED;
+ settings.color_filter = ColorFilter::RED;
} else if (s->color_filter == "Green") {
- s->dev->settings.color_filter = ColorFilter::GREEN;
+ settings.color_filter = ColorFilter::GREEN;
} else if (s->color_filter == "Blue") {
- s->dev->settings.color_filter = ColorFilter::BLUE;
+ settings.color_filter = ColorFilter::BLUE;
} else {
- s->dev->settings.color_filter = ColorFilter::NONE;
+ settings.color_filter = ColorFilter::NONE;
}
- // true gray
if (s->color_filter == "None") {
- s->dev->settings.true_gray = 1;
+ settings.true_gray = 1;
} else {
- s->dev->settings.true_gray = 0;
+ settings.true_gray = 0;
}
- // threshold curve for dynamic rasterization
- s->dev->settings.threshold_curve = s->threshold_curve;
-
- /* some digital processing requires the whole picture to be buffered */
- /* no digital processing takes place when doing preview, or when bit depth is
- * higher than 8 bits */
- if ((s->swdespeck || s->swcrop || s->swdeskew || s->swderotate ||(SANE_UNFIX(s->swskip)>0))
- && (!s->preview)
- && (s->bit_depth <= 8))
- {
- s->dev->buffer_image = true;
- }
- else
- {
- s->dev->buffer_image = false;
+ // brigthness and contrast only for for 8 bit scans
+ if (s->bit_depth == 8) {
+ settings.contrast = (s->contrast * 127) / 100;
+ settings.brightness = (s->brightness * 127) / 100;
+ } else {
+ settings.contrast = 0;
+ settings.brightness = 0;
}
- /* brigthness and contrast only for for 8 bit scans */
- if(s->bit_depth <= 8)
- {
- s->dev->settings.contrast = (s->contrast * 127) / 100;
- s->dev->settings.brightness = (s->brightness * 127) / 100;
- }
- else
- {
- s->dev->settings.contrast=0;
- s->dev->settings.brightness=0;
+ settings.expiration_time = s->expiration_time;
+
+ return settings;
+}
+
+static SANE_Parameters calculate_scan_parameters(const Genesys_Device& dev,
+ const Genesys_Settings& settings)
+{
+ DBG_HELPER(dbg);
+
+ auto sensor = sanei_genesys_find_sensor(&dev, settings.xres, settings.get_channels(),
+ settings.scan_method);
+ auto session = dev.cmd_set->calculate_scan_session(&dev, sensor, settings);
+ auto pipeline = build_image_pipeline(dev, session, 0, false);
+
+ SANE_Parameters params;
+ if (settings.scan_mode == ScanColorMode::GRAY) {
+ params.format = SANE_FRAME_GRAY;
+ } else {
+ params.format = SANE_FRAME_RGB;
}
+ // only single-pass scanning supported
+ params.last_frame = true;
+ params.depth = settings.depth;
+ params.lines = pipeline.get_output_height();
+ params.pixels_per_line = pipeline.get_output_width();
+ params.bytes_per_line = pipeline.get_output_row_bytes();
- /* cache expiration time */
- s->dev->settings.expiration_time = s->expiration_time;
+ return params;
}
+static void calc_parameters(Genesys_Scanner* s)
+{
+ DBG_HELPER(dbg);
+
+ s->dev->settings = calculate_scan_settings(s);
+ s->params = calculate_scan_parameters(*s->dev, s->dev->settings);
+}
static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& bpp)
{
@@ -3760,7 +4430,7 @@ static void create_bpp_list (Genesys_Scanner * s, const std::vector<unsigned>& b
/** @brief this function initialize a gamma vector based on the ASIC:
* Set up a default gamma table vector based on device description
- * gl646: 12 or 14 bits gamma table depending on GENESYS_FLAG_14BIT_GAMMA
+ * gl646: 12 or 14 bits gamma table depending on ModelFlag::GAMMA_14BIT
* gl84x: 16 bits
* gl12x: 16 bits
* @param scanner pointer to scanner session to get options
@@ -3776,8 +4446,7 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
scanner->opt[option].unit = SANE_UNIT_NONE;
scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE;
if (scanner->dev->model->asic_type == AsicType::GL646) {
- if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0)
- {
+ if (has_flag(scanner->dev->model->flags, ModelFlag::GAMMA_14BIT)) {
scanner->opt[option].size = 16384 * sizeof (SANE_Word);
scanner->opt[option].constraint.range = &u14_range;
}
@@ -3802,9 +4471,9 @@ init_gamma_vector_option (Genesys_Scanner * scanner, int option)
static SANE_Range create_range(float size)
{
SANE_Range range;
- range.min = SANE_FIX(0.0);
- range.max = SANE_FIX(size);
- range.quant = SANE_FIX(0.0);
+ range.min = float_to_fixed(0.0);
+ range.max = float_to_fixed(size);
+ range.quant = float_to_fixed(0.0);
return range;
}
@@ -3855,7 +4524,7 @@ static std::string calibration_filename(Genesys_Device *currdev)
/* count models of the same names if several scanners attached */
if(s_devices->size() > 1) {
for (const auto& dev : *s_devices) {
- if (dev.model->model_id == currdev->model->model_id) {
+ if (dev.vendorId == currdev->vendorId && dev.productId == currdev->productId) {
count++;
}
}
@@ -3921,13 +4590,13 @@ static void set_xy_range_option_values(Genesys_Scanner& s)
{
if (s.scan_method == ScanMethod::FLATBED)
{
- s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size));
- s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size));
+ s.opt_x_range = create_range(s.dev->model->x_size);
+ s.opt_y_range = create_range(s.dev->model->y_size);
}
else
{
- s.opt_x_range = create_range(static_cast<float>(s.dev->model->x_size_ta));
- s.opt_y_range = create_range(static_cast<float>(s.dev->model->y_size_ta));
+ s.opt_x_range = create_range(s.dev->model->x_size_ta);
+ s.opt_y_range = create_range(s.dev->model->y_size_ta);
}
s.opt[OPT_TL_X].constraint.range = &s.opt_x_range;
@@ -3945,7 +4614,7 @@ static void init_options(Genesys_Scanner* s)
{
DBG_HELPER(dbg);
SANE_Int option;
- Genesys_Model *model = s->dev->model;
+ const Genesys_Model* model = s->dev->model;
memset (s->opt, 0, sizeof (s->opt));
@@ -4038,8 +4707,8 @@ static void init_options(Genesys_Scanner* s)
s->opt[OPT_GEOMETRY_GROUP].size = 0;
s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
- s->opt_x_range = create_range(static_cast<float>(model->x_size));
- s->opt_y_range = create_range(static_cast<float>(model->y_size));
+ s->opt_x_range = create_range(model->x_size);
+ s->opt_y_range = create_range(model->y_size);
// scan area
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
@@ -4116,8 +4785,7 @@ static void init_options(Genesys_Scanner* s)
/* currently, there are only gamma table options in this group,
* so if the scanner doesn't support gamma table, disable the
* whole group */
- if (!(model->flags & GENESYS_FLAG_CUSTOM_GAMMA))
- {
+ if (!has_flag(model->flags, ModelFlag::CUSTOM_GAMMA)) {
s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE;
DBG(DBG_info, "%s: custom gamma disabled\n", __func__);
@@ -4127,61 +4795,6 @@ static void init_options(Genesys_Scanner* s)
* memory than used by the full scanned image and may fail at high
* resolution
*/
- /* software deskew */
- s->opt[OPT_SWDESKEW].name = "swdeskew";
- s->opt[OPT_SWDESKEW].title = "Software deskew";
- s->opt[OPT_SWDESKEW].desc = "Request backend to rotate skewed pages digitally";
- s->opt[OPT_SWDESKEW].type = SANE_TYPE_BOOL;
- s->opt[OPT_SWDESKEW].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
- s->swdeskew = false;
-
- /* software deskew */
- s->opt[OPT_SWDESPECK].name = "swdespeck";
- s->opt[OPT_SWDESPECK].title = "Software despeck";
- s->opt[OPT_SWDESPECK].desc = "Request backend to remove lone dots digitally";
- s->opt[OPT_SWDESPECK].type = SANE_TYPE_BOOL;
- s->opt[OPT_SWDESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
- s->swdespeck = false;
-
- /* software despeckle radius */
- s->opt[OPT_DESPECK].name = "despeck";
- s->opt[OPT_DESPECK].title = "Software despeckle diameter";
- s->opt[OPT_DESPECK].desc = "Maximum diameter of lone dots to remove from scan";
- s->opt[OPT_DESPECK].type = SANE_TYPE_INT;
- s->opt[OPT_DESPECK].unit = SANE_UNIT_NONE;
- s->opt[OPT_DESPECK].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_DESPECK].constraint.range = &swdespeck_range;
- s->opt[OPT_DESPECK].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED | SANE_CAP_INACTIVE;
- s->despeck = 1;
-
- /* crop by software */
- s->opt[OPT_SWCROP].name = "swcrop";
- s->opt[OPT_SWCROP].title = SANE_I18N ("Software crop");
- s->opt[OPT_SWCROP].desc = SANE_I18N ("Request backend to remove border from pages digitally");
- s->opt[OPT_SWCROP].type = SANE_TYPE_BOOL;
- s->opt[OPT_SWCROP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
- s->opt[OPT_SWCROP].unit = SANE_UNIT_NONE;
- s->swcrop = false;
-
- /* Software blank page skip */
- s->opt[OPT_SWSKIP].name = "swskip";
- s->opt[OPT_SWSKIP].title = SANE_I18N ("Software blank skip percentage");
- s->opt[OPT_SWSKIP].desc = SANE_I18N("Request driver to discard pages with low numbers of dark pixels");
- s->opt[OPT_SWSKIP].type = SANE_TYPE_FIXED;
- s->opt[OPT_SWSKIP].unit = SANE_UNIT_PERCENT;
- s->opt[OPT_SWSKIP].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_SWSKIP].constraint.range = &(percentage_range);
- s->opt[OPT_SWSKIP].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
- s->swskip = 0; // disable by default
-
- /* Software Derotate */
- s->opt[OPT_SWDEROTATE].name = "swderotate";
- s->opt[OPT_SWDEROTATE].title = SANE_I18N ("Software derotate");
- s->opt[OPT_SWDEROTATE].desc = SANE_I18N("Request driver to detect and correct 90 degree image rotation");
- s->opt[OPT_SWDEROTATE].type = SANE_TYPE_BOOL;
- s->opt[OPT_SWDEROTATE].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
- s->opt[OPT_SWDEROTATE].unit = SANE_UNIT_NONE;
- s->swderotate = false;
/* Software brightness */
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
@@ -4214,39 +4827,6 @@ static void init_options(Genesys_Scanner* s)
s->opt[OPT_EXTRAS_GROUP].size = 0;
s->opt[OPT_EXTRAS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
- /* BW threshold */
- s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
- s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
- s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
- s->opt[OPT_THRESHOLD].type = SANE_TYPE_FIXED;
- s->opt[OPT_THRESHOLD].unit = SANE_UNIT_PERCENT;
- s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_THRESHOLD].constraint.range = &percentage_range;
- s->threshold = SANE_FIX(50);
-
- /* BW threshold curve */
- s->opt[OPT_THRESHOLD_CURVE].name = "threshold-curve";
- s->opt[OPT_THRESHOLD_CURVE].title = SANE_I18N ("Threshold curve");
- s->opt[OPT_THRESHOLD_CURVE].desc = SANE_I18N ("Dynamic threshold curve, from light to dark, normally 50-65");
- s->opt[OPT_THRESHOLD_CURVE].type = SANE_TYPE_INT;
- s->opt[OPT_THRESHOLD_CURVE].unit = SANE_UNIT_NONE;
- s->opt[OPT_THRESHOLD_CURVE].constraint_type = SANE_CONSTRAINT_RANGE;
- s->opt[OPT_THRESHOLD_CURVE].constraint.range = &threshold_curve_range;
- s->threshold_curve = 50;
-
- /* disable_interpolation */
- s->opt[OPT_DISABLE_INTERPOLATION].name = "disable-interpolation";
- s->opt[OPT_DISABLE_INTERPOLATION].title =
- SANE_I18N ("Disable interpolation");
- s->opt[OPT_DISABLE_INTERPOLATION].desc =
- SANE_I18N
- ("When using high resolutions where the horizontal resolution is smaller "
- "than the vertical resolution this disables horizontal interpolation.");
- s->opt[OPT_DISABLE_INTERPOLATION].type = SANE_TYPE_BOOL;
- s->opt[OPT_DISABLE_INTERPOLATION].unit = SANE_UNIT_NONE;
- s->opt[OPT_DISABLE_INTERPOLATION].constraint_type = SANE_CONSTRAINT_NONE;
- s->disable_interpolation = false;
-
/* color filter */
s->opt[OPT_COLOR_FILTER].name = "color-filter";
s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter");
@@ -4508,36 +5088,39 @@ check_present (SANE_String_Const devname) noexcept
return SANE_STATUS_GOOD;
}
-static Genesys_Device* attach_usb_device(const char* devname,
- std::uint16_t vendor_id, std::uint16_t product_id)
+const UsbDeviceEntry& get_matching_usb_dev(std::uint16_t vendor_id, std::uint16_t product_id,
+ std::uint16_t bcd_device)
{
- Genesys_USB_Device_Entry* found_usb_dev = nullptr;
for (auto& usb_dev : *s_usb_devices) {
- if (usb_dev.vendor == vendor_id &&
- usb_dev.product == product_id)
- {
- found_usb_dev = &usb_dev;
- break;
+ if (usb_dev.matches(vendor_id, product_id, bcd_device)) {
+ return usb_dev;
}
}
- if (found_usb_dev == nullptr) {
- throw SaneException("vendor 0x%xd product 0x%xd is not supported by this backend",
- vendor_id, product_id);
- }
+ throw SaneException("vendor 0x%x product 0x%x (bcdDevice 0x%x) "
+ "is not supported by this backend",
+ vendor_id, product_id, bcd_device);
+}
+
+static Genesys_Device* attach_usb_device(const char* devname,
+ std::uint16_t vendor_id, std::uint16_t product_id,
+ std::uint16_t bcd_device)
+{
+ const auto& usb_dev = get_matching_usb_dev(vendor_id, product_id, bcd_device);
s_devices->emplace_back();
Genesys_Device* dev = &s_devices->back();
dev->file_name = devname;
-
- dev->model = &found_usb_dev->model;
- dev->vendorId = found_usb_dev->vendor;
- dev->productId = found_usb_dev->product;
+ dev->vendorId = vendor_id;
+ dev->productId = product_id;
+ dev->model = &usb_dev.model();
dev->usb_mode = 0; // i.e. unset
dev->already_initialized = false;
return dev;
}
+static bool s_attach_device_by_name_evaluate_bcd_device = false;
+
static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may_wait)
{
DBG_HELPER_ARGS(dbg, " devname: %s, may_wait = %d", devname, may_wait);
@@ -4560,26 +5143,31 @@ static Genesys_Device* attach_device_by_name(SANE_String_Const devname, bool may
usb_dev.open(devname);
DBG(DBG_info, "%s: device `%s' successfully opened\n", __func__, devname);
- int vendor, product;
- usb_dev.get_vendor_product(vendor, product);
+ auto vendor_id = usb_dev.get_vendor_id();
+ auto product_id = usb_dev.get_product_id();
+ auto bcd_device = UsbDeviceEntry::BCD_DEVICE_NOT_SET;
+ if (s_attach_device_by_name_evaluate_bcd_device) {
+ // when the device is already known before scanning, we don't want to call get_bcd_device()
+ // when iterating devices, as that will interfere with record/replay during testing.
+ bcd_device = usb_dev.get_bcd_device();
+ }
usb_dev.close();
/* KV-SS080 is an auxiliary device which requires a master device to be here */
- if(vendor == 0x04da && product == 0x100f)
- {
+ if (vendor_id == 0x04da && product_id == 0x100f) {
present = false;
- sanei_usb_find_devices (vendor, 0x1006, check_present);
- sanei_usb_find_devices (vendor, 0x1007, check_present);
- sanei_usb_find_devices (vendor, 0x1010, check_present);
+ sanei_usb_find_devices(vendor_id, 0x1006, check_present);
+ sanei_usb_find_devices(vendor_id, 0x1007, check_present);
+ sanei_usb_find_devices(vendor_id, 0x1010, check_present);
if (present == false) {
throw SaneException("master device not present");
}
}
- Genesys_Device* dev = attach_usb_device(devname, vendor, product);
+ Genesys_Device* dev = attach_usb_device(devname, vendor_id, product_id, bcd_device);
- DBG(DBG_info, "%s: found %s flatbed scanner %s at %s\n", __func__, dev->model->vendor,
- dev->model->model, dev->file_name.c_str());
+ DBG(DBG_info, "%s: found %u flatbed scanner %u at %s\n", __func__, vendor_id, product_id,
+ dev->file_name.c_str());
return dev;
}
@@ -4614,7 +5202,8 @@ static void probe_genesys_devices()
DBG_HELPER(dbg);
if (is_testing_mode()) {
attach_usb_device(get_testing_device_name().c_str(),
- get_testing_vendor_id(), get_testing_product_id());
+ get_testing_vendor_id(), get_testing_product_id(),
+ get_testing_bcd_device());
return;
}
@@ -4625,7 +5214,12 @@ static void probe_genesys_devices()
config.values = nullptr;
config.count = 0;
- TIE(sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys));
+ auto status = sanei_configure_attach(GENESYS_CONFIG_FILE, &config, config_attach_genesys);
+ if (status == SANE_STATUS_ACCESS_DENIED) {
+ dbg.vlog(DBG_error0, "Critical error: Couldn't access configuration file '%s'",
+ GENESYS_CONFIG_FILE);
+ }
+ TIE(status);
DBG(DBG_info, "%s: %zu devices currently attached\n", __func__, s_devices->size());
}
@@ -4637,7 +5231,7 @@ static void probe_genesys_devices()
of Genesys_Calibration_Cache as is.
*/
static const char* CALIBRATION_IDENT = "sane_genesys";
-static const int CALIBRATION_VERSION = 21;
+static const int CALIBRATION_VERSION = 31;
bool read_calibration(std::istream& str, Genesys_Device::Calibration& calibration,
const std::string& path)
@@ -4706,114 +5300,6 @@ static void write_calibration(Genesys_Device::Calibration& calibration, const st
write_calibration(str, calibration);
}
-/** @brief buffer scanned picture
- * In order to allow digital processing, we must be able to put all the
- * scanned picture in a buffer.
- */
-static void genesys_buffer_image(Genesys_Scanner *s)
-{
- DBG_HELPER(dbg);
- size_t maximum; /**> maximum bytes size of the scan */
- size_t len; /**> length of scanned data read */
- size_t total; /**> total of butes read */
- size_t size; /**> size of image buffer */
- size_t read_size; /**> size of reads */
- int lines; /** number of lines of the scan */
- Genesys_Device *dev = s->dev;
-
- /* compute maximum number of lines for the scan */
- if (s->params.lines > 0)
- {
- lines = s->params.lines;
- }
- else
- {
- lines = static_cast<int>((dev->model->y_size * dev->settings.yres) / MM_PER_INCH);
- }
- DBG(DBG_info, "%s: buffering %d lines of %d bytes\n", __func__, lines,
- s->params.bytes_per_line);
-
- /* maximum bytes to read */
- maximum = s->params.bytes_per_line * lines;
- if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
- maximum *= 8;
- }
-
- /* initial size of the read buffer */
- size =
- ((2048 * 2048) / s->params.bytes_per_line) * s->params.bytes_per_line;
-
- /* read size */
- read_size = size / 2;
-
- dev->img_buffer.resize(size);
-
- /* loop reading data until we reach maximum or EOF */
- total = 0;
- while (total < maximum) {
- len = size - maximum;
- if (len > read_size)
- {
- len = read_size;
- }
-
- try {
- genesys_read_ordered_data(dev, dev->img_buffer.data() + total, &len);
- } catch (const SaneException& e) {
- if (e.status() == SANE_STATUS_EOF) {
- // ideally we shouldn't end up here, but because computations are duplicated and
- // slightly different everywhere in the genesys backend, we have no other choice
- break;
- }
- throw;
- }
- total += len;
-
- // do we need to enlarge read buffer ?
- if (total + read_size > size) {
- size += read_size;
- dev->img_buffer.resize(size);
- }
- }
-
- /* since digital processing is going to take place,
- * issue head parking command so that the head move while
- * computing so we can save time
- */
- if (!dev->model->is_sheetfed && !dev->parking) {
- dev->cmd_set->move_back_home(dev, dev->model->flags & GENESYS_FLAG_MUST_WAIT);
- dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
- }
-
- /* in case of dynamic lineart, we have buffered gray data which
- * must be converted to lineart first */
- if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
- total/=8;
- std::vector<uint8_t> lineart(total);
-
- genesys_gray_lineart (dev,
- dev->img_buffer.data(),
- lineart.data(),
- dev->settings.pixels,
- (total*8)/dev->settings.pixels,
- dev->settings.threshold);
- dev->img_buffer = lineart;
- }
-
- /* update counters */
- dev->total_bytes_to_read = total;
- dev->total_bytes_read = 0;
-
- /* update params */
- s->params.lines = total / s->params.bytes_per_line;
- if (DBG_LEVEL >= DBG_io2)
- {
- sanei_genesys_write_pnm_file("gl_unprocessed.pnm", dev->img_buffer.data(), s->params.depth,
- s->params.format==SANE_FRAME_RGB ? 3 : 1,
- s->params.pixels_per_line, s->params.lines);
- }
-}
-
/* -------------------------- SANE API functions ------------------------- */
void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
@@ -4839,9 +5325,6 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
sanei_usb_init();
}
- /* init sanei_magic */
- sanei_magic_init();
-
s_scanners.init();
s_devices.init();
s_sane_devices.init();
@@ -4850,8 +5333,8 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
genesys_init_sensor_tables();
genesys_init_frontend_tables();
genesys_init_gpo_tables();
+ genesys_init_memory_layout_tables();
genesys_init_motor_tables();
- genesys_init_motor_profile_tables();
genesys_init_usb_device_tables();
@@ -4864,6 +5347,7 @@ void sane_init_impl(SANE_Int * version_code, SANE_Auth_Callback authorize)
);
// cold-plug case :detection of allready connected scanners
+ s_attach_device_by_name_evaluate_bcd_device = false;
probe_genesys_devices();
}
@@ -4903,6 +5387,7 @@ void sane_get_devices_impl(const SANE_Device *** device_list, SANE_Bool local_on
// hot-plug case : detection of newly connected scanners */
sanei_usb_scan_devices();
}
+ s_attach_device_by_name_evaluate_bcd_device = true;
probe_genesys_devices();
s_sane_devices->clear();
@@ -4969,7 +5454,7 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
}
if (dev) {
- DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->model->name);
+ DBG(DBG_info, "%s: found `%s' in devlist\n", __func__, dev->file_name.c_str());
} else if (is_testing_mode()) {
DBG(DBG_info, "%s: couldn't find `%s' in devlist, not attaching", __func__, devicename);
} else {
@@ -4991,37 +5476,53 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
throw SaneException("could not find the device to open: %s", devicename);
}
- if (dev->model->flags & GENESYS_FLAG_UNTESTED)
- {
- DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n");
- DBG(DBG_error0, " had only limited testing. Please be careful and \n");
- DBG(DBG_error0, " report any failure/success to \n");
- DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n");
- DBG(DBG_error0, " details as possible, e.g. the exact name of your\n");
- DBG(DBG_error0, " scanner and what does (not) work.\n");
- }
+ if (is_testing_mode()) {
+ // during testing we need to initialize dev->model before test scanner interface is created
+ // as that it needs to know what type of chip it needs to mimic.
+ auto vendor_id = get_testing_vendor_id();
+ auto product_id = get_testing_product_id();
+ auto bcd_device = get_testing_bcd_device();
- dbg.vstatus("open device '%s'", dev->file_name.c_str());
+ dev->model = &get_matching_usb_dev(vendor_id, product_id, bcd_device).model();
- if (is_testing_mode()) {
- auto interface = std::unique_ptr<TestScannerInterface>{new TestScannerInterface{dev}};
+ auto interface = std::unique_ptr<TestScannerInterface>{
+ new TestScannerInterface{dev, vendor_id, product_id, bcd_device}};
interface->set_checkpoint_callback(get_testing_checkpoint_callback());
dev->interface = std::move(interface);
+
+ dev->interface->get_usb_device().open(dev->file_name.c_str());
} else {
dev->interface = std::unique_ptr<ScannerInterfaceUsb>{new ScannerInterfaceUsb{dev}};
+
+ dbg.vstatus("open device '%s'", dev->file_name.c_str());
+ dev->interface->get_usb_device().open(dev->file_name.c_str());
+ dbg.clear();
+
+ auto bcd_device = dev->interface->get_usb_device().get_bcd_device();
+
+ dev->model = &get_matching_usb_dev(dev->vendorId, dev->productId, bcd_device).model();
+ }
+
+ dbg.vlog(DBG_info, "Opened device %s", dev->model->name);
+
+ if (has_flag(dev->model->flags, ModelFlag::UNTESTED)) {
+ DBG(DBG_error0, "WARNING: Your scanner is not fully supported or at least \n");
+ DBG(DBG_error0, " had only limited testing. Please be careful and \n");
+ DBG(DBG_error0, " report any failure/success to \n");
+ DBG(DBG_error0, " sane-devel@alioth-lists.debian.net. Please provide as many\n");
+ DBG(DBG_error0, " details as possible, e.g. the exact name of your\n");
+ DBG(DBG_error0, " scanner and what does (not) work.\n");
}
- dev->interface->get_usb_device().open(dev->file_name.c_str());
- dbg.clear();
s_scanners->push_back(Genesys_Scanner());
auto* s = &s_scanners->back();
- s->dev = dev;
+ s->dev = dev;
s->scanning = false;
- s->dev->parking = false;
- s->dev->read_active = false;
- s->dev->force_calibration = 0;
- s->dev->line_count = 0;
+ dev->parking = false;
+ dev->read_active = false;
+ dev->force_calibration = 0;
+ dev->line_count = 0;
*handle = s;
@@ -5029,31 +5530,18 @@ static void sane_open_impl(SANE_String_Const devicename, SANE_Handle * handle)
sanei_genesys_init_structs (dev);
}
+ dev->cmd_set = create_cmd_set(dev->model->asic_type);
+
init_options(s);
- sanei_genesys_init_cmd_set(s->dev);
+ DBG_INIT();
// FIXME: we create sensor tables for the sensor, this should happen when we know which sensor
// we will select
dev->cmd_set->init(dev);
// some hardware capabilities are detected through sensors
- s->dev->cmd_set->update_hardware_sensors (s);
-
- /* here is the place to fetch a stored calibration cache */
- if (s->dev->force_calibration == 0)
- {
- auto path = calibration_filename(s->dev);
- s->calibration_file = path;
- s->dev->calib_file = path;
- DBG(DBG_info, "%s: Calibration filename set to:\n", __func__);
- DBG(DBG_info, "%s: >%s<\n", __func__, s->dev->calib_file.c_str());
-
- catch_all_exceptions(__func__, [&]()
- {
- sanei_genesys_read_calibration(s->dev->calibration_cache, s->dev->calib_file);
- });
- }
+ dev->cmd_set->update_hardware_sensors (s);
}
SANE_GENESYS_API_LINKAGE
@@ -5085,46 +5573,42 @@ sane_close_impl(SANE_Handle handle)
return; /* oops, not a handle we know about */
}
- Genesys_Scanner* s = &*it;
+ auto* dev = it->dev;
- /* eject document for sheetfed scanners */
- if (s->dev->model->is_sheetfed) {
- catch_all_exceptions(__func__, [&](){ s->dev->cmd_set->eject_document(s->dev); });
- }
- else
- {
- /* in case scanner is parking, wait for the head
- * to reach home position */
- if (s->dev->parking) {
- sanei_genesys_wait_for_home(s->dev);
+ // eject document for sheetfed scanners
+ if (dev->model->is_sheetfed) {
+ catch_all_exceptions(__func__, [&](){ dev->cmd_set->eject_document(dev); });
+ } else {
+ // in case scanner is parking, wait for the head to reach home position
+ if (dev->parking) {
+ sanei_genesys_wait_for_home(dev);
}
}
// enable power saving before leaving
- s->dev->cmd_set->save_power(s->dev, true);
+ dev->cmd_set->save_power(dev, true);
// here is the place to store calibration cache
- if (s->dev->force_calibration == 0 && !is_testing_mode()) {
- catch_all_exceptions(__func__, [&](){ write_calibration(s->dev->calibration_cache,
- s->dev->calib_file); });
+ if (dev->force_calibration == 0 && !is_testing_mode()) {
+ catch_all_exceptions(__func__, [&](){ write_calibration(dev->calibration_cache,
+ dev->calib_file); });
}
- s->dev->already_initialized = false;
-
- s->dev->clear();
+ dev->already_initialized = false;
+ dev->clear();
// LAMP OFF : same register across all the ASICs */
- s->dev->interface->write_register(0x03, 0x00);
+ dev->interface->write_register(0x03, 0x00);
- catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().clear_halt(); });
+ catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().clear_halt(); });
// we need this to avoid these ASIC getting stuck in bulk writes
- catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().reset(); });
+ catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().reset(); });
- // not freeing s->dev because it's in the dev list
- catch_all_exceptions(__func__, [&](){ s->dev->interface->get_usb_device().close(); });
+ // not freeing dev because it's in the dev list
+ catch_all_exceptions(__func__, [&](){ dev->interface->get_usb_device().close(); });
- s_scanners->erase(it);
+ s_scanners->erase(it);
}
SANE_GENESYS_API_LINKAGE
@@ -5174,7 +5658,7 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int
return;
}
case SANE_TYPE_FIXED: {
- dbg.vlog(DBG_proc, "value: %f", SANE_UNFIX(*reinterpret_cast<SANE_Word*>(val)));
+ dbg.vlog(DBG_proc, "value: %f", fixed_to_float(*reinterpret_cast<SANE_Word*>(val)));
return;
}
case SANE_TYPE_STRING: {
@@ -5189,18 +5673,19 @@ static void print_option(DebugMessageHelper& dbg, const Genesys_Scanner& s, int
static void get_option_value(Genesys_Scanner* s, int option, void* val)
{
DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
+ auto* dev = s->dev;
unsigned int i;
SANE_Word* table = nullptr;
std::vector<uint16_t> gamma_table;
unsigned option_size = 0;
const Genesys_Sensor* sensor = nullptr;
- if (sanei_genesys_has_sensor(s->dev, s->dev->settings.xres, s->dev->settings.get_channels(),
- s->dev->settings.scan_method))
+ if (sanei_genesys_has_sensor(dev, dev->settings.xres, dev->settings.get_channels(),
+ dev->settings.scan_method))
{
- sensor = &sanei_genesys_find_sensor(s->dev, s->dev->settings.xres,
- s->dev->settings.get_channels(),
- s->dev->settings.scan_method);
+ sensor = &sanei_genesys_find_sensor(dev, dev->settings.xres,
+ dev->settings.get_channels(),
+ dev->settings.scan_method);
}
switch (option)
@@ -5231,39 +5716,12 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
case OPT_PREVIEW:
*reinterpret_cast<SANE_Word*>(val) = s->preview;
break;
- case OPT_THRESHOLD:
- *reinterpret_cast<SANE_Word*>(val) = s->threshold;
- break;
- case OPT_THRESHOLD_CURVE:
- *reinterpret_cast<SANE_Word*>(val) = s->threshold_curve;
- break;
- case OPT_DISABLE_INTERPOLATION:
- *reinterpret_cast<SANE_Word*>(val) = s->disable_interpolation;
- break;
case OPT_LAMP_OFF:
*reinterpret_cast<SANE_Word*>(val) = s->lamp_off;
break;
case OPT_LAMP_OFF_TIME:
*reinterpret_cast<SANE_Word*>(val) = s->lamp_off_time;
break;
- case OPT_SWDESKEW:
- *reinterpret_cast<SANE_Word*>(val) = s->swdeskew;
- break;
- case OPT_SWCROP:
- *reinterpret_cast<SANE_Word*>(val) = s->swcrop;
- break;
- case OPT_SWDESPECK:
- *reinterpret_cast<SANE_Word*>(val) = s->swdespeck;
- break;
- case OPT_SWDEROTATE:
- *reinterpret_cast<SANE_Word*>(val) = s->swderotate;
- break;
- case OPT_SWSKIP:
- *reinterpret_cast<SANE_Word*>(val) = s->swskip;
- break;
- case OPT_DESPECK:
- *reinterpret_cast<SANE_Word*>(val) = s->despeck;
- break;
case OPT_CONTRAST:
*reinterpret_cast<SANE_Word*>(val) = s->contrast;
break;
@@ -5297,13 +5755,13 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
throw SaneException("Unsupported scanner mode selected");
table = reinterpret_cast<SANE_Word*>(val);
- if (s->color_filter == "Red") {
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED);
- } else if (s->color_filter == "Blue") {
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE);
- } else {
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN);
- }
+ if (s->color_filter == "Red") {
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
+ } else if (s->color_filter == "Blue") {
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
+ } else {
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
+ }
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -5317,7 +5775,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
throw SaneException("Unsupported scanner mode selected");
table = reinterpret_cast<SANE_Word*>(val);
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_RED);
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_RED);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -5331,7 +5789,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
throw SaneException("Unsupported scanner mode selected");
table = reinterpret_cast<SANE_Word*>(val);
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_GREEN);
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_GREEN);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -5345,7 +5803,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
throw SaneException("Unsupported scanner mode selected");
table = reinterpret_cast<SANE_Word*>(val);
- gamma_table = get_gamma_table(s->dev, *sensor, GENESYS_BLUE);
+ gamma_table = get_gamma_table(dev, *sensor, GENESYS_BLUE);
option_size = s->opt[option].size / sizeof (SANE_Word);
if (gamma_table.size() != option_size) {
throw std::runtime_error("The size of the gamma tables does not match");
@@ -5377,11 +5835,10 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
bool result = true;
- auto session = s->dev->cmd_set->calculate_scan_session(s->dev, *sensor,
- s->dev->settings);
+ auto session = dev->cmd_set->calculate_scan_session(dev, *sensor, dev->settings);
- for (auto& cache : s->dev->calibration_cache) {
- if (sanei_genesys_is_compatible_calibration(s->dev, session, &cache, false)) {
+ for (auto& cache : dev->calibration_cache) {
+ if (sanei_genesys_is_compatible_calibration(dev, session, &cache, false)) {
*reinterpret_cast<SANE_Bool*>(val) = SANE_FALSE;
}
}
@@ -5400,6 +5857,7 @@ static void get_option_value(Genesys_Scanner* s, int option, void* val)
static void set_calibration_value(Genesys_Scanner* s, const char* val)
{
DBG_HELPER(dbg);
+ auto dev = s->dev;
std::string new_calib_path = val;
Genesys_Device::Calibration new_calibration;
@@ -5414,8 +5872,8 @@ static void set_calibration_value(Genesys_Scanner* s, const char* val)
return;
}
- s->dev->calibration_cache = std::move(new_calibration);
- s->dev->calib_file = new_calib_path;
+ dev->calibration_cache = std::move(new_calibration);
+ dev->calib_file = new_calib_path;
s->calibration_file = new_calib_path;
DBG(DBG_info, "%s: Calibration filename set to '%s':\n", __func__, new_calib_path.c_str());
}
@@ -5426,12 +5884,13 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
DBG_HELPER_ARGS(dbg, "option: %s (%d)", s->opt[option].name, option);
print_option(dbg, *s, option, val);
+ auto* dev = s->dev;
+
SANE_Word *table;
unsigned int i;
unsigned option_size = 0;
- switch (option)
- {
+ switch (option) {
case OPT_TL_X:
s->pos_top_left_x = *reinterpret_cast<SANE_Word*>(val);
calc_parameters(s);
@@ -5457,46 +5916,6 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
- case OPT_THRESHOLD:
- s->threshold = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_THRESHOLD_CURVE:
- s->threshold_curve = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_SWCROP:
- s->swcrop = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_SWDESKEW:
- s->swdeskew = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_DESPECK:
- s->despeck = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_SWDEROTATE:
- s->swderotate = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_SWSKIP:
- s->swskip = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
- case OPT_DISABLE_INTERPOLATION:
- s->disable_interpolation = *reinterpret_cast<SANE_Word*>(val);
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS;
- break;
case OPT_LAMP_OFF:
s->lamp_off = *reinterpret_cast<SANE_Word*>(val);
calc_parameters(s);
@@ -5517,38 +5936,14 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
calc_parameters(s);
*myinfo |= SANE_INFO_RELOAD_PARAMS;
break;
- case OPT_SWDESPECK:
- s->swdespeck = *reinterpret_cast<SANE_Word*>(val);
- if (s->swdespeck) {
- ENABLE(OPT_DESPECK);
- } else {
- DISABLE(OPT_DESPECK);
- }
- calc_parameters(s);
- *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
- break;
/* software enhancement functions only apply to 8 or 1 bits data */
case OPT_BIT_DEPTH:
s->bit_depth = *reinterpret_cast<SANE_Word*>(val);
if(s->bit_depth>8)
{
- DISABLE(OPT_SWDESKEW);
- DISABLE(OPT_SWDESPECK);
- DISABLE(OPT_SWCROP);
- DISABLE(OPT_DESPECK);
- DISABLE(OPT_SWDEROTATE);
- DISABLE(OPT_SWSKIP);
DISABLE(OPT_CONTRAST);
DISABLE(OPT_BRIGHTNESS);
- }
- else
- {
- ENABLE(OPT_SWDESKEW);
- ENABLE(OPT_SWDESPECK);
- ENABLE(OPT_SWCROP);
- ENABLE(OPT_DESPECK);
- ENABLE(OPT_SWDEROTATE);
- ENABLE(OPT_SWSKIP);
+ } else {
ENABLE(OPT_CONTRAST);
ENABLE(OPT_BRIGHTNESS);
}
@@ -5567,38 +5962,22 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
}
break;
}
- case OPT_MODE:
- s->mode = reinterpret_cast<const char*>(val);
+ case OPT_MODE: {
+ s->mode = reinterpret_cast<const char*>(val);
- if (s->mode == SANE_VALUE_SCAN_MODE_LINEART)
- {
- ENABLE (OPT_THRESHOLD);
- ENABLE (OPT_THRESHOLD_CURVE);
- DISABLE (OPT_BIT_DEPTH);
- if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) {
+ if (s->mode == SANE_VALUE_SCAN_MODE_GRAY) {
+ if (dev->model->asic_type != AsicType::GL646 || !dev->model->is_cis) {
ENABLE(OPT_COLOR_FILTER);
}
- }
- else
- {
- DISABLE (OPT_THRESHOLD);
- DISABLE (OPT_THRESHOLD_CURVE);
- if (s->mode == SANE_VALUE_SCAN_MODE_GRAY)
- {
- if (s->dev->model->asic_type != AsicType::GL646 || !s->dev->model->is_cis) {
- ENABLE(OPT_COLOR_FILTER);
- }
- create_bpp_list (s, s->dev->model->bpp_gray_values);
- s->bit_depth = s->dev->model->bpp_gray_values[0];
- }
- else
- {
- DISABLE (OPT_COLOR_FILTER);
- create_bpp_list (s, s->dev->model->bpp_color_values);
- s->bit_depth = s->dev->model->bpp_color_values[0];
- }
- }
- calc_parameters(s);
+ create_bpp_list(s, dev->model->bpp_gray_values);
+ s->bit_depth = dev->model->bpp_gray_values[0];
+ } else {
+ DISABLE(OPT_COLOR_FILTER);
+ create_bpp_list(s, dev->model->bpp_color_values);
+ s->bit_depth = dev->model->bpp_color_values[0];
+ }
+
+ calc_parameters(s);
/* if custom gamma, toggle gamma table options according to the mode */
if (s->custom_gamma)
@@ -5621,30 +6000,31 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
break;
+ }
case OPT_COLOR_FILTER:
s->color_filter = reinterpret_cast<const char*>(val);
calc_parameters(s);
break;
- case OPT_CALIBRATION_FILE:
- if (s->dev->force_calibration == 0) {
+ case OPT_CALIBRATION_FILE: {
+ if (dev->force_calibration == 0) {
set_calibration_value(s, reinterpret_cast<const char*>(val));
}
break;
- case OPT_LAMP_OFF_TIME:
- if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) {
- s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val);
- s->dev->cmd_set->set_powersaving(s->dev, s->lamp_off_time);
}
- break;
- case OPT_EXPIRATION_TIME:
- if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) {
- s->expiration_time = *reinterpret_cast<SANE_Word*>(val);
- // BUG: this is most likely not intended behavior, found out during refactor
- s->dev->cmd_set->set_powersaving(s->dev, s->expiration_time);
- }
- break;
-
- case OPT_CUSTOM_GAMMA:
+ case OPT_LAMP_OFF_TIME: {
+ if (*reinterpret_cast<SANE_Word*>(val) != s->lamp_off_time) {
+ s->lamp_off_time = *reinterpret_cast<SANE_Word*>(val);
+ dev->cmd_set->set_powersaving(dev, s->lamp_off_time);
+ }
+ break;
+ }
+ case OPT_EXPIRATION_TIME: {
+ if (*reinterpret_cast<SANE_Word*>(val) != s->expiration_time) {
+ s->expiration_time = *reinterpret_cast<SANE_Word*>(val);
+ }
+ break;
+ }
+ case OPT_CUSTOM_GAMMA: {
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
s->custom_gamma = *reinterpret_cast<SANE_Bool*>(val);
@@ -5670,88 +6050,96 @@ static void set_option_value(Genesys_Scanner* s, int option, void *val, SANE_Int
DISABLE (OPT_GAMMA_VECTOR_R);
DISABLE (OPT_GAMMA_VECTOR_G);
DISABLE (OPT_GAMMA_VECTOR_B);
- for (auto& table : s->dev->gamma_override_tables) {
- table.clear();
+ for (auto& table : dev->gamma_override_tables) {
+ table.clear();
+ }
}
- }
- break;
-
- case OPT_GAMMA_VECTOR:
- table = reinterpret_cast<SANE_Word*>(val);
- option_size = s->opt[option].size / sizeof (SANE_Word);
+ break;
+ }
- s->dev->gamma_override_tables[GENESYS_RED].resize(option_size);
- s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
- s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
- for (i = 0; i < option_size; i++) {
- s->dev->gamma_override_tables[GENESYS_RED][i] = table[i];
- s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
- s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
+ case OPT_GAMMA_VECTOR: {
+ table = reinterpret_cast<SANE_Word*>(val);
+ option_size = s->opt[option].size / sizeof (SANE_Word);
+
+ dev->gamma_override_tables[GENESYS_RED].resize(option_size);
+ dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
+ dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
+ for (i = 0; i < option_size; i++) {
+ dev->gamma_override_tables[GENESYS_RED][i] = table[i];
+ dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
+ dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
+ }
+ break;
}
- break;
- case OPT_GAMMA_VECTOR_R:
- table = reinterpret_cast<SANE_Word*>(val);
- option_size = s->opt[option].size / sizeof (SANE_Word);
- s->dev->gamma_override_tables[GENESYS_RED].resize(option_size);
- for (i = 0; i < option_size; i++) {
- s->dev->gamma_override_tables[GENESYS_RED][i] = table[i];
+ case OPT_GAMMA_VECTOR_R: {
+ table = reinterpret_cast<SANE_Word*>(val);
+ option_size = s->opt[option].size / sizeof (SANE_Word);
+ dev->gamma_override_tables[GENESYS_RED].resize(option_size);
+ for (i = 0; i < option_size; i++) {
+ dev->gamma_override_tables[GENESYS_RED][i] = table[i];
+ }
+ break;
}
- break;
- case OPT_GAMMA_VECTOR_G:
- table = reinterpret_cast<SANE_Word*>(val);
- option_size = s->opt[option].size / sizeof (SANE_Word);
- s->dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
- for (i = 0; i < option_size; i++) {
- s->dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
+ case OPT_GAMMA_VECTOR_G: {
+ table = reinterpret_cast<SANE_Word*>(val);
+ option_size = s->opt[option].size / sizeof (SANE_Word);
+ dev->gamma_override_tables[GENESYS_GREEN].resize(option_size);
+ for (i = 0; i < option_size; i++) {
+ dev->gamma_override_tables[GENESYS_GREEN][i] = table[i];
+ }
+ break;
}
- break;
- case OPT_GAMMA_VECTOR_B:
- table = reinterpret_cast<SANE_Word*>(val);
- option_size = s->opt[option].size / sizeof (SANE_Word);
- s->dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
- for (i = 0; i < option_size; i++) {
- s->dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
+ case OPT_GAMMA_VECTOR_B: {
+ table = reinterpret_cast<SANE_Word*>(val);
+ option_size = s->opt[option].size / sizeof (SANE_Word);
+ dev->gamma_override_tables[GENESYS_BLUE].resize(option_size);
+ for (i = 0; i < option_size; i++) {
+ dev->gamma_override_tables[GENESYS_BLUE][i] = table[i];
+ }
+ break;
}
- break;
case OPT_CALIBRATE: {
- auto& sensor = sanei_genesys_find_sensor_for_write(s->dev, s->dev->settings.xres,
- s->dev->settings.get_channels(),
- s->dev->settings.scan_method);
+ auto& sensor = sanei_genesys_find_sensor_for_write(dev, dev->settings.xres,
+ dev->settings.get_channels(),
+ dev->settings.scan_method);
catch_all_exceptions(__func__, [&]()
{
- s->dev->cmd_set->save_power(s->dev, false);
- genesys_scanner_calibration(s->dev, sensor);
+ dev->cmd_set->save_power(dev, false);
+ genesys_scanner_calibration(dev, sensor);
});
catch_all_exceptions(__func__, [&]()
{
- s->dev->cmd_set->save_power(s->dev, true);
+ dev->cmd_set->save_power(dev, true);
});
*myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
break;
}
- case OPT_CLEAR_CALIBRATION:
- s->dev->calibration_cache.clear();
+ case OPT_CLEAR_CALIBRATION: {
+ dev->calibration_cache.clear();
- /* remove file */
- unlink(s->dev->calib_file.c_str());
- /* signals that sensors will have to be read again */
- *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
- break;
- case OPT_FORCE_CALIBRATION:
- s->dev->force_calibration = 1;
- s->dev->calibration_cache.clear();
- s->dev->calib_file.clear();
+ // remove file
+ unlink(dev->calib_file.c_str());
+ // signals that sensors will have to be read again
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ }
+ case OPT_FORCE_CALIBRATION: {
+ dev->force_calibration = 1;
+ dev->calibration_cache.clear();
+ dev->calib_file.clear();
- /* signals that sensors will have to be read again */
- *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
- break;
+ // signals that sensors will have to be read again
+ *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ }
case OPT_IGNORE_OFFSETS: {
- s->dev->ignore_offsets = true;
+ dev->ignore_offsets = true;
break;
}
- default:
- DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option);
+ default: {
+ DBG(DBG_warn, "%s: can't set unknown option %d\n", __func__, option);
+ }
}
}
@@ -5829,13 +6217,13 @@ void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
{
DBG_HELPER(dbg);
Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
+ auto* dev = s->dev;
/* don't recompute parameters once data reading is active, ie during scan */
- if (!s->dev->read_active) {
+ if (!dev->read_active) {
calc_parameters(s);
}
- if (params)
- {
+ if (params) {
*params = s->params;
/* in the case of a sheetfed scanner, when full height is specified
@@ -5843,11 +6231,11 @@ void sane_get_parameters_impl(SANE_Handle handle, SANE_Parameters* params)
* don't know the real document height.
* We don't do that doing buffering image for digital processing
*/
- if (s->dev->model->is_sheetfed && !s->dev->buffer_image &&
+ if (dev->model->is_sheetfed &&
s->pos_bottom_right_y == s->opt[OPT_BR_Y].constraint.range->max)
{
- params->lines = -1;
- }
+ params->lines = -1;
+ }
}
debug_dump(DBG_proc, *params);
}
@@ -5865,6 +6253,7 @@ void sane_start_impl(SANE_Handle handle)
{
DBG_HELPER(dbg);
Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
+ auto* dev = s->dev;
if (s->pos_top_left_x >= s->pos_bottom_right_x) {
throw SaneException("top left x >= bottom right x");
@@ -5873,67 +6262,27 @@ void sane_start_impl(SANE_Handle handle)
throw SaneException("top left y >= bottom right y");
}
- /* First make sure we have a current parameter set. Some of the
- parameters will be overwritten below, but that's OK. */
-
- calc_parameters(s);
- genesys_start_scan(s->dev, s->lamp_off);
-
- s->scanning = true;
+ // fetch stored calibration
+ if (dev->force_calibration == 0) {
+ auto path = calibration_filename(dev);
+ s->calibration_file = path;
+ dev->calib_file = path;
+ DBG(DBG_info, "%s: Calibration filename set to:\n", __func__);
+ DBG(DBG_info, "%s: >%s<\n", __func__, dev->calib_file.c_str());
- /* allocate intermediate buffer when doing dynamic lineart */
- if (s->dev->settings.scan_mode == ScanColorMode::LINEART) {
- s->dev->binarize_buffer.clear();
- s->dev->binarize_buffer.alloc(s->dev->settings.pixels);
- s->dev->local_buffer.clear();
- s->dev->local_buffer.alloc(s->dev->binarize_buffer.size() * 8);
+ catch_all_exceptions(__func__, [&]()
+ {
+ sanei_genesys_read_calibration(dev->calibration_cache, dev->calib_file);
+ });
}
- /* if one of the software enhancement option is selected,
- * we do the scan internally, process picture then put it an internal
- * buffer. Since cropping may change scan parameters, we recompute them
- * at the end */
- if (s->dev->buffer_image)
- {
- genesys_buffer_image(s);
-
- /* check if we need to skip this page, sheetfed scanners
- * can go to next doc while flatbed ones can't */
- if (s->swskip > 0 && IS_ACTIVE(OPT_SWSKIP)) {
- auto status = sanei_magic_isBlank(&s->params,
- s->dev->img_buffer.data(),
- SANE_UNFIX(s->swskip));
-
- if (status == SANE_STATUS_NO_DOCS && s->dev->model->is_sheetfed) {
- DBG(DBG_info, "%s: blank page, recurse\n", __func__);
- sane_start(handle);
- return;
- }
+ // First make sure we have a current parameter set. Some of the
+ // parameters will be overwritten below, but that's OK.
- if (status != SANE_STATUS_GOOD) {
- throw SaneException(status);
- }
- }
-
- if (s->swdeskew) {
- const auto& sensor = sanei_genesys_find_sensor(s->dev, s->dev->settings.xres,
- s->dev->settings.get_channels(),
- s->dev->settings.scan_method);
- catch_all_exceptions(__func__, [&](){ genesys_deskew(s, sensor); });
- }
-
- if (s->swdespeck) {
- catch_all_exceptions(__func__, [&](){ genesys_despeck(s); });
- }
-
- if(s->swcrop) {
- catch_all_exceptions(__func__, [&](){ genesys_crop(s); });
- }
+ calc_parameters(s);
+ genesys_start_scan(dev, s->lamp_off);
- if(s->swderotate) {
- catch_all_exceptions(__func__, [&](){ genesys_derotate(s); });
- }
- }
+ s->scanning = true;
}
SANE_GENESYS_API_LINKAGE
@@ -5945,18 +6294,18 @@ SANE_Status sane_start(SANE_Handle handle)
});
}
-void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
+// returns SANE_STATUS_GOOD if there are more data, SANE_STATUS_EOF otherwise
+SANE_Status sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
{
DBG_HELPER(dbg);
Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
- Genesys_Device *dev;
size_t local_len;
if (!s) {
throw SaneException("handle is nullptr");
}
- dev=s->dev;
+ auto* dev = s->dev;
if (!dev) {
throw SaneException("dev is nullptr");
}
@@ -5986,86 +6335,33 @@ void sane_read_impl(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_
/* issue park command immediatly in case scanner can handle it
* so we save time */
- if (!dev->model->is_sheetfed && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) &&
+ if (!dev->model->is_sheetfed && !has_flag(dev->model->flags, ModelFlag::MUST_WAIT) &&
!dev->parking)
{
dev->cmd_set->move_back_home(dev, false);
dev->parking = true;
}
- throw SaneException(SANE_STATUS_EOF);
+ return SANE_STATUS_EOF;
}
local_len = max_len;
- /* in case of image processing, all data has been stored in
- * buffer_image. So read data from it if it exists, else from scanner */
- if(!dev->buffer_image)
- {
- /* dynamic lineart is another kind of digital processing that needs
- * another layer of buffering on top of genesys_read_ordered_data */
- if (dev->settings.scan_mode == ScanColorMode::LINEART) {
- /* if buffer is empty, fill it with genesys_read_ordered_data */
- if(dev->binarize_buffer.avail() == 0)
- {
- /* store gray data */
- local_len=dev->local_buffer.size();
- dev->local_buffer.reset();
- genesys_read_ordered_data(dev, dev->local_buffer.get_write_pos(local_len),
- &local_len);
- dev->local_buffer.produce(local_len);
-
- dev->binarize_buffer.reset();
- if (!is_testing_mode()) {
- genesys_gray_lineart(dev, dev->local_buffer.get_read_pos(),
- dev->binarize_buffer.get_write_pos(local_len / 8),
- dev->settings.pixels,
- local_len / dev->settings.pixels,
- dev->settings.threshold);
- }
- dev->binarize_buffer.produce(local_len / 8);
- }
-
- /* return data from lineart buffer if any, up to the available amount */
- local_len = max_len;
- if (static_cast<std::size_t>(max_len) > dev->binarize_buffer.avail())
- {
- local_len=dev->binarize_buffer.avail();
- }
- if(local_len)
- {
- memcpy(buf, dev->binarize_buffer.get_read_pos(), local_len);
- dev->binarize_buffer.consume(local_len);
- }
- }
- else
- {
- // most usual case, direct read of data from scanner */
- genesys_read_ordered_data(dev, buf, &local_len);
- }
- }
- else /* read data from buffer */
- {
- if(dev->total_bytes_read+local_len>dev->total_bytes_to_read)
- {
- local_len=dev->total_bytes_to_read-dev->total_bytes_read;
- }
- memcpy(buf, dev->img_buffer.data() + dev->total_bytes_read, local_len);
- dev->total_bytes_read+=local_len;
- }
+ genesys_read_ordered_data(dev, buf, &local_len);
*len = local_len;
if (local_len > static_cast<std::size_t>(max_len)) {
- fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n");
+ dbg.log(DBG_error, "error: returning incorrect length");
}
DBG(DBG_proc, "%s: %d bytes returned\n", __func__, *len);
+ return SANE_STATUS_GOOD;
}
SANE_GENESYS_API_LINKAGE
SANE_Status sane_read(SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, SANE_Int* len)
{
- return wrap_exceptions_to_status_code(__func__, [=]()
+ return wrap_exceptions_to_status_code_return(__func__, [=]()
{
- sane_read_impl(handle, buf, max_len, len);
+ return sane_read_impl(handle, buf, max_len, len);
});
}
@@ -6073,36 +6369,31 @@ void sane_cancel_impl(SANE_Handle handle)
{
DBG_HELPER(dbg);
Genesys_Scanner* s = reinterpret_cast<Genesys_Scanner*>(handle);
+ auto* dev = s->dev;
s->scanning = false;
- s->dev->read_active = false;
- s->dev->img_buffer.clear();
+ dev->read_active = false;
- /* no need to end scan if we are parking the head */
- if (!s->dev->parking) {
- s->dev->cmd_set->end_scan(s->dev, &s->dev->reg, true);
+ // no need to end scan if we are parking the head
+ if (!dev->parking) {
+ dev->cmd_set->end_scan(dev, &dev->reg, true);
}
- /* park head if flatbed scanner */
- if (!s->dev->model->is_sheetfed) {
- if (!s->dev->parking) {
- s->dev->cmd_set->move_back_home (s->dev, s->dev->model->flags &
- GENESYS_FLAG_MUST_WAIT);
-
- s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT);
+ // park head if flatbed scanner
+ if (!dev->model->is_sheetfed) {
+ if (!dev->parking) {
+ dev->cmd_set->move_back_home(dev, has_flag(dev->model->flags, ModelFlag::MUST_WAIT));
+ dev->parking = !has_flag(dev->model->flags, ModelFlag::MUST_WAIT);
}
- }
- else
- { /* in case of sheetfed scanners, we have to eject the document if still present */
- s->dev->cmd_set->eject_document(s->dev);
+ } else {
+ // in case of sheetfed scanners, we have to eject the document if still present
+ dev->cmd_set->eject_document(dev);
}
- /* enable power saving mode unless we are parking .... */
- if (!s->dev->parking) {
- s->dev->cmd_set->save_power(s->dev, true);
+ // enable power saving mode unless we are parking ....
+ if (!dev->parking) {
+ dev->cmd_set->save_power(dev, true);
}
-
- return;
}
SANE_GENESYS_API_LINKAGE