diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2019-07-31 16:59:49 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2019-07-31 16:59:49 +0200 |
commit | 1687222e1b9e74c89cafbb5910e72d8ec7bfd40f (patch) | |
tree | d78102ce30207c63e7608eeba743efd680c888dc /backend/genesys.c | |
parent | 58912f68c2489bcee787599837447e0d64dfd61a (diff) |
New upstream version 1.0.28upstream/1.0.28
Diffstat (limited to 'backend/genesys.c')
-rw-r--r-- | backend/genesys.c | 8047 |
1 files changed, 0 insertions, 8047 deletions
diff --git a/backend/genesys.c b/backend/genesys.c deleted file mode 100644 index db0a2b2..0000000 --- a/backend/genesys.c +++ /dev/null @@ -1,8047 +0,0 @@ -/* sane - Scanner Access Now Easy. - - Copyright (C) 2003, 2004 Henning Meier-Geinitz <henning@meier-geinitz.de> - Copyright (C) 2004, 2005 Gerhard Jaeger <gerhard@gjaeger.de> - Copyright (C) 2004-2016 Stéphane Voltz <stef.dev@free.fr> - Copyright (C) 2005-2009 Pierre Willenbrock <pierre@pirsoft.dnsalias.org> - Copyright (C) 2006 Laurent Charpentier <laurent_pubs@yahoo.com> - Copyright (C) 2007 Luke <iceyfor@gmail.com> - Copyright (C) 2010 Chris Berry <s0457957@sms.ed.ac.uk> and Michael Rickmann <mrickma@gwdg.de> - for Plustek Opticbook 3600 support - - Dynamic rasterization code was taken from the epjistsu backend by - m. allan noah <kitno455 at gmail dot com> - - Software processing for deskew, crop and dspeckle are inspired by allan's - noah work in the fujitsu backend - - This file is part of the SANE package. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, - MA 02111-1307, USA. - - As a special exception, the authors of SANE give permission for - additional uses of the libraries contained in this release of SANE. - - The exception is that, if you link a SANE library with other files - to produce an executable, this does not by itself cause the - resulting executable to be covered by the GNU General Public - License. Your use of that executable is in no way restricted on - account of linking the SANE library code into it. - - This exception does not, however, invalidate any other reasons why - the executable file might be covered by the GNU General Public - License. - - If you submit changes to SANE to the maintainers to be included in - a subsequent release, you agree by submitting the changes that - those changes may be distributed with this exception intact. - - If you write modifications of your own for SANE, it is your choice - whether to permit this exception to apply to your modifications. - If you do not wish that, delete this exception notice. -*/ - -/* - * SANE backend for Genesys Logic GL646/GL841/GL842/GL843/GL846/GL847/GL124 based scanners - */ - -#define BUILD 2511 -#define BACKEND_NAME genesys - -#include "genesys.h" -#include "../include/sane/sanei_config.h" -#include "../include/sane/sanei_magic.h" -#include "genesys_devices.c" - -static SANE_Int num_devices = 0; -static Genesys_Device *first_dev = 0; -static Genesys_Scanner *first_handle = 0; -static const SANE_Device **devlist = 0; -/* Array of newly attached devices */ -static Genesys_Device **new_dev = 0; -/* Length of new_dev array */ -static SANE_Int new_dev_len = 0; -/* Number of entries alloced for new_dev */ -static SANE_Int new_dev_alloced = 0; - -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, - 0 -}; - -static SANE_String_Const color_filter_list[] = { - SANE_I18N ("Red"), - SANE_I18N ("Green"), - SANE_I18N ("Blue"), - 0 -}; - -static SANE_String_Const cis_color_filter_list[] = { - SANE_I18N ("Red"), - SANE_I18N ("Green"), - SANE_I18N ("Blue"), - SANE_I18N ("None"), - 0 -}; - -static SANE_String_Const source_list[] = { - SANE_I18N (FLATBED), - SANE_I18N (TRANSPARENCY_ADAPTER), - 0 -}; - -static SANE_Range swdespeck_range = { - 1, - 9, - 1 -}; - -static SANE_Range time_range = { - 0, /* minimum */ - 60, /* maximum */ - 0 /* quantization */ -}; - -static const SANE_Range u12_range = { - 0, /* minimum */ - 4095, /* maximum */ - 0 /* quantization */ -}; - -static const SANE_Range u14_range = { - 0, /* minimum */ - 16383, /* maximum */ - 0 /* quantization */ -}; - -static const SANE_Range u16_range = { - 0, /* minimum */ - 65535, /* maximum */ - 0 /* quantization */ -}; - -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 */ -}; - -/** - * range for brightness and contrast - */ -static const SANE_Range enhance_range = { - -100, /* minimum */ - 100, /* maximum */ - 1 /* quantization */ -}; - -/** - * range for expiration time - */ -static const SANE_Range expiration_range = { - -1, /* minimum */ - 30000, /* maximum */ - 1 /* quantization */ -}; - -void -sanei_genesys_init_structs (Genesys_Device * dev) -{ - unsigned int i, sensor_ok = 0, gpo_ok = 0, motor_ok = 0; - - /* initialize the sensor data stuff */ - for (i = 0; i < sizeof (Sensor) / sizeof (Genesys_Sensor); i++) - { - if (dev->model->ccd_type == Sensor[i].sensor_id) - { - memcpy (&dev->sensor, &Sensor[i], sizeof (Genesys_Sensor)); - sensor_ok = 1; - } - } - - /* initialize the GPO data stuff */ - for (i = 0; i < sizeof (Gpo) / sizeof (Genesys_Gpo); i++) - { - if (dev->model->gpo_type == Gpo[i].gpo_id) - { - memcpy (&dev->gpo, &Gpo[i], sizeof (Genesys_Gpo)); - gpo_ok = 1; - } - } - - /* initialize the motor data stuff */ - for (i = 0; i < sizeof (Motor) / sizeof (Genesys_Motor); i++) - { - if (dev->model->motor_type == Motor[i].motor_id) - { - memcpy (&dev->motor, &Motor[i], sizeof (Genesys_Motor)); - motor_ok = 1; - } - } - - /* sanity check */ - if (sensor_ok == 0 || motor_ok == 0 || gpo_ok == 0) - { - DBG (DBG_error0, - "sanei_genesys_init_structs: bad description(s) for ccd/gpo/motor=%d/%d/%d\n", - dev->model->ccd_type, dev->model->gpo_type, - dev->model->motor_type); - } - - /* set up initial line distance shift */ - dev->ld_shift_r = dev->model->ld_shift_r; - dev->ld_shift_g = dev->model->ld_shift_g; - dev->ld_shift_b = dev->model->ld_shift_b; -} - -void -sanei_genesys_init_fe (Genesys_Device * dev) -{ - unsigned int i; - - DBGSTART; - for (i = 0; i < sizeof (Wolfson) / sizeof (Genesys_Frontend); i++) - { - if (dev->model->dac_type == Wolfson[i].fe_id) - { - memcpy (&dev->frontend, &Wolfson[i], sizeof (Genesys_Frontend)); - return; - } - } - DBG (DBG_error0, - "sanei_genesys_init_fe: failed to find description for dac_type %d\n", - dev->model->dac_type); - DBG (DBG_info, "sanei_genesys_init_fe: dac_type %d set up\n", - dev->model->dac_type); - DBGCOMPLETED; -} - -/* main function for slope creation */ -/** - * This function generates a slope table using the given slope - * truncated at the given exposure time or step count, whichever comes first. - * The reached step time is then stored in final_exposure and used for the rest - * of the table. The summed time of the acceleration steps is returned, and the - * number of accerelation steps is put into used_steps. - * - * @param slope_table Table to write to - * @param max_steps Size of slope_table in steps - * @param use_steps Maximum number of steps to use for acceleration - * @param stop_at Minimum step time to use - * @param vstart Start step time of default slope - * @param vend End step time of default slope - * @param steps Step count of default slope - * @param g Power for default slope - * @param used_steps Final number of steps is stored here - * @param vfinal Final step time is stored here - * @return Time for acceleration - * @note All times in pixel time. Correction for other motor timings is not - * done. - */ -SANE_Int -sanei_genesys_generate_slope_table (uint16_t * slope_table, - unsigned int max_steps, - unsigned int use_steps, - uint16_t stop_at, - uint16_t vstart, - uint16_t vend, - unsigned int steps, - double g, - unsigned int *used_steps, - unsigned int *vfinal) -{ - double t; - SANE_Int sum = 0; - unsigned int i; - unsigned int c = 0; - uint16_t t2; - unsigned int dummy; - unsigned int _vfinal; - if (!used_steps) - used_steps = &dummy; - if (!vfinal) - vfinal = &_vfinal; - - DBG (DBG_proc, "sanei_genesys_generate_slope_table: table size: %d\n", - max_steps); - - DBG (DBG_proc, - "sanei_genesys_generate_slope_table: stop at time: %d, use %d steps max\n", - stop_at, use_steps); - - DBG (DBG_proc, - "sanei_genesys_generate_slope_table: target slope: " - "vstart: %d, vend: %d, steps: %d, g: %g\n", vstart, vend, steps, g); - - sum = 0; - c = 0; - *used_steps = 0; - - if (use_steps < 1) - use_steps = 1; - - if (stop_at < vstart) - { - t2 = vstart; - for (i = 0; i < steps && i < use_steps - 1 && i < max_steps; i++, c++) - { - t = pow (((double) i) / ((double) (steps - 1)), g); - t2 = vstart * (1 - t) + t * vend; - if (t2 < stop_at) - break; - *slope_table++ = t2; - /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, t2); */ - sum += t2; - } - if (t2 > stop_at) - { - DBG (DBG_warn, "Can not reach target speed(%d) in %d steps.\n", - stop_at, use_steps); - DBG (DBG_warn, "Expect image to be distorted. " - "Ignore this if only feeding.\n"); - } - *vfinal = t2; - *used_steps += i; - max_steps -= i; - } - else - *vfinal = stop_at; - - for (i = 0; i < max_steps; i++, c++) - { - *slope_table++ = *vfinal; - /* DBG (DBG_io, "slope_table[%3d] = %5d\n", c, *vfinal); */ - } - - (*used_steps)++; - sum += *vfinal; - - DBG (DBG_proc, - "sanei_genesys_generate_slope_table: returns sum=%d, used %d steps, completed\n", - sum, *used_steps); - - return sum; -} - -/* 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 reached step time is then stored in final_exposure and used for the rest - * of the table. 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 max_step Size of slope_table in steps - * @param use_steps Maximum number of steps to use for acceleration - * @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 - * @param final_exposure Final step time is stored here - * @param power_mode Power mode (related to the Vref used) of the motor - * @return Time for acceleration - * @note all times in pixel time - */ -SANE_Int -sanei_genesys_create_slope_table3 (Genesys_Device * dev, - uint16_t * slope_table, - int max_step, - unsigned int use_steps, - int step_type, - int exposure_time, - double yres, - unsigned int *used_steps, - unsigned int *final_exposure, - int power_mode) -{ - unsigned int sum_time = 0; - unsigned int vtarget; - unsigned int vend; - unsigned int vstart; - unsigned int vfinal; - - DBG (DBG_proc, - "%s: step_type = %d, " - "exposure_time = %d, yres = %g, power_mode = %d\n", __func__, step_type, - exposure_time, yres, power_mode); - - /* final speed */ - vtarget = (exposure_time * yres) / dev->motor.base_ydpi; - - vstart = dev->motor.slopes[power_mode][step_type].maximum_start_speed; - vend = dev->motor.slopes[power_mode][step_type].maximum_speed; - - vtarget >>= step_type; - if (vtarget > 65535) - vtarget = 65535; - - vstart >>= step_type; - if (vstart > 65535) - vstart = 65535; - - vend >>= step_type; - if (vend > 65535) - vend = 65535; - - sum_time = sanei_genesys_generate_slope_table (slope_table, - max_step, - use_steps, - vtarget, - vstart, - vend, - dev->motor.slopes[power_mode][step_type].minimum_steps << step_type, - dev->motor.slopes[power_mode][step_type].g, - used_steps, - &vfinal); - - if (final_exposure) - *final_exposure = (vfinal * dev->motor.base_ydpi) / yres; - - DBG (DBG_proc, - "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n", - sum_time); - - return sum_time; -} - - -/* alternate slope table creation function */ -/* the hardcoded values (g and vstart) will go in a motor struct */ -static SANE_Int -genesys_create_slope_table2 (Genesys_Device * dev, - uint16_t * slope_table, int steps, - int step_type, int exposure_time, - SANE_Bool same_speed, double yres, - int power_mode) -{ - double t, g; - SANE_Int sum = 0; - int vstart, vend; - int i; - - DBG (DBG_proc, - "sanei_genesys_create_slope_table2: %d steps, step_type = %d, " - "exposure_time = %d, same_speed = %d, yres = %.2f, power_mode = %d\n", - steps, step_type, exposure_time, same_speed, yres, power_mode); - - /* start speed */ - if (dev->model->motor_type == MOTOR_5345) - { - if (yres < dev->motor.base_ydpi / 6) - vstart = 2500; - else - vstart = 2000; - } - else - { - if (steps == 2) - vstart = exposure_time; - else if (steps == 3) - vstart = 2 * exposure_time; - else if (steps == 4) - vstart = 1.5 * exposure_time; - else if (steps == 120) - vstart = 1.81674 * exposure_time; - else - vstart = exposure_time; - } - - /* final speed */ - vend = (exposure_time * yres) / (dev->motor.base_ydpi * (1 << step_type)); - - /* - type=1 : full - type=2 : half - type=4 : quarter - vend * type * base_ydpi / exposure = yres - */ - - /* acceleration */ - switch (steps) - { - case 255: - /* test for special case: fast moving slope */ - /* todo: a 'fast' boolean parameter should be better */ - if (vstart == 2000) - g = 0.2013; - else - g = 0.1677; - break; - case 120: - g = 0.5; - break; - case 67: - g = 0.5; - break; - case 64: - g = 0.2555; - break; - case 44: - g = 0.5; - break; - case 4: - g = 0.5; - break; - case 3: - g = 1; - break; - case 2: - vstart = vend; - g = 1; - break; - default: - g = 0.2635; - } - - /* if same speed, no 'g' */ - sum = 0; - if (same_speed) - { - for (i = 0; i < 255; i++) - { - slope_table[i] = vend; - sum += slope_table[i]; - DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]); - } - } - else - { - for (i = 0; i < steps; i++) - { - t = pow (((double) i) / ((double) (steps - 1)), g); - slope_table[i] = vstart * (1 - t) + t * vend; - DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]); - sum += slope_table[i]; - } - for (i = steps; i < 255; i++) - { - slope_table[i] = vend; - DBG (DBG_io, "slope_table[%3d] = %5d\n", i, slope_table[i]); - sum += slope_table[i]; - } - } - - DBG (DBG_proc, - "sanei_genesys_create_slope_table2: returns sum=%d, completed\n", sum); - - return sum; -} - -/* Generate slope table for motor movement */ -/* todo: check details */ -SANE_Int -sanei_genesys_create_slope_table (Genesys_Device * dev, - uint16_t * slope_table, int steps, - int step_type, int exposure_time, - SANE_Bool same_speed, double yres, - int power_mode) -{ - double t; - double start_speed; - double g; - uint32_t time_period; - int sum_time = 0; - int i, divider; - int same_step; - - if (dev->model->motor_type == MOTOR_5345 - || dev->model->motor_type == MOTOR_HP2300 - || dev->model->motor_type == MOTOR_HP2400) - return genesys_create_slope_table2 (dev, slope_table, steps, - step_type, exposure_time, - same_speed, yres, power_mode); - - DBG (DBG_proc, - "sanei_genesys_create_slope_table: %d steps, step_type = %d, " - "exposure_time = %d, same_speed =%d\n", steps, step_type, - exposure_time, same_speed); - DBG (DBG_proc, "sanei_genesys_create_slope_table: yres = %.2f\n", yres); - - g = 0.6; - start_speed = 0.01; - same_step = 4; - divider = 1 << step_type; - - time_period = - (uint32_t) (yres * exposure_time / dev->motor.base_ydpi /*MOTOR_GEAR */ ); - if ((time_period < 2000) && (same_speed)) - same_speed = SANE_FALSE; - - time_period = time_period / divider; - - if (same_speed) - { - for (i = 0; i < steps; i++) - { - slope_table[i] = (uint16_t) time_period; - sum_time += time_period; - - DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period); - } - DBG (DBG_info, - "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n", - sum_time); - return sum_time; - } - - if (time_period > MOTOR_SPEED_MAX * 5) - { - g = 1.0; - start_speed = 0.05; - same_step = 2; - } - else if (time_period > MOTOR_SPEED_MAX * 4) - { - g = 0.8; - start_speed = 0.04; - same_step = 2; - } - else if (time_period > MOTOR_SPEED_MAX * 3) - { - g = 0.7; - start_speed = 0.03; - same_step = 2; - } - else if (time_period > MOTOR_SPEED_MAX * 2) - { - g = 0.6; - start_speed = 0.02; - same_step = 3; - } - - if (dev->model->motor_type == MOTOR_ST24) - { - steps = 255; - switch ((int) yres) - { - case 2400: - g = 0.1672; - start_speed = 1.09; - break; - case 1200: - g = 1; - start_speed = 6.4; - break; - case 600: - g = 0.1672; - start_speed = 1.09; - break; - case 400: - g = 0.2005; - start_speed = 20.0 / 3.0 /*7.5 */ ; - break; - case 300: - g = 0.253; - start_speed = 2.182; - break; - case 150: - g = 0.253; - start_speed = 4.367; - break; - default: - g = 0.262; - start_speed = 7.29; - } - same_step = 1; - } - - if (steps <= same_step) - { - time_period = - (uint32_t) (yres * exposure_time / - dev->motor.base_ydpi /*MOTOR_GEAR */ ); - time_period = time_period / divider; - - if (time_period > 65535) - time_period = 65535; - - for (i = 0; i < same_step; i++) - { - slope_table[i] = (uint16_t) time_period; - sum_time += time_period; - - DBG (DBG_io, "slope_table[%d] = %d\n", i, time_period); - } - - DBG (DBG_proc, - "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n", - sum_time); - return sum_time; - } - - for (i = 0; i < steps; i++) - { - double j = ((double) i) - same_step + 1; /* start from 1/16 speed */ - - if (j <= 0) - t = 0; - else - t = pow (j / (steps - same_step), g); - - time_period = /* time required for full steps */ - (uint32_t) (yres * exposure_time / - dev->motor.base_ydpi /*MOTOR_GEAR */ * - (start_speed + (1 - start_speed) * t)); - - time_period = time_period / divider; - if (time_period > 65535) - time_period = 65535; - - slope_table[i] = (uint16_t) time_period; - sum_time += time_period; - - DBG (DBG_io, "slope_table[%d] = %d\n", i, slope_table[i]); - } - - DBG (DBG_proc, - "sanei_genesys_create_slope_table: returns sum_time=%d, completed\n", - sum_time); - - return sum_time; -} - -/** @brief computes gamma table - * Generates a gamma table of the given length within 0 and the given - * maximum value - * @param gamma_table gamma table to fill - * @param size size of the table - * @param maximum value allowed for gamma - * @param gamma_max maximum gamma value - * @param gamma gamma to compute values - * @return a gamma table filled with the computed values - * */ -void -sanei_genesys_create_gamma_table (uint16_t * gamma_table, int size, - float maximum, float gamma_max, float gamma) -{ - int i; - float value; - - if(gamma_table==NULL) - { - DBG (DBG_proc, "sanei_genesys_create_gamma_table: gamma table is NULL\n"); - return; - } - DBG (DBG_proc, - "sanei_genesys_create_gamma_table: size = %d, " - "maximum = %g, gamma_max = %g, gamma = %g\n", - size, maximum, gamma_max, gamma); - for (i = 0; i < size; i++) - { - value = gamma_max * pow ((float) i / size, 1.0 / gamma); - if (value > maximum) - value = maximum; - gamma_table[i] = value; - } - DBG (DBG_proc, "sanei_genesys_create_gamma_table: completed\n"); -} - - -/* computes the exposure_time on the basis of the given vertical dpi, - the number of pixels the ccd needs to send, - the step_type and the corresponding maximum speed from the motor struct */ -/* - Currently considers maximum motor speed at given step_type, minimum - line exposure needed for conversion and led exposure time. - - TODO: Should also consider maximum transfer rate: ~6.5MB/s. - 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, - int step_type, int endpixel, - int exposure_by_led, int power_mode) -{ - int exposure_by_ccd = endpixel + 32; - int exposure_by_motor = - (dev->motor.slopes[power_mode][step_type].maximum_speed - * dev->motor.base_ydpi) / ydpi; - - int exposure = exposure_by_ccd; - - if (exposure < exposure_by_motor) - exposure = exposure_by_motor; - - 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, power=%d => exposure=%d\n", - __func__, (int)ydpi, step_type, endpixel, exposure_by_led, power_mode, exposure); - return exposure; -} - -/* computes the exposure_time on the basis of the given horizontal dpi */ -/* we will clean/simplify it by using constants from a future motor struct */ -SANE_Int -sanei_genesys_exposure_time (Genesys_Device * dev, Genesys_Register_Set * reg, - int xdpi) -{ - if (dev->model->motor_type == MOTOR_5345) - { - if (dev->model->cmd_set->get_filter_bit (reg)) - { - /* monochrome */ - switch (xdpi) - { - case 600: - return 8500; - case 500: - case 400: - case 300: - case 250: - case 200: - case 150: - return 5500; - case 100: - return 6500; - case 50: - return 12000; - default: - return 11000; - } - } - else - { - /* color scan */ - switch (xdpi) - { - case 300: - case 250: - case 200: - return 5500; - case 50: - return 12000; - default: - return 11000; - } - } - } - else if (dev->model->motor_type == MOTOR_HP2400) - { - if (dev->model->cmd_set->get_filter_bit (reg)) - { - /* monochrome */ - switch (xdpi) - { - case 200: - return 7210; - default: - return 11111; - } - } - else - { - /* color scan */ - switch (xdpi) - { - case 600: - return 8751; /*11902; 19200 */ - default: - return 11111; - } - } - } - else if (dev->model->motor_type == MOTOR_HP2300) - { - if (dev->model->cmd_set->get_filter_bit (reg)) - { - /* monochrome */ - switch (xdpi) - { - case 600: - return 8699; /* 3200; */ - case 300: - return 3200; /*10000;, 3200 -> too dark */ - case 150: - return 4480; /* 3200 ???, warmup needs 4480 */ - case 75: - return 5500; - default: - return 11111; - } - } - else - { - /* color scan */ - switch (xdpi) - { - case 600: - return 8699; - case 300: - return 4349; - case 150: - case 75: - return 4480; - default: - return 11111; - } - } - } - return dev->settings.exposure_time; -} - - - -/* 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. - */ -#ifndef UNIT_TESTING -static -#endif - SANE_Status -genesys_send_offset_and_shading (Genesys_Device * dev, uint8_t * data, - int size) -{ - int dpihw; - int start_address; - SANE_Status status; - - DBG (DBG_proc, "%s: (size = %d)\n", __func__, size); - - /* ASIC higher than gl843 doesn't have register 2A/2B, so we route to - * a per ASIC shading data loading function if available. - * It is also used for scanners using SHDAREA */ - if(dev->model->cmd_set->send_shading_data!=NULL) - { - status=dev->model->cmd_set->send_shading_data(dev, data, size); - DBGCOMPLETED; - return status; - } - - /* gl646, gl84[123] case */ - dpihw = sanei_genesys_read_reg_from_set (dev->reg, 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 < 2 - && dev->model->ccd_type != CCD_PLUSTEK3800 - && dev->model->ccd_type != CCD_KVSS080 - && dev->model->ccd_type != CCD_G4050 - && dev->model->ccd_type != CCD_CS4400F - && dev->model->ccd_type != CCD_CS8400F - && dev->model->ccd_type != CCD_DSMOBILE600 - && dev->model->ccd_type != CCD_XP300 - && dev->model->ccd_type != CCD_DP665 - && dev->model->ccd_type != CCD_DP685 - && dev->model->ccd_type != CIS_CANONLIDE80 - && dev->model->ccd_type != CCD_ROADWARRIOR - && dev->model->ccd_type != CCD_HP2300 - && dev->model->ccd_type != CCD_HP2400 - && dev->model->ccd_type != CCD_HP3670 - && dev->model->ccd_type != 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 */ - return SANE_STATUS_INVAL; - } - else /* color */ - start_address = 0x00; - - status = sanei_genesys_set_buffer_address (dev, start_address); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "%s: failed to set buffer address: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - status = dev->model->cmd_set->bulk_write_data (dev, 0x3c, data, size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "%s: failed to send shading table: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -/* ? */ -SANE_Status -sanei_genesys_init_shading_data (Genesys_Device * dev, int pixels_per_line) -{ - SANE_Status status; - uint8_t *shading_data, *shading_data_ptr; - int channels; - int i; - - /* these models don't need to init shading data due to the use of specific send shading data - function */ - if (dev->model->ccd_type==CCD_KVSS080 - || dev->model->ccd_type==CCD_G4050 - || dev->model->ccd_type==CCD_CS4400F - || dev->model->ccd_type==CCD_CS8400F - || dev->model->cmd_set->send_shading_data!=NULL) - return SANE_STATUS_GOOD; - - DBG (DBG_proc, "sanei_genesys_init_shading_data (pixels_per_line = %d)\n", - pixels_per_line); - - if (dev->settings.scan_mode >= 2) /* 3 pass or single pass color */ - channels = 3; - else - channels = 1; - - shading_data = malloc (pixels_per_line * 4 * channels); /* 16 bit black, 16 bit white */ - if (!shading_data) - { - DBG (DBG_error, - "sanei_genesys_init_shading_data: failed to allocate memory\n"); - return SANE_STATUS_NO_MEM; - } - - shading_data_ptr = shading_data; - - for (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 */ - *shading_data_ptr++ = 0x40; /* white hi -> 0x4000 */ - } - - status = genesys_send_offset_and_shading (dev, - shading_data, - pixels_per_line * 4 * channels); - free (shading_data); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "%s: failed to send shading data: %s\n", __func__, - sane_strstatus (status)); - } - - DBGCOMPLETED; - return status; -} - - -/* Find the position of the reference point: - takes gray level 8 bits data and find - first CCD usable pixel and top of scanning area */ -SANE_Status -sanei_genesys_search_reference_point (Genesys_Device * dev, uint8_t * data, - int start_pixel, int dpi, int width, - int height) -{ - int x, y; - int current, left, top = 0; - uint8_t *image; - int size, count; - int level = 80; /* edge threshold level */ - - /*sanity check */ - if ((width < 3) || (height < 3)) - return SANE_STATUS_INVAL; - - /* transformed image data */ - size = width * height; - image = malloc (size); - if (!image) - { - DBG (DBG_error, - "sanei_genesys_search_reference_point: failed to allocate memory\n"); - return SANE_STATUS_NO_MEM; - } - - /* laplace filter to denoise picture */ - memcpy (image, 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] = - (data[(y - 1) * width + x + 1] + 2 * data[(y - 1) * width + x] + - data[(y - 1) * width + x - 1] + 2 * data[y * width + x + 1] + - 4 * data[y * width + x] + 2 * data[y * width + x - 1] + - data[(y + 1) * width + x + 1] + 2 * data[(y + 1) * width + x] + - data[(y + 1) * width + x - 1]) / 16; - } - - memcpy (data, image, size); - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file ("laplace.pnm", image, 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 = - data[(y - 1) * width + x + 1] - data[(y - 1) * width + x - 1] + - 2 * data[y * width + x + 1] - 2 * data[y * width + x - 1] + - data[(y + 1) * width + x + 1] - data[(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 ("xsobel.pnm", image, 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 ("detected-xsobel.pnm", image, 8, 1, width, - height); - left = left / count; - - /* turn it in CCD pixel at full sensor optical resolution */ - dev->sensor.CCD_start_xoffset = - start_pixel + (left * dev->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 = - -data[(y - 1) * width + x + 1] - 2 * data[(y - 1) * width + x] - - data[(y - 1) * width + x - 1] + data[(y + 1) * width + x + 1] + - 2 * data[(y + 1) * width + x] + data[(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 ("ysobel.pnm", image, 8, 1, width, height); - - /* set up detection level */ - level = level / 3; - - /* search top of horizontal black stripe : TODO yet another flag */ - if (dev->model->ccd_type == CCD_5345 - && dev->model->motor_type == MOTOR_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 ("detected-ysobel.pnm", image, 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 = SANE_FIX ((top * MM_PER_INCH) / dpi); - DBG (DBG_info, - "sanei_genesys_search_reference_point: black stripe y_offset = %f mm \n", - SANE_UNFIX (dev->model->y_offset_calib)); - } - - /* find white corner in dark area : TODO yet another flag */ - if ((dev->model->ccd_type == CCD_HP2300 - && dev->model->motor_type == MOTOR_HP2300) - || (dev->model->ccd_type == CCD_HP2400 - && dev->model->motor_type == MOTOR_HP2400) - || (dev->model->ccd_type == CCD_HP3670 - && dev->model->motor_type == MOTOR_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 = SANE_FIX ((top * MM_PER_INCH) / dpi); - DBG (DBG_info, - "sanei_genesys_search_reference_point: white corner y_offset = %f mm\n", - SANE_UNFIX (dev->model->y_offset_calib)); - } - - free (image); - DBG (DBG_proc, - "sanei_genesys_search_reference_point: CCD_start_xoffset = %d, left = %d, top = %d\n", - dev->sensor.CCD_start_xoffset, left, top); - - return SANE_STATUS_GOOD; -} - - -void -sanei_genesys_calculate_zmode2 (SANE_Bool two_table, - uint32_t exposure_time, - uint16_t * slope_table, - int reg21, - int move, int reg22, uint32_t * z1, - uint32_t * z2) -{ - int i; - int sum; - DBG (DBG_info, "sanei_genesys_calculate_zmode2: two_table=%d\n", two_table); - - /* acceleration total time */ - sum = 0; - for (i = 0; i < reg21; i++) - sum += slope_table[i]; - - /* compute Z1MOD */ - /* c=sum(slope_table;reg21) - d=reg22*cruising speed - Z1MOD=(c+d) % exposure_time */ - *z1 = (sum + reg22 * slope_table[reg21 - 1]) % exposure_time; - - /* compute Z2MOD */ - /* a=sum(slope_table;reg21), b=move or 1 if 2 tables */ - /* Z2MOD=(a+b) % exposure_time */ - if (!two_table) - sum = sum + (move * slope_table[reg21 - 1]); - else - sum = sum + slope_table[reg21 - 1]; - *z2 = sum % exposure_time; -} - - -/* huh? */ -/* todo: double check */ -/* Z1 and Z2 seem to be a time to synchronize with clock or a phase correction */ -/* steps_sum is the result of create_slope_table */ -/* last_speed is the last entry of the slope_table */ -/* feedl is registers 3d,3e,3f */ -/* fastfed is register 02 bit 3 */ -/* scanfed is register 1f */ -/* fwdstep is register 22 */ -/* tgtime is register 6c bit 6+7 >> 6 */ - -void -sanei_genesys_calculate_zmode (uint32_t exposure_time, - uint32_t steps_sum, uint16_t last_speed, - uint32_t feedl, uint8_t fastfed, - uint8_t scanfed, uint8_t fwdstep, - uint8_t tgtime, uint32_t * z1, uint32_t * z2) -{ - uint8_t exposure_factor; - - exposure_factor = pow (2, tgtime); /* todo: originally, this is always 2^0 ! */ - - /* Z1 is for buffer-full backward forward moving */ - *z1 = - exposure_factor * ((steps_sum + fwdstep * last_speed) % exposure_time); - - /* Z2 is for acceleration before scan */ - if (fastfed) /* two curve mode */ - { - *z2 = - exposure_factor * ((steps_sum + scanfed * last_speed) % - exposure_time); - } - else /* one curve mode */ - { - *z2 = - exposure_factor * ((steps_sum + feedl * last_speed) % exposure_time); - } -} - - -static void -genesys_adjust_gain (double *applied_multi, - uint8_t * new_gain, double multi, uint8_t gain) -{ - double voltage, original_voltage; - - DBG (DBG_proc, "genesys_adjust_gain: multi=%f, gain=%d\n", multi, gain); - - voltage = 0.5 + gain * 0.25; - original_voltage = voltage; - - voltage *= multi; - - *new_gain = (uint8_t) ((voltage - 0.5) * 4); - if (*new_gain > 0x0e) - *new_gain = 0x0e; - - voltage = 0.5 + (*new_gain) * 0.25; - - *applied_multi = voltage / original_voltage; - - DBG (DBG_proc, - "genesys_adjust_gain: orig voltage=%.2f, new voltage=%.2f, " - "*applied_multi=%f, *new_gain=%d\n", original_voltage, voltage, - *applied_multi, *new_gain); - return; -} - - -/* todo: is return status necessary (unchecked?) */ -static SANE_Status -genesys_average_white (Genesys_Device * dev, int channels, int channel, - uint8_t * data, int size, int *max_average) -{ - int gain_white_ref, sum, range; - int average; - int i; - - DBG (DBG_proc, - "genesys_average_white: channels=%d, channel=%d, size=%d\n", - channels, channel, size); - - range = size / 50; - - if (dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY) /* transparency mode */ - gain_white_ref = dev->sensor.fau_gain_white_ref * 256; - else - gain_white_ref = dev->sensor.gain_white_ref * 256; - - if (range < 1) - range = 1; - - size = size / (2 * range * channels); - - data += (channel * 2); - - *max_average = 0; - - while (size--) - { - sum = 0; - for (i = 0; i < range; i++) - { - sum += (*data); - sum += *(data + 1) * 256; - data += (2 * channels); /* byte based */ - } - - average = (sum / range); - if (average > *max_average) - *max_average = average; - } - - DBG (DBG_proc, - "genesys_average_white: max_average=%d, gain_white_ref = %d, finished\n", - *max_average, gain_white_ref); - - if (*max_average >= gain_white_ref) - return SANE_STATUS_INVAL; - - return SANE_STATUS_GOOD; -} - -/* todo: understand, values are too high */ -static int -genesys_average_black (Genesys_Device * dev, int channel, - uint8_t * data, int pixels) -{ - int i; - int sum; - int pixel_step; - - DBG (DBG_proc, "genesys_average_black: channel=%d, pixels=%d\n", - channel, pixels); - - sum = 0; - - if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */ - { - data += (channel * 2); - pixel_step = 3 * 2; - } - else - { - pixel_step = 2; - } - - for (i = 0; i < pixels; i++) - { - sum += *data; - sum += *(data + 1) * 256; - - data += pixel_step; - } - - DBG (DBG_proc, "genesys_average_black = %d\n", sum / pixels); - - return (int) (sum / pixels); -} - - -/* todo: check; it works but the lines 1, 2, and 3 are too dark even with the - same offset and gain settings? */ -static SANE_Status -genesys_coarse_calibration (Genesys_Device * dev) -{ - int size; - int black_pixels; - int white_average; - int channels; - SANE_Status status; - uint8_t offset[4] = { 0xa0, 0x00, 0xa0, 0x40 }; /* first value isn't used */ - uint16_t white[12], dark[12]; - int i, j; - uint8_t *calibration_data, *all_data; - - DBG (DBG_info, "genesys_coarse_calibration (scan_mode = %d)\n", - dev->settings.scan_mode); - - black_pixels = dev->sensor.black_pixels - * dev->settings.xres / dev->sensor.optical_res; - - if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */ - channels = 3; - else - channels = 1; - - DBG (DBG_info, "channels %d y_size %d xres %d\n", - channels, dev->model->y_size, dev->settings.xres); - size = - channels * 2 * SANE_UNFIX (dev->model->y_size) * dev->settings.xres / - 25.4; - /* 1 1 mm 1/inch inch/mm */ - - calibration_data = malloc (size); - if (!calibration_data) - { - DBG (DBG_error, - "genesys_coarse_calibration: failed to allocate memory(%d bytes)\n", - size); - return SANE_STATUS_NO_MEM; - } - - all_data = calloc (1, size * 4); - - status = dev->model->cmd_set->set_fe (dev, AFE_INIT); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "%s: failed to set frontend: %s\n", __func__, - sane_strstatus (status)); - free(all_data); - free(calibration_data); - return status; - } - - dev->frontend.sign[0] = 0; - dev->frontend.sign[1] = 0; - dev->frontend.sign[2] = 0; - dev->frontend.gain[0] = 2; - dev->frontend.gain[1] = 2; - dev->frontend.gain[2] = 2; /* todo: ? was 2 */ - dev->frontend.offset[0] = offset[0]; - dev->frontend.offset[1] = offset[0]; - dev->frontend.offset[2] = offset[0]; - - for (i = 0; i < 4; i++) /* read 4 lines */ - { - if (i < 3) /* first 3 lines */ - { - dev->frontend.offset[0] = offset[i]; - dev->frontend.offset[1] = offset[i]; - dev->frontend.offset[2] = offset[i]; - } - - if (i == 1) /* second line */ - { - double applied_multi; - double gain_white_ref; - - if (dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY) /* Transparency */ - gain_white_ref = dev->sensor.fau_gain_white_ref * 256; - else - gain_white_ref = dev->sensor.gain_white_ref * 256; - /* white and black are defined downwards */ - - genesys_adjust_gain (&applied_multi, - &dev->frontend.gain[0], - gain_white_ref / (white[0] - dark[0]), - dev->frontend.gain[0]); - genesys_adjust_gain (&applied_multi, - &dev->frontend.gain[1], - gain_white_ref / (white[1] - dark[1]), - dev->frontend.gain[1]); - genesys_adjust_gain (&applied_multi, - &dev->frontend.gain[2], - gain_white_ref / (white[2] - dark[2]), - dev->frontend.gain[2]); - - dev->frontend.gain[0] = dev->frontend.gain[1] = - dev->frontend.gain[2] = 2; - - status = - sanei_genesys_fe_write_data (dev, 0x28, dev->frontend.gain[0]); - if (status != SANE_STATUS_GOOD) /* todo: this was 0x28 + 3 ? */ - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write gain[0]: %s\n", - sane_strstatus (status)); - return status; - } - - status = - sanei_genesys_fe_write_data (dev, 0x29, dev->frontend.gain[1]); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write gain[1]: %s\n", - sane_strstatus (status)); - return status; - } - - status = - sanei_genesys_fe_write_data (dev, 0x2a, dev->frontend.gain[2]); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write gain[2]: %s\n", - sane_strstatus (status)); - return status; - } - } - - if (i == 3) /* last line */ - { - double x, y, rate; - - for (j = 0; j < 3; j++) - { - - x = - (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; - - dev->frontend.offset[j] = (uint8_t) (rate); - - if (dev->frontend.offset[j] > 0x7f) - dev->frontend.offset[j] = 0x7f; - dev->frontend.offset[j] <<= 1; - } - } - status = - sanei_genesys_fe_write_data (dev, 0x20, dev->frontend.offset[0]); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write offset[0]: %s\n", - sane_strstatus (status)); - return status; - } - - status = - sanei_genesys_fe_write_data (dev, 0x21, dev->frontend.offset[1]); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write offset[1]: %s\n", - sane_strstatus (status)); - return status; - } - - status = - sanei_genesys_fe_write_data (dev, 0x22, dev->frontend.offset[2]); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to write offset[2]: %s\n", - sane_strstatus (status)); - return status; - } - - DBG (DBG_info, - "genesys_coarse_calibration: doing scan: sign: %d/%d/%d, gain: %d/%d/%d, offset: %d/%d/%d\n", - dev->frontend.sign[0], dev->frontend.sign[1], - dev->frontend.sign[2], dev->frontend.gain[0], - dev->frontend.gain[1], dev->frontend.gain[2], - dev->frontend.offset[0], dev->frontend.offset[1], - dev->frontend.offset[2]); - - status = - dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to begin scan: %s\n", - sane_strstatus (status)); - return status; - } - - status = - sanei_genesys_read_data_from_scanner (dev, calibration_data, size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to read data: %s\n", - sane_strstatus (status)); - return status; - } - - memcpy (all_data + i * size, calibration_data, size); - if (i == 3) /* last line */ - { - SANE_Byte *all_data_8 = malloc (size * 4 / 2); - unsigned int count; - - for (count = 0; count < (unsigned int) (size * 4 / 2); count++) - all_data_8[count] = all_data[count * 2 + 1]; - status = - sanei_genesys_write_pnm_file ("coarse.pnm", all_data_8, 8, - channels, size / 6, 4); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: sanei_genesys_write_pnm_file failed: %s\n", - sane_strstatus (status)); - return status; - } - } - - status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_coarse_calibration: Failed to end scan: %s\n", - sane_strstatus (status)); - return status; - } - if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */ - { - for (j = 0; j < 3; j++) - { - genesys_average_white (dev, 3, j, calibration_data, size, - &white_average); - white[i * 3 + j] = white_average; - dark[i * 3 + j] = - genesys_average_black (dev, j, calibration_data, - black_pixels); - DBG (DBG_info, - "genesys_coarse_calibration: white[%d]=%d, black[%d]=%d\n", - i * 3 + j, white[i * 3 + j], i * 3 + j, dark[i * 3 + j]); - } - } - else /* one color-component modes */ - { - genesys_average_white (dev, 1, 0, calibration_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, black_pixels); - } - - if (i == 3) - { - if (dev->settings.scan_mode == SCAN_MODE_COLOR) /* single pass color */ - { - /* todo: huh? */ - dev->dark[0] = - (uint16_t) (1.6925 * dark[i * 3 + 0] + 0.1895 * 256); - dev->dark[1] = - (uint16_t) (1.4013 * dark[i * 3 + 1] + 0.3147 * 256); - dev->dark[2] = - (uint16_t) (1.2931 * dark[i * 3 + 2] + 0.1558 * 256); - } - else /* one color-component modes */ - { - switch (dev->settings.color_filter) - { - case 0: - default: - dev->dark[0] = - (uint16_t) (1.6925 * dark[i * 3 + 0] + - (1.1895 - 1.0) * 256); - dev->dark[1] = dev->dark[2] = dev->dark[0]; - break; - - case 1: - dev->dark[1] = - (uint16_t) (1.4013 * dark[i * 3 + 1] + - (1.3147 - 1.0) * 256); - dev->dark[0] = dev->dark[2] = dev->dark[1]; - break; - - case 2: - dev->dark[2] = - (uint16_t) (1.2931 * dark[i * 3 + 2] + - (1.1558 - 1.0) * 256); - dev->dark[0] = dev->dark[1] = dev->dark[2]; - break; - } - } - } - } /* for (i = 0; i < 4; i++) */ - - free(all_data); - DBG (DBG_info, - "genesys_coarse_calibration: final: sign: %d/%d/%d, gain: %d/%d/%d, offset: %d/%d/%d\n", - dev->frontend.sign[0], dev->frontend.sign[1], dev->frontend.sign[2], - dev->frontend.gain[0], dev->frontend.gain[1], dev->frontend.gain[2], - dev->frontend.offset[0], dev->frontend.offset[1], - dev->frontend.offset[2]); - DBGCOMPLETED; - - return status; -} - -/* Averages image data. - average_data and calibration_data are little endian 16 bit words. - */ -#ifndef UNIT_TESTING -static -#endif -void -genesys_average_data (uint8_t * average_data, - uint8_t * calibration_data, - uint32_t lines, - uint32_t pixel_components_per_line) -{ - uint32_t x, y; - uint32_t sum; - - for (x = 0; x < pixel_components_per_line; x++) - { - sum = 0; - for (y = 0; y < lines; y++) - { - sum += calibration_data[(x + y * pixel_components_per_line) * 2]; - sum += - calibration_data[(x + y * pixel_components_per_line) * 2 + - 1] * 256; - } - sum /= lines; - *average_data++ = sum & 255; - *average_data++ = sum / 256; - } -} - -/** - * scans a white area with motor and lamp off to get the per CCD pixel offset - * that will be used to compute shading coefficient - * @param dev scanner's device - * @return SANE_STATUS_GOOD if OK, else an error - */ -static SANE_Status -genesys_dark_shading_calibration (Genesys_Device * dev) -{ - SANE_Status status; - size_t size; - uint32_t pixels_per_line; - uint8_t channels; - uint8_t *calibration_data; - SANE_Bool motor; - - DBGSTART; - - /* end pixel - start pixel */ - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; - - FREE_IFNOT_NULL (dev->dark_average_data); - - dev->average_size = channels * 2 * pixels_per_line; - - dev->dark_average_data = malloc (dev->average_size); - if (!dev->dark_average_data) - { - DBG (DBG_error, - "genesys_dark_shading_calibration: failed to allocate average memory\n"); - return SANE_STATUS_NO_MEM; - } - - /* size is size in bytes for scanarea: bytes_per_line * lines */ - size = channels * 2 * pixels_per_line * (dev->calib_lines + 1); - - calibration_data = malloc (size); - if (!calibration_data) - { - DBG (DBG_error, - "genesys_dark_shading_calibration: failed to allocate calibration data memory\n"); - return SANE_STATUS_NO_MEM; - } - - motor=SANE_TRUE; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) - { - motor=SANE_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 (dev->model->is_sheetfed == SANE_FALSE) - { - dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_FALSE); - dev->model->cmd_set->set_motor_power (dev->calib_reg, motor); - } - else - { - dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE); - dev->model->cmd_set->set_motor_power (dev->calib_reg, motor); - } - - status = - dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, - dev->model-> - cmd_set->bulk_full_size ()); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_dark_shading_calibration: failed to bulk write registers: %s\n", - sane_strstatus (status)); - return status; - } - - usleep (200 * 1000); /* wait 200 ms: lamp needs some time to get dark */ - - status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_dark_shading_calibration: Failed to begin scan: %s\n", - sane_strstatus (status)); - return status; - } - - status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_dark_shading_calibration: failed to read data: %s\n", - sane_strstatus (status)); - return status; - } - - status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_dark_shading_calibration: failed to end scan: %s\n", - sane_strstatus (status)); - return status; - } - - genesys_average_data (dev->dark_average_data, calibration_data, - dev->calib_lines, - pixels_per_line * channels); - - if (DBG_LEVEL >= DBG_data) - { - sanei_genesys_write_pnm_file ("black_shading.pnm", calibration_data, 16, - channels, pixels_per_line, - dev->calib_lines); - sanei_genesys_write_pnm_file ("black_average.pnm", - dev->dark_average_data, 16, channels, - pixels_per_line, 1); - } - - free (calibration_data); - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -/* - * this function builds dummy dark calibration data so that we can - * compute shading coefficient in a clean way - * todo: current values are hardcoded, we have to find if they - * can be computed from previous calibration data (when doing offset - * calibration ?) - */ -static SANE_Status -genesys_dummy_dark_shading (Genesys_Device * dev) -{ - uint32_t pixels_per_line; - uint8_t channels; - uint32_t x, skip, xend; - int dummy1, dummy2, dummy3; /* dummy black average per channel */ - - DBGSTART; - - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; - - FREE_IFNOT_NULL (dev->dark_average_data); - - dev->average_size = channels * 2 * pixels_per_line; - dev->dark_average_data = malloc (dev->average_size); - if (!dev->dark_average_data) - { - DBG (DBG_error, - "genesys_dummy_dark_shading: failed to allocate average memory\n"); - return SANE_STATUS_NO_MEM; - } - memset (dev->dark_average_data, 0x00, channels * 2 * pixels_per_line); - - /* 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 <= dev->sensor.optical_res / 2) - { - skip = 4; - xend = 36; - } - else - { - skip = 4; - xend = 68; - } - if (dev->model->ccd_type==CCD_G4050 - || dev->model->ccd_type==CCD_CS4400F - || dev->model->ccd_type==CCD_CS8400F - || dev->model->ccd_type==CCD_KVSS080) - { - skip = 2; - xend = dev->sensor.black_pixels; - } - - /* average each channels on half left margin */ - dummy1 = 0; - dummy2 = 0; - dummy3 = 0; - - for (x = skip + 1; x <= xend; x++) - { - dummy1 += - dev->white_average_data[channels * 2 * x] + - 256 * dev->white_average_data[channels * 2 * x + 1]; - if (channels > 1) - { - dummy2 += - (dev->white_average_data[channels * 2 * x + 2] + - 256 * dev->white_average_data[channels * 2 * x + 3]); - dummy3 += - (dev->white_average_data[channels * 2 * x + 4] + - 256 * dev->white_average_data[channels * 2 * x + 5]); - } - } - - dummy1 /= (xend - skip); - if (channels > 1) - { - dummy2 /= (xend - skip); - dummy3 /= (xend - skip); - } - DBG (DBG_proc, - "genesys_dummy_dark_shading: dummy1=%d, dummy2=%d, dummy3=%d \n", - dummy1, dummy2, dummy3); - - /* fill dark_average */ - for (x = 0; x < pixels_per_line; x++) - { - dev->dark_average_data[channels * 2 * x] = dummy1 & 0xff; - dev->dark_average_data[channels * 2 * x + 1] = dummy1 >> 8; - if (channels > 1) - { - dev->dark_average_data[channels * 2 * x + 2] = dummy2 & 0xff; - dev->dark_average_data[channels * 2 * x + 3] = dummy2 >> 8; - dev->dark_average_data[channels * 2 * x + 4] = dummy3 & 0xff; - dev->dark_average_data[channels * 2 * x + 5] = dummy3 >> 8; - } - } - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - - -static SANE_Status -genesys_white_shading_calibration (Genesys_Device * dev) -{ - SANE_Status status; - size_t size; - uint32_t pixels_per_line; - uint8_t *calibration_data; - uint8_t channels; - SANE_Bool motor; - - DBG (DBG_proc, "genesys_white_shading_calibration (lines = %d)\n", - (unsigned int)dev->calib_lines); - - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; - - if (dev->white_average_data) - free (dev->white_average_data); - - dev->white_average_data = malloc (channels * 2 * pixels_per_line); - if (!dev->white_average_data) - { - DBG (DBG_error, - "genesys_white_shading_calibration: failed to allocate average memory\n"); - return SANE_STATUS_NO_MEM; - } - - size = channels * 2 * pixels_per_line * (dev->calib_lines + 1); - - calibration_data = malloc (size); - if (!calibration_data) - { - DBG (DBG_error, - "genesys_white_shading_calibration: failed to allocate calibration memory\n"); - return SANE_STATUS_NO_MEM; - } - - motor=SANE_TRUE; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) - { - motor=SANE_FALSE; - } - - /* turn on motor and lamp power */ - dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE); - dev->model->cmd_set->set_motor_power (dev->calib_reg, motor); - - /* if needed, go back before doin next scan, by using rewind, registers and - * slopes table are kept intact from previous scan */ - if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK && dev->model->cmd_set->rewind) - { - status = dev->model->cmd_set->rewind (dev); - } - - status = - dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, - dev->model-> - cmd_set->bulk_full_size ()); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_white_shading_calibration: failed to bulk write registers: %s\n", - sane_strstatus (status)); - return status; - } - - if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) - usleep (500 * 1000); /* wait 500ms to make sure lamp is bright again */ - - status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_white_shading_calibration: Failed to begin scan: %s\n", - sane_strstatus (status)); - return status; - } - - status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_white_shading_calibration: failed to read data: %s\n", - sane_strstatus (status)); - return status; - } - - status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, - "genesys_white_shading_calibration: failed to end scan: %s\n", - sane_strstatus (status)); - return status; - } - - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file ("white_shading.pnm", calibration_data, 16, - channels, pixels_per_line, - dev->calib_lines); - - genesys_average_data (dev->white_average_data, calibration_data, - dev->calib_lines, - pixels_per_line * channels); - - if (DBG_LEVEL >= DBG_data) - sanei_genesys_write_pnm_file ("white_average.pnm", - dev->white_average_data, 16, channels, - pixels_per_line, 1); - - free (calibration_data); - - /* in case we haven't done dark calibration, build dummy data from white_average */ - if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) - { - status = genesys_dummy_dark_shading (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_white_shading_calibration: failed to do dummy dark shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - - if (dev->model->flags & GENESYS_FLAG_SHADING_REPARK) - { - status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE); - } - - DBGCOMPLETED; - - return status; -} - -/* This calibration uses a scan over the calibration target, comprising a - * black and a white strip. (So the motor must be on.) - */ -#ifndef UNIT_TESTING -static -#endif -SANE_Status -genesys_dark_white_shading_calibration (Genesys_Device * dev) -{ - SANE_Status status; - size_t size; - uint32_t pixels_per_line; - uint8_t *calibration_data, *average_white, *average_dark; - uint8_t channels; - unsigned int x; - int y; - uint32_t dark, white, dark_sum, white_sum, dark_count, white_count, col, - dif; - SANE_Bool motor; - - - DBG (DBG_proc, "%s: (lines = %d)\n", __func__, (unsigned int)dev->calib_lines); - - pixels_per_line = dev->calib_pixels; - channels = dev->calib_channels; - - if (dev->white_average_data) - free (dev->white_average_data); - - dev->average_size = channels * 2 * pixels_per_line; - - dev->white_average_data = malloc (dev->average_size); - if (!dev->white_average_data) - { - DBG (DBG_error, "%s: failed to allocate white average memory\n", __func__); - return SANE_STATUS_NO_MEM; - } - - if (dev->dark_average_data) - free (dev->dark_average_data); - - dev->dark_average_data = malloc (channels * 2 * pixels_per_line); - if (!dev->dark_average_data) - { - DBG (DBG_error, "%s: failed to allocate dark average memory\n", __func__); - return SANE_STATUS_NO_MEM; - } - - size = channels * 2 * pixels_per_line * dev->calib_lines; - - calibration_data = malloc (size); - if (!calibration_data) - { - DBG (DBG_error, "%s: failed to allocate calibration memory\n", __func__); - return SANE_STATUS_NO_MEM; - } - - motor=SANE_TRUE; - if (dev->model->flags & GENESYS_FLAG_SHADING_NO_MOVE) - { - motor=SANE_FALSE; - } - - /* turn on motor and lamp power */ - dev->model->cmd_set->set_lamp_power (dev, dev->calib_reg, SANE_TRUE); - dev->model->cmd_set->set_motor_power (dev->calib_reg, motor); - - status = - dev->model->cmd_set->bulk_write_register (dev, dev->calib_reg, - dev->model-> - cmd_set->bulk_full_size ()); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, "%s: failed to bulk write registers: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - status = dev->model->cmd_set->begin_scan (dev, dev->calib_reg, SANE_FALSE); - - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, "%s: failed to begin scan: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - status = sanei_genesys_read_data_from_scanner (dev, calibration_data, size); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, "%s: failed to read data: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - status = dev->model->cmd_set->end_scan (dev, dev->calib_reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - free (calibration_data); - DBG (DBG_error, "%s: Failed to end scan: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - if (DBG_LEVEL >= DBG_data) - { - if (dev->model->is_cis) - { - sanei_genesys_write_pnm_file ("black_white_shading.pnm", calibration_data, - 16, 1, pixels_per_line*channels, - dev->calib_lines); - } - else - { - sanei_genesys_write_pnm_file ("black_white_shading.pnm", calibration_data, - 16, channels, pixels_per_line, - dev->calib_lines); - } - } - - - average_white = dev->white_average_data; - average_dark = dev->dark_average_data; - - for (x = 0; x < pixels_per_line * channels; x++) - { - dark = 0xffff; - white = 0; - - for (y = 0; y < (int)dev->calib_lines; y++) - { - col = calibration_data[(x + y * pixels_per_line * channels) * 2]; - col |= - calibration_data[(x + y * pixels_per_line * channels) * 2 + - 1] << 8; - - if (col > white) - white = col; - if (col < dark) - dark = col; - } - - dif = white - dark; - - dark = dark + dif / 8; - white = white - dif / 8; - - dark_count = 0; - dark_sum = 0; - - white_count = 0; - white_sum = 0; - - for (y = 0; y < (int)dev->calib_lines; y++) - { - col = calibration_data[(x + y * pixels_per_line * channels) * 2]; - col |= - calibration_data[(x + y * pixels_per_line * channels) * 2 + - 1] << 8; - - if (col >= white) - { - white_sum += col; - white_count++; - } - if (col <= dark) - { - dark_sum += col; - dark_count++; - } - - } - - dark_sum /= dark_count; - white_sum /= white_count; - - *average_dark++ = dark_sum & 255; - *average_dark++ = dark_sum >> 8; - - *average_white++ = white_sum & 255; - *average_white++ = white_sum >> 8; - } - - if (DBG_LEVEL >= DBG_data) - { - sanei_genesys_write_pnm_file ("white_average.pnm", - dev->white_average_data, 16, channels, - pixels_per_line, 1); - sanei_genesys_write_pnm_file ("dark_average.pnm", - dev->dark_average_data, 16, channels, - pixels_per_line, 1); - } - - free (calibration_data); - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -/* computes one coefficient given bright-dark value - * @param coeff factor giving 1.00 gain - * @param target desired target code - * @param value brght-dark value - * */ -static unsigned int -compute_coefficient (unsigned int coeff, unsigned int target, unsigned int value) -{ - int result; - - if (value > 0) - { - result = (coeff * target) / value; - if (result >= 65535) - { - result = 65535; - } - } - else - { - result = coeff; - } - return result; -} - -/** @brief compute shading coefficients for LiDE scanners - * The dark/white shading is actually performed _after_ reducing - * resolution via averaging. only dark/white shading data for what would be - * first pixel at full resolution is used. - * - * scanner raw input to output value calculation: - * o=(i-off)*(gain/coeff) - * - * from datasheet: - * off=dark_average - * gain=coeff*bright_target/(bright_average-dark_average) - * works for dark_target==0 - * - * what we want is these: - * bright_target=(bright_average-off)*(gain/coeff) - * dark_target=(dark_average-off)*(gain/coeff) - * leading to - * off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target) - * gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff - * - * @param dev scanner's device - * @param shading_data memory area where to store the computed shading coefficients - * @param pixels_per_line number of pixels per line - * @param words_per_color memory words per color channel - * @param channels number of color channels (actually 1 or 3) - * @param o shading coefficients left offset - * @param coeff 4000h or 2000h depending on fast scan mode or not (GAIN4 bit) - * @param target_bright value of the white target code - * @param target_dark value of the black target code -*/ -#ifndef UNIT_TESTING -static -#endif -void -compute_averaged_planar (Genesys_Device * dev, - uint8_t * shading_data, - unsigned int pixels_per_line, - unsigned int words_per_color, - unsigned int channels, - unsigned int o, - unsigned int coeff, - unsigned int target_bright, - unsigned int target_dark) -{ - unsigned int x, i, j, br, dk, res, avgpixels, basepixels, val; - unsigned int fill,factor; - - DBG (DBG_info, "%s: pixels=%d, offset=%d\n", __func__, pixels_per_line, o); - - /* initialize result */ - memset (shading_data, 0xff, words_per_color * 3 * 2); - - /* - strangely i can write 0x20000 bytes beginning at 0x00000 without overwriting - slope tables - which begin at address 0x10000(for 1200dpi hw mode): - memory is organized in words(2 bytes) instead of single bytes. explains - quite some things - */ -/* - another one: the dark/white shading is actually performed _after_ reducing - resolution via averaging. only dark/white shading data for what would be - first pixel at full resolution is used. - */ -/* - scanner raw input to output value calculation: - o=(i-off)*(gain/coeff) - - from datasheet: - off=dark_average - gain=coeff*bright_target/(bright_average-dark_average) - works for dark_target==0 - - what we want is these: - bright_target=(bright_average-off)*(gain/coeff) - dark_target=(dark_average-off)*(gain/coeff) - leading to - off = (dark_average*bright_target - bright_average*dark_target)/(bright_target - dark_target) - gain = (bright_target - dark_target)/(bright_average - dark_average)*coeff - */ - res = dev->settings.xres; - - /* duplicate half-ccd logic */ - if ((dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE) && - dev->settings.xres <= dev->sensor.optical_res / 2) - res *= 2; - - /* this should be evenly dividable */ - basepixels = dev->sensor.optical_res / 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) - avgpixels = 1; - else if (basepixels < 6) - avgpixels = basepixels; - else if (basepixels < 8) - avgpixels = 6; - else if (basepixels < 10) - avgpixels = 8; - else if (basepixels < 12) - avgpixels = 10; - else if (basepixels < 15) - avgpixels = 12; - else - avgpixels = 15; - - /* LiDE80 packs shading data */ - if(dev->model->ccd_type != CIS_CANONLIDE80) - { - factor=1; - fill=avgpixels; - } - else - { - factor=avgpixels; - fill=1; - } - - DBG (DBG_info, "%s: averaging over %d pixels\n", __func__, avgpixels); - DBG (DBG_info, "%s: packing factor is %d\n", __func__, factor); - DBG (DBG_info, "%s: fill length is %d\n", __func__, fill); - - for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) - { - if ((x + o) * 2 * 2 + 3 > words_per_color * 2) - break; - - for (j = 0; j < channels; j++) - { - - dk = 0; - br = 0; - for (i = 0; i < avgpixels; i++) - { - /* dark data */ - dk += - (dev->dark_average_data[(x + i + - pixels_per_line * j) * - 2] | - (dev->dark_average_data - [(x + i + pixels_per_line * j) * 2 + 1] << 8)); - - /* white data */ - br += - (dev->white_average_data[(x + i + - pixels_per_line * j) * - 2] | - (dev->white_average_data - [(x + i + pixels_per_line * j) * 2 + 1] << 8)); - } - - br /= avgpixels; - dk /= avgpixels; - - if (br * target_dark > dk * target_bright) - val = 0; - else if (dk * target_bright - br * target_dark > - 65535 * (target_bright - target_dark)) - val = 65535; - else - { - val = (dk * target_bright - br * target_dark) / (target_bright - target_dark); - } - - /*fill all pixels, even if only the last one is relevant*/ - for (i = 0; i < fill; i++) - { - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j] = val & 0xff; - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = val >> 8; - } - - val = br - dk; - - if (65535 * val > (target_bright - target_dark) * coeff) - { - val = (coeff * (target_bright - target_dark)) / val; - } - else - { - val = 65535; - } - - /*fill all pixels, even if only the last one is relevant*/ - for (i = 0; i < fill; i++) - { - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = val & 0xff; - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = val >> 8; - } - } - - /* fill remaining channels */ - for (j = channels; j < 3; j++) - { - for (i = 0; i < fill; i++) - { - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j ] = shading_data[(x/factor + o + i) * 2 * 2 ]; - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 1] = shading_data[(x/factor + o + i) * 2 * 2 + 1]; - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 2] = shading_data[(x/factor + o + i) * 2 * 2 + 2]; - shading_data[(x/factor + o + i) * 2 * 2 + words_per_color * 2 * j + 3] = shading_data[(x/factor + o + i) * 2 * 2 + 3]; - } - } - } -} - -/** - * Computes shading coefficient using formula in data sheet. 16bit data values - * manipulated here are little endian. For now we assume deletion scanning type - * and that there is always 3 channels. - * @param dev scanner's device - * @param shading_data memory area where to store the computed shading coefficients - * @param pixels_per_line number of pixels per line - * @param channels number of color channels (actually 1 or 3) - * @param cmat color transposition matrix - * @param offset shading coefficients left offset - * @param coeff 4000h or 2000h depending on fast scan mode or not - * @param target value of the target code - */ -#ifndef UNIT_TESTING -static -#endif -void -compute_coefficients (Genesys_Device * dev, - uint8_t * shading_data, - unsigned int pixels_per_line, - unsigned int channels, - unsigned int cmat[3], - int offset, - unsigned int coeff, - unsigned int target) -{ - uint8_t *ptr; /* contain 16bit words in little endian */ - unsigned int x, c; - unsigned int val, br, dk; - unsigned int start, end; - - DBG (DBG_io, - "compute_coefficients: pixels_per_line=%d, coeff=0x%04x\n", pixels_per_line, coeff); - - /* compute start & end values depending of the offset */ - if (offset < 0) - { - start = -1 * offset; - end = pixels_per_line; - } - else - { - start = 0; - end = pixels_per_line - offset; - } - - for (c = 0; c < channels; c++) - { - for (x = start; x < end; x++) - { - /* TODO if channels=1 , use filter to know the base addr */ - ptr = shading_data + 4 * ((x + offset) * channels + cmat[c]); - - /* dark data */ - dk = dev->dark_average_data[x * 2 * channels + c * 2]; - dk += 256 * dev->dark_average_data[x * 2 * channels + c * 2 + 1]; - - /* white data */ - br = dev->white_average_data[x * 2 * channels + c * 2]; - br += 256 * dev->white_average_data[x * 2 * channels + c * 2 + 1]; - - /* compute coeff */ - val=compute_coefficient(coeff,target,br-dk); - - /* assign it */ - ptr[0] = dk & 255; - ptr[1] = dk / 256; - ptr[2] = val & 0xff; - ptr[3] = val / 256; - - } - } -} - -/** - * Computes shading coefficient using formula in data sheet. 16bit data values - * manipulated here are little endian. Data is in planar form, ie grouped by - * lines of the same color component. - * @param dev scanner's device - * @param shading_data memory area where to store the computed shading coefficients - * @param factor averaging factor when the calibration scan is done at a higher resolution - * than the final scan - * @param pixels_per_line number of pixels per line - * @param words_per_color total number of shading data words for one color element - * @param channels number of color channels (actually 1 or 3) - * @param cmat transcoding matrix for color channel order - * @param offset shading coefficients left offset - * @param coeff 4000h or 2000h depending on fast scan mode or not - * @param target white target value - */ -#ifndef UNIT_TESTING -static -#endif -void -compute_planar_coefficients (Genesys_Device * dev, - uint8_t * shading_data, - unsigned int factor, - unsigned int pixels_per_line, - unsigned int words_per_color, - unsigned int channels, - unsigned int cmat[3], - unsigned int offset, - unsigned int coeff, - unsigned int target) -{ - uint8_t *ptr; /* contains 16bit words in little endian */ - uint32_t x, c, i; - uint32_t val, dk, br; - - DBG (DBG_io, - "compute_planar_coefficients: factor=%d, pixels_per_line=%d, words=0x%X, coeff=0x%04x\n", factor, - pixels_per_line, words_per_color, coeff); - for (c = 0; c < channels; c++) - { - /* shading data is larger than pixels_per_line so offset can be neglected */ - for (x = 0; x < pixels_per_line; x+=factor) - { - /* x2 because of 16 bit values, and x2 since one coeff for dark - * and another for white */ - ptr = shading_data + words_per_color * cmat[c] * 2 + (x + offset) * 4; - - dk = 0; - br = 0; - - /* average case */ - for(i=0;i<factor;i++) - { - dk += - 256 * dev->dark_average_data[((x+i) + pixels_per_line * c) * 2 + 1]; - dk += dev->dark_average_data[((x+i) + pixels_per_line * c) * 2]; - br += - 256 * dev->white_average_data[((x+i) + pixels_per_line * c) * 2 + 1]; - br += dev->white_average_data[((x+i) + pixels_per_line * c) * 2]; - } - dk /= factor; - br /= factor; - - val = compute_coefficient (coeff, target, br - dk); - - /* we duplicate the information to have calibration data at optical resolution */ - for (i = 0; i < factor; i++) - { - ptr[0 + 4 * i] = dk & 255; - ptr[1 + 4 * i] = dk / 256; - ptr[2 + 4 * i] = val & 0xff; - ptr[3 + 4 * i] = val / 256; - } - } - } - /* in case of gray level scan, we duplicate shading information on all - * three color channels */ - if(channels==1) - { - memcpy(shading_data+cmat[1]*2*words_per_color, - shading_data+cmat[0]*2*words_per_color, - words_per_color*2); - memcpy(shading_data+cmat[2]*2*words_per_color, - shading_data+cmat[0]*2*words_per_color, - words_per_color*2); - } -} - -#ifndef UNIT_TESTING -static -#endif -void -compute_shifted_coefficients (Genesys_Device * dev, - uint8_t * shading_data, - unsigned int pixels_per_line, - unsigned int channels, - unsigned int cmat[3], - int offset, - unsigned int coeff, - unsigned int target_dark, - unsigned int target_bright, - unsigned int patch_size) /* contigous extent */ -{ - unsigned int x, avgpixels, basepixels, i, j, val1, val2; - unsigned int br_tmp [3], dk_tmp [3]; - uint8_t *ptr = shading_data + offset * 3 * 4; /* contain 16bit words in little endian */ - unsigned int patch_cnt = offset * 3; /* at start, offset of first patch */ - - x = dev->settings.xres; - if ((dev->model->flags & GENESYS_FLAG_HALF_CCD_MODE) && - (dev->settings.xres <= dev->sensor.optical_res / 2)) - x *= 2; /* scanner is using half-ccd mode */ - basepixels = dev->sensor.optical_res / 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) - avgpixels = 1; - else if (basepixels < 6) - avgpixels = basepixels; - else if (basepixels < 8) - avgpixels = 6; - else if (basepixels < 10) - avgpixels = 8; - else if (basepixels < 12) - avgpixels = 10; - else if (basepixels < 15) - avgpixels = 12; - else - avgpixels = 15; - DBG (DBG_info, "compute_shifted_coefficients: pixels_per_line=%d, coeff=0x%04x, averaging over %d pixels\n", pixels_per_line, coeff, avgpixels); - - for (x = 0; x <= pixels_per_line - avgpixels; x += avgpixels) { - memset (&br_tmp, 0, sizeof(br_tmp)); - memset (&dk_tmp, 0, sizeof(dk_tmp)); - - for (i = 0; i < avgpixels; i++) { - for (j = 0; j < channels; j++) { - br_tmp[j] += (dev->white_average_data[((x + i) * channels + j) * 2] | - (dev->white_average_data[((x + i) * channels + j) * 2 + 1] << 8)); - dk_tmp[i] += (dev->dark_average_data[((x + i) * channels + j) * 2] | - (dev->dark_average_data[((x + i) * channels + j) * 2 + 1] << 8)); - } - } - for (j = 0; j < channels; j++) { - br_tmp[j] /= avgpixels; - dk_tmp[j] /= avgpixels; - - if (br_tmp[j] * target_dark > dk_tmp[j] * target_bright) - val1 = 0; - else if (dk_tmp[j] * target_bright - br_tmp[j] * target_dark > 65535 * (target_bright - target_dark)) - val1 = 65535; - else - val1 = (dk_tmp[j] * target_bright - br_tmp[j] * target_dark) / (target_bright - target_dark); - - val2 = br_tmp[j] - dk_tmp[j]; - if (65535 * val2 > (target_bright - target_dark) * coeff) - val2 = (coeff * (target_bright - target_dark)) / val2; - else - val2 = 65535; - - br_tmp[j] = val1; - dk_tmp[j] = val2; - } - for (i = 0; i < avgpixels; i++) { - for (j = 0; j < channels; j++) { - * ptr++ = br_tmp[ cmat[j] ] & 0xff; - * ptr++ = br_tmp[ cmat[j] ] >> 8; - * ptr++ = dk_tmp[ cmat[j] ] & 0xff; - * ptr++ = dk_tmp[ cmat[j] ] >> 8; - patch_cnt++; - if (patch_cnt == patch_size) { - patch_cnt = 0; - val1 = cmat[2]; - cmat[2] = cmat[1]; - cmat[1] = cmat[0]; - cmat[0] = val1; - } - } - } - } -} - -GENESYS_STATIC SANE_Status -genesys_send_shading_coefficient (Genesys_Device * dev) -{ - SANE_Status status; - uint32_t pixels_per_line; - uint8_t *shading_data; /**> contains 16bit words in little endian */ - uint8_t channels; - int o; - unsigned int length; /**> number of shading calibration data words */ - unsigned int factor; - unsigned int cmat[3]; /**> matrix of color channels */ - unsigned int coeff, target_code, words_per_color = 0; - - DBGSTART; - - pixels_per_line = dev->calib_pixels; - channels = dev->calib_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 - * to the next one, which allow to write the 3 channels in 1 write - * during genesys_send_shading_coefficient, some values are words, other bytes - * hence the x2 factor */ - switch (sanei_genesys_read_reg_from_set (dev->reg, 0x05) >> 6) - { - /* 600 dpi */ - case 0: - words_per_color = 0x2a00; - break; - /* 1200 dpi */ - case 1: - words_per_color = 0x5500; - break; - /* 2400 dpi */ - case 2: - words_per_color = 0xa800; - break; - /* 4800 dpi */ - case 3: - words_per_color = 0x15000; - break; - } - - /* special case, memory is aligned on 0x5400, this has yet to be explained */ - /* could be 0xa800 because sensor is truly 2400 dpi, then halved because - * we only set 1200 dpi */ - if(dev->model->ccd_type==CIS_CANONLIDE80) - { - words_per_color = 0x5400; - } - - length = words_per_color * 3 * 2; - - /* allocate computed size */ - shading_data = malloc (length); - if (!shading_data) - { - DBG (DBG_error, "%s: failed to allocate memory\n", __func__); - return SANE_STATUS_NO_MEM; - } - memset (shading_data, 0, length); - - /* 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 (dev->model->cmd_set->get_gain4_bit (dev->calib_reg)) - coeff = 0x4000; - else - coeff = 0x2000; - - /* compute avg factor */ - if(dev->settings.xres>dev->sensor.optical_res) - { - factor=1; - } - else - { - factor=dev->sensor.optical_res/dev->settings.xres; - } - - /* for GL646, shading data is planar if REG01_FASTMOD is set and - * chunky if not. For now we rely on the fact that we know that - * each sensor is used only in one mode. Currently only the CIS_XP200 - * sets REG01_FASTMOD. - */ - - /* TODO setup a struct in genesys_devices that - * will handle these settings instead of having this switch growing up */ - cmat[0] = 0; - cmat[1] = 1; - cmat[2] = 2; - switch (dev->model->ccd_type) - { - case CCD_XP300: - case CCD_ROADWARRIOR: - case CCD_DP665: - case CCD_DP685: - case CCD_DSMOBILE600: - target_code = 0xdc00; - o = 4; - compute_planar_coefficients (dev, - shading_data, - factor, - pixels_per_line, - words_per_color, - channels, - cmat, - o, - coeff, - target_code); - break; - case CIS_XP200: - target_code = 0xdc00; - o = 2; - cmat[0] = 2; /* red is last */ - cmat[1] = 0; /* green is first */ - cmat[2] = 1; /* blue is second */ - compute_planar_coefficients (dev, - shading_data, - 1, - pixels_per_line, - words_per_color, - channels, - cmat, - o, - coeff, - target_code); - break; - case CCD_HP2300: - target_code = 0xdc00; - o = 2; - if(dev->settings.xres<=dev->sensor.optical_res/2) - { - o = o - dev->sensor.dummy_pixel / 2; - } - compute_coefficients (dev, - shading_data, - pixels_per_line, - 3, - cmat, - o, - coeff, - target_code); - break; - case CCD_5345: - target_code = 0xe000; - o = 4; - if(dev->settings.xres<=dev->sensor.optical_res/2) - { - o = o - dev->sensor.dummy_pixel; - } - compute_coefficients (dev, - shading_data, - pixels_per_line, - 3, - cmat, - o, - coeff, - target_code); - break; - case CCD_HP3670: - case CCD_HP2400: - target_code = 0xe000; - /* offset is cksel dependent, but we can't use this in common code */ - if(dev->settings.xres<=300) - { - o = -10; /* OK for <=300 */ - } - else if(dev->settings.xres<=600) - { - o = -6; /* ok at 600 */ - } - else - { - o = +2; - } - compute_coefficients (dev, - shading_data, - pixels_per_line, - 3, - cmat, - o, - coeff, - target_code); - break; - case CCD_KVSS080: - case CCD_PLUSTEK3800: - case CCD_G4050: - case CCD_CS4400F: - case CCD_CS8400F: - target_code = 0xe000; - o = 0; - compute_coefficients (dev, - shading_data, - pixels_per_line, - 3, - cmat, - o, - coeff, - target_code); - break; - case CIS_CANONLIDE700: - case CIS_CANONLIDE100: - case CIS_CANONLIDE200: - case CIS_CANONLIDE110: - case CIS_CANONLIDE120: - case CIS_CANONLIDE210: - case CIS_CANONLIDE220: - /* TODO store this in a data struct so we avoid - * growing this switch */ - if(dev->model->ccd_type!=CIS_CANONLIDE110 - && dev->model->ccd_type!=CIS_CANONLIDE210 - && dev->model->ccd_type!=CIS_CANONLIDE120 - && dev->model->ccd_type!=CIS_CANONLIDE220) - target_code=0xdc00; - else - target_code=0xf000; - words_per_color=pixels_per_line*2; - length = words_per_color * 3 * 2; - free(shading_data); - shading_data = malloc (length); - if (!shading_data) - { - DBG (DBG_error, "%s: failed to allocate memory\n", __func__); - return SANE_STATUS_NO_MEM; - } - memset (shading_data, 0, length); - compute_planar_coefficients (dev, - shading_data, - 1, - pixels_per_line, - words_per_color, - channels, - cmat, - 0, - coeff, - target_code); - break; - case CCD_CANONLIDE35: - compute_averaged_planar (dev, - shading_data, - pixels_per_line, - words_per_color, - channels, - 4, - coeff, - 0xe000, - 0x0a00); - break; - case CIS_CANONLIDE80: - compute_averaged_planar (dev, - shading_data, - pixels_per_line, - words_per_color, - channels, - 0, - coeff, - 0xd000, - 0x0800); - break; - case CCD_PLUSTEK_3600: - compute_shifted_coefficients (dev, - shading_data, - pixels_per_line, - channels, - cmat, - 12, /* offset */ - coeff, - 0x0001, /* target_dark */ - 0xf900, /* target_bright */ - 256); /* patch_size: contigous extent */ - break; - default: - DBG (DBG_error, "%s: sensor %d not supported\n", __func__, dev->model->ccd_type); - return SANE_STATUS_UNSUPPORTED; - break; - } - - /* do the actual write of shading calibration data to the scanner */ - status = genesys_send_offset_and_shading (dev, shading_data, length); - free (shading_data); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "%s: failed to send shading data: %s\n", __func__, - sane_strstatus (status)); - } - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - - -/** - * search calibration cache list for an entry matching required scan. - * If one is found, set device calibration with it - * @param dev scanner's device - * @return SANE_STATUS_UNSUPPORTED if no matching cache entry has been - * found, SANE_STATUS_GOOD if one has been found and used. - */ -static SANE_Status -genesys_restore_calibration (Genesys_Device * dev) -{ - SANE_Status status; - Genesys_Calibration_Cache *cache; - - DBGSTART; - - /* if no cache or no function to evaluate cache entry ther can be no match */ - if (!dev->model->cmd_set->is_compatible_calibration - || dev->calibration_cache == NULL) - return SANE_STATUS_UNSUPPORTED; - - /* we walk the link list of calibration cache in search for a - * matching one */ - for (cache = dev->calibration_cache; cache; cache = cache->next) - { - status = dev->model->cmd_set->is_compatible_calibration (dev, cache, SANE_FALSE); - /* SANE_STATUS_GOOD, a matching cache has been found - * so we use it to populate calibration data - */ - if (status == SANE_STATUS_GOOD) - { - memcpy (&dev->frontend, &cache->frontend, sizeof (dev->frontend)); - /* we don't restore the gamma fields */ - memcpy (dev->sensor.regs_0x10_0x1d, cache->sensor.regs_0x10_0x1d, 6); - free (dev->dark_average_data); - free (dev->white_average_data); - - dev->average_size = cache->average_size; - dev->calib_pixels = cache->calib_pixels; - dev->calib_channels = cache->calib_channels; - - dev->dark_average_data = (uint8_t *) malloc (cache->average_size); - dev->white_average_data = (uint8_t *) malloc (cache->average_size); - - if (!dev->dark_average_data || !dev->white_average_data) - return SANE_STATUS_NO_MEM; - - memcpy (dev->dark_average_data, - cache->dark_average_data, dev->average_size); - memcpy (dev->white_average_data, - cache->white_average_data, dev->average_size); - - - if(dev->model->cmd_set->send_shading_data==NULL) - { - status = genesys_send_shading_coefficient (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_restore_calibration: failed to send shading calibration coefficients: %s\n", - sane_strstatus (status)); - return status; - } - } - - DBG (DBG_proc, "genesys_restore_calibration: restored\n"); - return SANE_STATUS_GOOD; - } - - /* here status is either SANE_STATUS_UNSUPPORTED which mean tested cache - * entry doesn't match, or an fatal error */ - if (status != SANE_STATUS_UNSUPPORTED) - { - DBG (DBG_error, - "genesys_restore_calibration: fail while checking compatibility: %s\n", - sane_strstatus (status)); - return status; - } - } - DBG (DBG_proc, "genesys_restore_calibration: completed(nothing found)\n"); - return SANE_STATUS_UNSUPPORTED; -} - - -static SANE_Status -genesys_save_calibration (Genesys_Device * dev) -{ - SANE_Status status = SANE_STATUS_UNSUPPORTED; - Genesys_Calibration_Cache *cache = NULL; -#ifdef HAVE_SYS_TIME_H - struct timeval time; -#endif - - DBGSTART; - - if (!dev->model->cmd_set->is_compatible_calibration) - return SANE_STATUS_UNSUPPORTED; - - if (dev->calibration_cache != NULL) - { - for (cache = dev->calibration_cache; cache; cache = cache->next) - { - status = dev->model->cmd_set->is_compatible_calibration (dev, cache, SANE_TRUE); - if (status == SANE_STATUS_UNSUPPORTED) - { - continue; - } - else if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_save_calibration: fail while checking compatibility: %s\n", - sane_strstatus (status)); - return status; - } - break; - } - } - - /* if we found on overridable cache, we reuse it */ - if (cache) - { - free(cache->dark_average_data); - free(cache->white_average_data); - } - else - { - /* create a new cache entry and insert it in the linked list */ - cache = malloc (sizeof (Genesys_Calibration_Cache)); - if (!cache) - return SANE_STATUS_NO_MEM; - - memset (cache, 0, sizeof (Genesys_Calibration_Cache)); - - cache->next = dev->calibration_cache; - dev->calibration_cache = cache; - } - - cache->average_size = dev->average_size; - - cache->dark_average_data = (uint8_t *) malloc (cache->average_size); - if (!cache->dark_average_data) - return SANE_STATUS_NO_MEM; - cache->white_average_data = (uint8_t *) malloc (cache->average_size); - if (!cache->white_average_data) - return SANE_STATUS_NO_MEM; - - memcpy (&cache->used_setup, &dev->current_setup, sizeof (cache->used_setup)); - memcpy (&cache->frontend, &dev->frontend, sizeof (cache->frontend)); - memcpy (&cache->sensor, &dev->sensor, sizeof (cache->sensor)); - - cache->calib_pixels = dev->calib_pixels; - cache->calib_channels = dev->calib_channels; - memcpy (cache->dark_average_data, dev->dark_average_data, cache->average_size); - memcpy (cache->white_average_data, dev->white_average_data, cache->average_size); -#ifdef HAVE_SYS_TIME_H - gettimeofday(&time,NULL); - cache->last_calibration = time.tv_sec; -#endif - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -/** - * does the calibration process for a flatbed scanner - * - offset calibration - * - gain calibration - * - shading calibration - * @param dev device to calibrate - * @return SANE_STATUS_GOOD if everything when all right, else the error code. - */ -GENESYS_STATIC SANE_Status -genesys_flatbed_calibration (Genesys_Device * dev) -{ - SANE_Status status; - uint32_t pixels_per_line; - int yres; - - DBG (DBG_info, "genesys_flatbed_calibration\n"); - - yres = dev->sensor.optical_res; - if (dev->settings.yres <= dev->sensor.optical_res / 2) - yres /= 2; - - /* do offset calibration if needed */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) - { - status = dev->model->cmd_set->offset_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: offset calibration failed: %s\n", - sane_strstatus (status)); - return status; - } - - /* since all the registers are set up correctly, just use them */ - status = dev->model->cmd_set->coarse_gain_calibration (dev, yres); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: coarse gain calibration: %s\n", - sane_strstatus (status)); - return status; - } - - } - else - /* since we have 2 gain calibration proc, skip second if first one was - used. */ - { - status = dev->model->cmd_set->init_regs_for_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to send calibration registers: %s\n", - sane_strstatus (status)); - return status; - } - - status = genesys_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to do coarse gain calibration: %s\n", - sane_strstatus (status)); - return status; - } - - } - - if (dev->model->is_cis) - { - /* the afe now sends valid data for doing led calibration */ - status = dev->model->cmd_set->led_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: led calibration failed: %s\n", - sane_strstatus (status)); - return status; - } - - /* calibrate afe again to match new exposure */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) - { - status = dev->model->cmd_set->offset_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: offset calibration failed: %s\n", - sane_strstatus (status)); - return status; - } - - /* since all the registers are set up correctly, just use them */ - - status = dev->model->cmd_set->coarse_gain_calibration (dev, yres); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: coarse gain calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - else - /* since we have 2 gain calibration proc, skip second if first one was - used. */ - { - status = - dev->model->cmd_set->init_regs_for_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to send calibration registers: %s\n", - sane_strstatus (status)); - return status; - } - - status = genesys_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to do static calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - } - - /* we always use sensor pixel number when the ASIC can't handle multi-segments sensor */ - if (!(dev->model->flags & GENESYS_FLAG_SIS_SENSOR)) - { - pixels_per_line = (SANE_UNFIX (dev->model->x_size) * dev->settings.xres) / MM_PER_INCH; - } - else - { - pixels_per_line = dev->sensor.sensor_pixels; - } - - /* send default shading data */ - status = sanei_genesys_init_shading_data (dev, pixels_per_line); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to init shading process: %s\n", - sane_strstatus (status)); - return status; - } - - /* shading calibration */ - status = dev->model->cmd_set->init_regs_for_shading (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "genesys_flatbed_calibration: failed to send shading " - "registers: %s\n", sane_strstatus (status)); - return status; - } - - if (dev->model->flags & GENESYS_FLAG_DARK_WHITE_CALIBRATION) - { - status = genesys_dark_white_shading_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to do dark+white shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - else - { - if (dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION) - { - status = genesys_dark_shading_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to do dark shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - - status = genesys_white_shading_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to do white shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - - if(dev->model->cmd_set->send_shading_data==NULL) - { - status = genesys_send_shading_coefficient (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_flatbed_calibration: failed to send shading calibration coefficients: %s\n", - sane_strstatus (status)); - return status; - } - } - - DBG (DBG_info, "genesys_flatbed_calibration: completed\n"); - - return SANE_STATUS_GOOD; -} - -/** - * Does the calibration process for a sheetfed scanner - * - offset calibration - * - gain calibration - * - shading calibration - * During calibration a predefined calibration sheet with specific black and white - * areas is used. - * @param dev device to calibrate - * @return SANE_STATUS_GOOD if everything when all right, else the error code. - */ -static SANE_Status -genesys_sheetfed_calibration (Genesys_Device * dev) -{ - SANE_Status status = SANE_STATUS_GOOD; - SANE_Bool forward = SANE_TRUE; - int xres; - - DBGSTART; - if (dev->model->cmd_set->search_strip == NULL) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: no strip searching function available\n"); - return SANE_STATUS_UNSUPPORTED; - } - - /* first step, load document */ - status = dev->model->cmd_set->load_document (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to load document: %s\n", - sane_strstatus (status)); - return status; - } - - - DBG (DBG_info, "genesys_sheetfed_calibration\n"); - - /* led, offset and gain calibration are influenced by scan - * settings. So we set it to sensor resolution */ - xres = dev->sensor.optical_res; - dev->settings.xres = dev->sensor.optical_res; - /* XP200 needs to calibrate a full and half sensor's resolution */ - if (dev->model->ccd_type == CIS_XP200 - && dev->settings.xres <= dev->sensor.optical_res / 2) - dev->settings.xres /= 2; - - /* the afe needs to sends valid data even before calibration */ - - /* go to a white area */ - status = dev->model->cmd_set->search_strip (dev, forward, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to find white strip: %s\n", - sane_strstatus (status)); - dev->model->cmd_set->eject_document (dev); - return status; - } - - if (dev->model->is_cis) - { - status = dev->model->cmd_set->led_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: led calibration failed: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* calibrate afe */ - if (dev->model->flags & GENESYS_FLAG_OFFSET_CALIBRATION) - { - status = dev->model->cmd_set->offset_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: offset calibration failed: %s\n", - sane_strstatus (status)); - return status; - } - - /* since all the registers are set up correctly, just use them */ - - status = dev->model->cmd_set->coarse_gain_calibration (dev, xres); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: coarse gain calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - else - /* since we have 2 gain calibration proc, skip second if first one was - used. */ - { - status = - dev->model->cmd_set->init_regs_for_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to send calibration registers: %s\n", - sane_strstatus (status)); - return status; - } - - status = genesys_coarse_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to do static calibration: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* 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 */ - status = dev->model->cmd_set->search_strip (dev, forward, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to find black strip: %s\n", - sane_strstatus (status)); - dev->model->cmd_set->eject_document (dev); - return status; - } - - status = dev->model->cmd_set->init_regs_for_shading (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to do set up registers for shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - status = genesys_dark_shading_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - dev->model->cmd_set->eject_document (dev); - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to do dark shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - forward = SANE_FALSE; - } - - - /* go to a white area */ - status = dev->model->cmd_set->search_strip (dev, forward, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to find white strip: %s\n", - sane_strstatus (status)); - dev->model->cmd_set->eject_document (dev); - return status; - } - - status = dev->model->cmd_set->init_regs_for_shading (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to do set up registers for shading calibration: %s\n", - sane_strstatus (status)); - return status; - } - status = genesys_white_shading_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - dev->model->cmd_set->eject_document (dev); - DBG (DBG_error, "%s: failed eject target: %s\n", __func__, - sane_strstatus (status)); - return status; - } - - /* in case we haven't black shading data, build it from black pixels - * of white calibration */ - if (!(dev->model->flags & GENESYS_FLAG_DARK_CALIBRATION)) - { - FREE_IFNOT_NULL (dev->dark_average_data); - dev->dark_average_data = malloc (dev->average_size); - memset (dev->dark_average_data, 0x0f, dev->average_size); - /* 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 - (dev->sensor.sensor_pixels * dev->settings.xres) / dev->sensor.optical_res, - dev->calib_lines, - */ - } - - /* send the shading coefficient when doing whole line shading - * but not when using SHDAREA like GL124 */ - if(dev->model->cmd_set->send_shading_data==NULL) - { - status = genesys_send_shading_coefficient (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to send shading calibration coefficients: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* save the calibration data */ - genesys_save_calibration (dev); - - /* and finally eject calibration sheet */ - status = dev->model->cmd_set->eject_document (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_sheetfed_calibration: failed to eject document: %s\n", - sane_strstatus (status)); - return status; - } - - /* resotre settings */ - dev->settings.xres = xres; - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -/** - * does the calibration process for a device - * @param dev device to calibrate - */ -static SANE_Status -genesys_scanner_calibration (Genesys_Device * dev) -{ - if (dev->model->is_sheetfed == SANE_FALSE) - { - return genesys_flatbed_calibration (dev); - } - return genesys_sheetfed_calibration (dev); -} - -/* unused function kept in case it may be usefull in the futur */ -#if 0 -static SANE_Status -genesys_wait_not_moving (Genesys_Device * dev, int mseconds) -{ - uint8_t value; - SANE_Status status; - - DBG (DBG_proc, - "genesys_wait_not_moving: waiting %d mseconds for motor to stop\n", - mseconds); - while (mseconds > 0) - { - RIE (sanei_genesys_get_status (dev, &value)); - - if (dev->model->cmd_set->test_motor_flag_bit (value)) - { - usleep (100 * 1000); - mseconds -= 100; - DBG (DBG_io, - "genesys_wait_not_moving: motor is moving, %d mseconds to go\n", - mseconds); - } - else - { - DBG (DBG_info, - "genesys_wait_not_moving: motor is not moving, exiting\n"); - return SANE_STATUS_GOOD; - } - - } - DBG (DBG_error, - "genesys_wait_not_moving: motor is still moving, timeout exceeded\n"); - return SANE_STATUS_DEVICE_BUSY; -} -#endif - - -/* ------------------------------------------------------------------------ */ -/* High level (exported) functions */ -/* ------------------------------------------------------------------------ */ - -/* - * wait lamp to be warm enough by scanning the same line until - * differences between two scans are below a threshold - */ -static SANE_Status -genesys_warmup_lamp (Genesys_Device * dev) -{ - uint8_t *first_line, *second_line; - int seconds = 0; - int pixel; - int channels, total_size; - double first_average = 0; - double second_average = 0; - int difference = 255; - int empty, lines = 3; - SANE_Status status = SANE_STATUS_IO_ERROR; - - DBGSTART; - - /* check if the current chipset implements warmup */ - if(dev->model->cmd_set->init_regs_for_warmup==NULL) - { - DBG (DBG_error, "%s: init_regs_for_warmup not implemented\n", __func__); - return status; - } - - dev->model->cmd_set->init_regs_for_warmup (dev, dev->reg, &channels, &total_size); - first_line = malloc (total_size); - if (!first_line) - return SANE_STATUS_NO_MEM; - - second_line = malloc (total_size); - if (!second_line) - { - free(first_line); - DBGCOMPLETED; - return SANE_STATUS_NO_MEM; - } - - do - { - DBG (DBG_info, "genesys_warmup_lamp: one more loop\n"); - RIEF2 (dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_FALSE), first_line, second_line); - do - { - sanei_genesys_test_buffer_empty (dev, &empty); - } - while (empty); - - status = sanei_genesys_read_data_from_scanner (dev, first_line, total_size); - if (status != SANE_STATUS_GOOD) - { - RIEF2 (sanei_genesys_read_data_from_scanner - (dev, first_line, total_size), first_line, second_line); - } - - RIEF2 (dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE), first_line, second_line); - - sleep (1); /* sleep 1 s */ - seconds++; - - RIEF2 (dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_FALSE), first_line, second_line); - do - { - sanei_genesys_test_buffer_empty (dev, &empty); - usleep (100 * 1000); - } - while (empty); - RIEF2 (sanei_genesys_read_data_from_scanner (dev, second_line, total_size), first_line, second_line); - RIEF2 (dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE), first_line, second_line); - - /* compute difference between the two scans */ - for (pixel = 0; pixel < total_size; pixel++) - { - /* 16 bit data */ - if (dev->model->cmd_set->get_bitset_bit (dev->reg)) - { - 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->model->cmd_set->get_bitset_bit (dev->reg)) - { - first_average /= pixel; - second_average /= pixel; - difference = fabs (first_average - second_average); - DBG (DBG_info, - "genesys_warmup_lamp: average = %.2f, diff = %.3f\n", - 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 ("warmup1.pnm", first_line, 8, - channels, - total_size / (lines * channels), - lines); - sanei_genesys_write_pnm_file ("warmup2.pnm", second_line, 8, - channels, - total_size / (lines * channels), - lines); - } - DBG (DBG_info, "genesys_warmup_lamp: average 1 = %.2f, average 2 = %.2f\n", 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; - } - - /* sleep another second before next loop */ - sleep (1); - seconds++; - } - while (seconds < WARMUP_TIME); - - if (seconds >= WARMUP_TIME) - { - DBG (DBG_error, - "genesys_warmup_lamp: warmup timed out after %d seconds. Lamp defective?\n", - seconds); - status = SANE_STATUS_IO_ERROR; - } - else - { - DBG (DBG_info, - "genesys_warmup_lamp: warmup succeeded after %d seconds\n", - seconds); - } - - free (first_line); - free (second_line); - - DBGCOMPLETED; - - return status; -} - - -/* High-level start of scanning */ -static SANE_Status -genesys_start_scan (Genesys_Device * dev, SANE_Bool lamp_off) -{ - SANE_Status status; - unsigned int steps, expected; - SANE_Bool empty; - - DBGSTART; - - /* 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 == SANE_TRUE) - { - status = sanei_genesys_wait_for_home (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to wait for head to park: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* disable power saving*/ - status = dev->model->cmd_set->save_power (dev, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to disable power saving mode: %s\n", - sane_strstatus (status)); - return status; - } - - /* 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 == SCAN_METHOD_FLATBED)) - { - RIE (genesys_warmup_lamp (dev)); - } - - /* set top left x and y values by scanning the internals if flatbed scanners */ - if (dev->model->is_sheetfed == SANE_FALSE) - { - /* do the geometry detection only once */ - if ((dev->model->flags & GENESYS_FLAG_SEARCH_START) - && (dev->model->y_offset_calib == 0)) - { - status = dev->model->cmd_set->search_start_position (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to search start position: %s\n", - sane_strstatus (status)); - return status; - } - - dev->parking = SANE_FALSE; - status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to move scanhead to " - "home position: %s\n", sane_strstatus (status)); - return status; - } - dev->scanhead_position_in_steps = 0; - } - else - { - /* Go home */ - /* TODO: check we can drop this since we cannot have the - scanner's head wandering here */ - dev->parking = SANE_FALSE; - status = dev->model->cmd_set->slow_back_home (dev, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to move scanhead to " - "home position: %s\n", sane_strstatus (status)); - return status; - } - dev->scanhead_position_in_steps = 0; - } - } - - /* move to calibration area for transparency adapter */ - if ((dev->settings.scan_method == SCAN_METHOD_TRANSPARENCY) - && dev->model->cmd_set->move_to_ta != NULL) - { - status=dev->model->cmd_set->move_to_ta(dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to move to start of transparency adapter: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* load document if needed (for sheetfed scanner for instance) */ - if (dev->model->is_sheetfed == SANE_TRUE - && dev->model->cmd_set->load_document != NULL) - { - status = dev->model->cmd_set->load_document (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to load document: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* send gamma tables. They have been set to device or user value - * when setting option value */ - status = dev->model->cmd_set->send_gamma_table (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to init gamma table: %s\n", - sane_strstatus (status)); - return status; - } - - /* try to use cached calibration first */ - status = genesys_restore_calibration (dev); - if (status == SANE_STATUS_UNSUPPORTED) - { - /* 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 == SANE_FALSE) - { - status = genesys_scanner_calibration (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to do scanner calibration: %s\n", - sane_strstatus (status)); - return status; - } - - genesys_save_calibration (dev); - } - else - { - DBG (DBG_warn, "genesys_start_scan: no calibration done\n"); - } - } - else if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to restore calibration: %s\n", - sane_strstatus (status)); - return status; - } - - /* build look up table for dynamic lineart */ - if(dev->settings.dynamic_lineart==SANE_TRUE) - { - status = sanei_genesys_load_lut(dev->lineart_lut, 8, 8, 50, 205, - dev->settings.threshold_curve, - dev->settings.threshold-127); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "genesys_start_scan: failed to build lut\n"); - return status; - } - } - - status = dev->model->cmd_set->init_regs_for_scan (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to do init registers for scan: %s\n", - sane_strstatus (status)); - return status; - } - - /* no lamp during scan */ - if(lamp_off == SANE_TRUE) - { - dev->model->cmd_set->set_lamp_power (dev, dev->reg, SANE_FALSE); - } - - /* GL124 is using SHDAREA, so we have to wait for scan to be set up before - * sending shading data */ - if( (dev->model->cmd_set->send_shading_data!=NULL) - && !(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION)) - { - status = genesys_send_shading_coefficient (dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to send shading calibration coefficients: %s\n", - sane_strstatus (status)); - return status; - } - } - - /* now send registers for scan */ - status = - dev->model->cmd_set->bulk_write_register (dev, dev->reg, - dev->model-> - cmd_set->bulk_full_size ()); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to bulk write registers, status = %d\n", - status); - return status; - } - - /* start effective scan */ - status = dev->model->cmd_set->begin_scan (dev, dev->reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to begin scan: %s\n", - sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - /*do we really need this? the valid data check should be sufficent -- pierre*/ - /* waits for head to reach scanning position */ - expected = sanei_genesys_read_reg_from_set (dev->reg, 0x3d) * 65536 - + sanei_genesys_read_reg_from_set (dev->reg, 0x3e) * 256 - + sanei_genesys_read_reg_from_set (dev->reg, 0x3f); - do - { - /* wait 1/10th of second between each test to avoid - overloading USB and CPU */ - usleep (100 * 1000); - status = sanei_genesys_read_feed_steps (dev, &steps); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: Failed to read feed steps: %s\n", - sane_strstatus (status)); - return status; - } - } - while (steps < expected); - - /* wait for buffers to be filled */ - do - { - RIE (sanei_genesys_test_buffer_empty (dev, &empty)); - } - while (empty); - - /* when doing one or two-table movement, let the motor settle to scanning speed */ - /* and scanning start before reading data */ -/* the valid data check already waits until the scanner delivers data. this here leads to unnecessary buffer full conditions in the scanner. - if (dev->model->cmd_set->get_fast_feed_bit (dev->reg)) - usleep (1000 * 1000); - else - usleep (500 * 1000); -*/ - /* then we wait for at least one word of valid scan data - - this is also done in sanei_genesys_read_data_from_scanner -- pierre */ - if (dev->model->is_sheetfed == SANE_FALSE) - { - do - { - usleep (100 * 1000); - status = sanei_genesys_read_valid_words (dev, &steps); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_start_scan: failed to read valid words: %s\n", - sane_strstatus (status)); - return status; - } - } - while (steps < 1); - } - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -/* this is _not_ a ringbuffer. - if we need a block which does not fit at the end of our available data, - we move the available data to the beginning. - */ - -SANE_Status -sanei_genesys_buffer_alloc (Genesys_Buffer * buf, size_t size) -{ - buf->buffer = (SANE_Byte *) malloc (size); - if (!buf->buffer) - return SANE_STATUS_NO_MEM; - buf->avail = 0; - buf->pos = 0; - buf->size = size; - return SANE_STATUS_GOOD; -} - -SANE_Status -sanei_genesys_buffer_free (Genesys_Buffer * buf) -{ - SANE_Byte *tmp = buf->buffer; - buf->avail = 0; - buf->size = 0; - buf->pos = 0; - buf->buffer = NULL; - if (tmp) - free (tmp); - return SANE_STATUS_GOOD; -} - -SANE_Byte * -sanei_genesys_buffer_get_write_pos (Genesys_Buffer * buf, size_t size) -{ - if (buf->avail + size > buf->size) - return NULL; - if (buf->pos + buf->avail + size > buf->size) - { - memmove (buf->buffer, buf->buffer + buf->pos, buf->avail); - buf->pos = 0; - } - return buf->buffer + buf->pos + buf->avail; -} - -SANE_Byte * -sanei_genesys_buffer_get_read_pos (Genesys_Buffer * buf) -{ - return buf->buffer + buf->pos; -} - -SANE_Status -sanei_genesys_buffer_produce (Genesys_Buffer * buf, size_t size) -{ - if (size > buf->size - buf->avail) - return SANE_STATUS_INVAL; - buf->avail += size; - return SANE_STATUS_GOOD; -} - -SANE_Status -sanei_genesys_buffer_consume (Genesys_Buffer * buf, size_t size) -{ - if (size > buf->avail) - return SANE_STATUS_INVAL; - buf->avail -= size; - buf->pos += size; - return SANE_STATUS_GOOD; -} - - -#include "genesys_conv.c" - -static SANE_Status accurate_line_read(Genesys_Device * dev, - SANE_Byte *buffer, - size_t size) -{ - SANE_Status status; - status = dev->model->cmd_set->bulk_read_data (dev, 0x45, buffer, size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "accurate_line_read: failed to read %lu bytes (%s)\n", - (u_long) size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - /* done reading */ - dev->oe_buffer.avail = size; - dev->oe_buffer.pos = 0; - return status; -} - -/** @brief fill buffer while reducing vertical resolution - * This function fills a read buffer with scanned data from a sensor - * which puts odd and even pixels in 2 different data segment. So a complete - * must be read and bytes interleaved to get usable by the other stages - * of the backend - */ -static SANE_Status -genesys_fill_line_interp_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size) -{ - size_t count; - SANE_Status status; - - /* fill buffer if needed */ - if (dev->oe_buffer.avail == 0) - { - status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "%s: failed to read %lu bytes (%s)\n", __func__, - (u_long) dev->oe_buffer.size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - } - - /* copy size bytes of data, copying from a line when line count matches */ - count = 0; - while (count < size) - { - /* line counter */ - /* dev->line_interp holds the number of lines scanned for one line of data sent */ - if(((dev->line_count/dev->current_setup.channels) % dev->line_interp)==0) - { - /* copy pixel when line matches */ - work_buffer_dst[count] = dev->oe_buffer.buffer[dev->cur + dev->oe_buffer.pos]; - count++; - } - - /* always update pointer so we skip uncopied data */ - dev->cur++; - - /* go to next line if needed */ - if (dev->cur == dev->len) - { - dev->oe_buffer.pos += dev->bpl; - dev->cur = 0; - dev->line_count++; - } - - /* read a new buffer if needed */ - if (dev->oe_buffer.pos >= dev->oe_buffer.avail) - { - status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "%s: failed to read %lu bytes (%s)\n", __func__, - (u_long) dev->oe_buffer.size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - } - } - - return SANE_STATUS_GOOD; -} - -/** @brief fill buffer for segmented sensors - * This function fills a read buffer with scanned data from a sensor segmented - * in several parts (multi-lines sensors). Data of the same valid area is read - * back to back and must be interleaved to get usable by the other stages - * of the backend - */ -static SANE_Status -genesys_fill_segmented_buffer (Genesys_Device * dev, uint8_t *work_buffer_dst, size_t size) -{ - size_t count; - SANE_Status status; - int depth,i,n,k; - - depth = dev->settings.depth; - if (dev->settings.scan_mode == SCAN_MODE_LINEART && dev->settings.dynamic_lineart==SANE_FALSE) - depth = 1; - - /* fill buffer if needed */ - if (dev->oe_buffer.avail == 0) - { - status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "%s: failed to read %lu bytes (%s)\n", __func__, - (u_long) dev->oe_buffer.size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - } - - /* copy size bytes of data, copying from a subwindow of each line - * when last line of buffer is exhausted, read another one */ - count = 0; - while (count < size) - { - if(dev->settings.double_xres==SANE_TRUE) - { - /* copy only even pixel */ - work_buffer_dst[count] = dev->oe_buffer.buffer[dev->cur + dev->oe_buffer.pos]; - /* update counter and pointer */ - count++; - dev->cur++; - } - else - { - if(depth==1) - { - while (dev->cur < dev->len && count < size) - { - for(n=0;n<dev->segnb;n++) - { - work_buffer_dst[count+n] = 0; - } - /* interleaving is at bit level */ - for(i=0;i<8;i++) - { - k=count+(i*dev->segnb)/8; - for(n=0;n<dev->segnb;n++) - { - work_buffer_dst[k] = work_buffer_dst[k] << 1; - if((dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos])&(128>>i)) - { - work_buffer_dst[k] |= 1; - } - } - } - - /* update counter and pointer */ - count += dev->segnb; - dev->cur++; - } - } - if(depth==8) - { - while (dev->cur < dev->len && count < size) - { - for(n=0;n<dev->segnb;n++) - { - work_buffer_dst[count+n] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos]; - } - /* update counter and pointer */ - count += dev->segnb; - dev->cur++; - } - } - if(depth==16) - { - while (dev->cur < dev->len && count < size) - { - for(n=0;n<dev->segnb;n++) - { - work_buffer_dst[count+n*2] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos]; - work_buffer_dst[count+n*2+1] = dev->oe_buffer.buffer[dev->cur + dev->skip + dev->dist*dev->order[n] + dev->oe_buffer.pos+1]; - } - /* update counter and pointer */ - count += dev->segnb*2; - dev->cur+=2; - } - } - } - - /* go to next line if needed */ - if (dev->cur == dev->len) - { - dev->oe_buffer.pos += dev->bpl; - dev->cur = 0; - } - - /* read a new buffer if needed */ - if (dev->oe_buffer.pos >= dev->oe_buffer.avail) - { - status = accurate_line_read(dev,dev->oe_buffer.buffer,dev->oe_buffer.size); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "%s: failed to read %lu bytes (%s)\n", __func__, - (u_long) dev->oe_buffer.size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - } - } - - return SANE_STATUS_GOOD; -} - -/** - * - */ -static SANE_Status -genesys_fill_read_buffer (Genesys_Device * dev) -{ - size_t size; - size_t space; - SANE_Status status; - uint8_t *work_buffer_dst; - - DBGSTART; - - /* for sheetfed scanner, we must check is document is shorter than - * the requested scan */ - if (dev->model->is_sheetfed == SANE_TRUE) - { - status = dev->model->cmd_set->detect_document_end (dev); - if (status != SANE_STATUS_GOOD) - return status; - } - - space = dev->read_buffer.size - dev->read_buffer.avail; - - work_buffer_dst = sanei_genesys_buffer_get_write_pos (&(dev->read_buffer), - space); - - size = space; - - /* never read an odd number. exception: last read - the chip internal counter does not count half words. */ - size &= ~1; - /* Some setups need the reads to be multiples of 256 bytes */ - size &= ~0xff; - - if (dev->read_bytes_left < size) - { - size = dev->read_bytes_left; - /*round up to a multiple of 256 bytes */ - size += (size & 0xff) ? 0x100 : 0x00; - size &= ~0xff; - } - - /* early out if our remaining buffer capacity is too low */ - if (size == 0) - return SANE_STATUS_GOOD; - - DBG (DBG_io, "genesys_fill_read_buffer: reading %lu bytes\n", - (u_long) size); - - /* size is already maxed to our needs. for most models bulk_read_data - will read as much data as requested. */ - - /* 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. - */ - if (dev->line_interp>0) - { - /* line interpolation */ - status = genesys_fill_line_interp_buffer (dev, work_buffer_dst, size); - } - else if (dev->segnb>1) - { - /* multi-segment sensors processing */ - status = genesys_fill_segmented_buffer (dev, work_buffer_dst, size); - } - else /* regular case with no extra copy */ - { - status = dev->model->cmd_set->bulk_read_data (dev, 0x45, work_buffer_dst, size); - } - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_fill_read_buffer: failed to read %lu bytes (%s)\n", - (u_long) size, sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - if (size > dev->read_bytes_left) - size = dev->read_bytes_left; - - dev->read_bytes_left -= size; - - RIE (sanei_genesys_buffer_produce (&(dev->read_buffer), size)); - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -/* 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. - */ -static SANE_Status -genesys_read_ordered_data (Genesys_Device * dev, SANE_Byte * destination, - size_t * len) -{ - SANE_Status status; - size_t bytes, extra; - unsigned int channels, depth, src_pixels; - unsigned int ccd_shift[12], shift_count; - uint8_t *work_buffer_src; - uint8_t *work_buffer_dst; - unsigned int dst_lines; - unsigned int step_1_mode; - unsigned int needs_reorder; - unsigned int needs_ccd; - unsigned int needs_shrink; - unsigned int needs_reverse; - Genesys_Buffer *src_buffer; - Genesys_Buffer *dst_buffer; - - DBGSTART; - if (dev->read_active != SANE_TRUE) - { - DBG (DBG_error, "genesys_read_ordered_data: read not active!\n"); - *len = 0; - return SANE_STATUS_INVAL; - } - - - DBG (DBG_info, "genesys_read_ordered_data: dumping current_setup:\n" - "\tpixels: %d\n" - "\tlines: %d\n" - "\tdepth: %d\n" - "\tchannels: %d\n" - "\texposure_time: %d\n" - "\txres: %g\n" - "\tyres: %g\n" - "\thalf_ccd: %s\n" - "\tstagger: %d\n" - "\tmax_shift: %d\n", - dev->current_setup.pixels, - dev->current_setup.lines, - dev->current_setup.depth, - dev->current_setup.channels, - dev->current_setup.exposure_time, - dev->current_setup.xres, - dev->current_setup.yres, - dev->current_setup.half_ccd ? "yes" : "no", - dev->current_setup.stagger, dev->current_setup.max_shift); - - /* prepare conversion */ - /* current settings */ - channels = dev->current_setup.channels; - depth = dev->current_setup.depth; - - src_pixels = dev->current_setup.pixels; - - needs_reorder = 1; - if (channels != 3 && depth != 16) - needs_reorder = 0; -#ifndef WORDS_BIGENDIAN - if (channels != 3 && depth == 16) - needs_reorder = 0; - if (channels == 3 && depth == 16 && !dev->model->is_cis && - dev->model->line_mode_color_order == COLOR_ORDER_RGB) - needs_reorder = 0; -#endif - if (channels == 3 && depth == 8 && !dev->model->is_cis && - dev->model->line_mode_color_order == COLOR_ORDER_RGB) - needs_reorder = 0; - - needs_ccd = dev->current_setup.max_shift > 0; - needs_shrink = dev->settings.pixels != src_pixels; - needs_reverse = depth == 1; - - DBG (DBG_info, - "genesys_read_ordered_data: using filters:%s%s%s%s\n", - needs_reorder ? " reorder" : "", - needs_ccd ? " ccd" : "", - needs_shrink ? " shrink" : "", - needs_reverse ? " reverse" : ""); - - DBG (DBG_info, - "genesys_read_ordered_data: frontend requested %lu bytes\n", - (u_long) * len); - - DBG (DBG_info, - "genesys_read_ordered_data: bytes_to_read=%lu, total_bytes_read=%lu\n", - (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read); - /* is there data left to scan */ - if (dev->total_bytes_read >= dev->total_bytes_to_read) - { - DBG (DBG_proc, - "genesys_read_ordered_data: nothing more to scan: EOF\n"); - *len = 0; - - /* issue park command immediatly in case scanner can handle it - * so we save time */ - if (dev->model->is_sheetfed == SANE_FALSE - && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) - && dev->parking == SANE_FALSE) - { - dev->model->cmd_set->slow_back_home (dev, SANE_FALSE); - dev->parking = SANE_TRUE; - } - return SANE_STATUS_EOF; - } - - DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by output\n", - ((dev->total_bytes_to_read - dev->total_bytes_read) * 8UL) / - (dev->settings.pixels * channels * depth)); - DBG (DBG_info, "genesys_read_ordered_data: %lu lines left by input\n", - ((dev->read_bytes_left + dev->read_buffer.avail) * 8UL) / - (src_pixels * channels * depth)); - - if (channels == 1) - { - ccd_shift[0] = 0; - ccd_shift[1] = dev->current_setup.stagger; - shift_count = 2; - } - else - { - ccd_shift[0] = - ((dev->ld_shift_r * dev->settings.yres) / - dev->motor.base_ydpi); - ccd_shift[1] = - ((dev->ld_shift_g * dev->settings.yres) / - dev->motor.base_ydpi); - ccd_shift[2] = - ((dev->ld_shift_b * dev->settings.yres) / - dev->motor.base_ydpi); - - ccd_shift[3] = ccd_shift[0] + dev->current_setup.stagger; - ccd_shift[4] = ccd_shift[1] + dev->current_setup.stagger; - ccd_shift[5] = ccd_shift[2] + dev->current_setup.stagger; - - shift_count = 6; - } - - -/* 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. - */ - - status = genesys_fill_read_buffer (dev); - - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_read_ordered_data: genesys_fill_read_buffer failed\n"); - return status; - } - - src_buffer = &(dev->read_buffer); - -/* maybe reorder components/bytes */ - if (needs_reorder) - { -/*not implemented for depth == 1.*/ - if (depth == 1) - { - DBG (DBG_error, "Can't reorder single bit data\n"); - return SANE_STATUS_INVAL; - } - - dst_buffer = &(dev->lines_buffer); - - work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer); - bytes = src_buffer->avail; - -/*how many bytes can be processed here?*/ -/*we are greedy. we work as much as possible*/ - if (bytes > dst_buffer->size - dst_buffer->avail) - bytes = dst_buffer->size - dst_buffer->avail; - - dst_lines = (bytes * 8) / (src_pixels * channels * depth); - bytes = (dst_lines * src_pixels * channels * depth) / 8; - - work_buffer_dst = sanei_genesys_buffer_get_write_pos (dst_buffer, - bytes); - - DBG (DBG_info, "genesys_read_ordered_data: reordering %d lines\n", - dst_lines); - - if (dst_lines != 0) - { - - if (channels == 3) - { - step_1_mode = 0; - - if (depth == 16) - step_1_mode |= 1; - - if (dev->model->is_cis) - step_1_mode |= 2; - - if (dev->model->line_mode_color_order == COLOR_ORDER_BGR) - step_1_mode |= 4; - - switch (step_1_mode) - { - case 1: /* RGB, chunky, 16 bit */ -#ifdef WORDS_BIGENDIAN - status = - genesys_reorder_components_endian_16 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels, 3); - break; -#endif /*WORDS_BIGENDIAN */ - case 0: /* RGB, chunky, 8 bit */ - status = SANE_STATUS_GOOD; - break; - case 2: /* RGB, cis, 8 bit */ - status = - genesys_reorder_components_cis_8 (work_buffer_src, - work_buffer_dst, - dst_lines, src_pixels); - break; - case 3: /* RGB, cis, 16 bit */ - status = - genesys_reorder_components_cis_16 (work_buffer_src, - work_buffer_dst, - dst_lines, src_pixels); - break; - case 4: /* BGR, chunky, 8 bit */ - status = - genesys_reorder_components_bgr_8 (work_buffer_src, - work_buffer_dst, - dst_lines, src_pixels); - break; - case 5: /* BGR, chunky, 16 bit */ - status = - genesys_reorder_components_bgr_16 (work_buffer_src, - work_buffer_dst, - dst_lines, src_pixels); - break; - case 6: /* BGR, cis, 8 bit */ - status = - genesys_reorder_components_cis_bgr_8 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels); - break; - case 7: /* BGR, cis, 16 bit */ - status = - genesys_reorder_components_cis_bgr_16 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels); - break; - } - } - else - { -#ifdef WORDS_BIGENDIAN - if (depth == 16) - { - status = - genesys_reorder_components_endian_16 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels, 1); - } - else - { - status = SANE_STATUS_GOOD; - } -#else /*!WORDS_BIGENDIAN */ - status = SANE_STATUS_GOOD; -#endif /*WORDS_BIGENDIAN */ - } - - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_read_ordered_data: failed to convert byte ordering(%s)\n", - sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - RIE (sanei_genesys_buffer_produce (dst_buffer, bytes)); - - RIE (sanei_genesys_buffer_consume (src_buffer, bytes)); - } - src_buffer = dst_buffer; - } - -/* maybe reverse effects of ccd layout */ - if (needs_ccd) - { -/*should not happen with depth == 1.*/ - if (depth == 1) - { - DBG (DBG_error, "Can't reverse ccd for single bit data\n"); - return SANE_STATUS_INVAL; - } - - dst_buffer = &(dev->shrink_buffer); - - work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer); - bytes = src_buffer->avail; - - extra = - (dev->current_setup.max_shift * src_pixels * channels * depth) / 8; - -/*extra bytes are reserved, and should not be consumed*/ - if (bytes < extra) - bytes = 0; - else - bytes -= extra; - -/*how many bytes can be processed here?*/ -/*we are greedy. we work as much as possible*/ - if (bytes > dst_buffer->size - dst_buffer->avail) - bytes = dst_buffer->size - dst_buffer->avail; - - dst_lines = (bytes * 8) / (src_pixels * channels * depth); - bytes = (dst_lines * src_pixels * channels * depth) / 8; - - work_buffer_dst = - sanei_genesys_buffer_get_write_pos (dst_buffer, bytes); - - DBG (DBG_info, "genesys_read_ordered_data: un-ccd-ing %d lines\n", - dst_lines); - - if (dst_lines != 0) - { - - if (depth == 8) - status = genesys_reverse_ccd_8 (work_buffer_src, work_buffer_dst, - dst_lines, - src_pixels * channels, - ccd_shift, shift_count); - else - status = genesys_reverse_ccd_16 (work_buffer_src, work_buffer_dst, - dst_lines, - src_pixels * channels, - ccd_shift, shift_count); - - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_read_ordered_data: failed to reverse ccd effects(%s)\n", - sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - RIE (sanei_genesys_buffer_produce (dst_buffer, bytes)); - - RIE (sanei_genesys_buffer_consume (src_buffer, bytes)); - } - src_buffer = dst_buffer; - } - -/* maybe shrink(or enlarge) lines */ - if (needs_shrink) - { - - dst_buffer = &(dev->out_buffer); - - work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer); - bytes = src_buffer->avail; - -/*lines in input*/ - dst_lines = (bytes * 8) / (src_pixels * channels * depth); - - /* how many lines can be processed here? */ - /* we are greedy. we work as much as possible */ - bytes = dst_buffer->size - dst_buffer->avail; - - if (dst_lines > (bytes * 8) / (dev->settings.pixels * channels * depth)) - dst_lines = (bytes * 8) / (dev->settings.pixels * channels * depth); - - bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8; - - work_buffer_dst = - sanei_genesys_buffer_get_write_pos (dst_buffer, bytes); - - DBG (DBG_info, "genesys_read_ordered_data: shrinking %d lines\n", - dst_lines); - - if (dst_lines != 0) - { - if (depth == 1) - status = genesys_shrink_lines_1 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels, - dev->settings.pixels, - channels); - else if (depth == 8) - status = genesys_shrink_lines_8 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels, - dev->settings.pixels, channels); - else - status = genesys_shrink_lines_16 (work_buffer_src, - work_buffer_dst, - dst_lines, - src_pixels, - dev->settings.pixels, channels); - - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_read_ordered_data: failed to shrink lines(%s)\n", - sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - - /* we just consumed this many bytes*/ - bytes = (dst_lines * src_pixels * channels * depth) / 8; - RIE (sanei_genesys_buffer_consume (src_buffer, bytes)); - - /* we just created this many bytes*/ - bytes = (dst_lines * dev->settings.pixels * channels * depth) / 8; - RIE (sanei_genesys_buffer_produce (dst_buffer, bytes)); - - } - src_buffer = dst_buffer; - } - - /* move data to destination */ - bytes = src_buffer->avail; - if (bytes > *len) - bytes = *len; - work_buffer_src = sanei_genesys_buffer_get_read_pos (src_buffer); - - if (needs_reverse) - { - status = genesys_reverse_bits (work_buffer_src, destination, bytes); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "genesys_read_ordered_data: failed to reverse bits(%s)\n", - sane_strstatus (status)); - return SANE_STATUS_IO_ERROR; - } - *len = bytes; - } - else - { - memcpy (destination, work_buffer_src, bytes); - *len = bytes; - } - - /* 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->total_bytes_read += *len; - - RIE (sanei_genesys_buffer_consume (src_buffer, bytes)); - - /* end scan if all needed data have been read */ - if(dev->total_bytes_read >= dev->total_bytes_to_read) - { - dev->model->cmd_set->end_scan (dev, dev->reg, SANE_TRUE); - if (dev->model->is_sheetfed == SANE_TRUE) - { - dev->model->cmd_set->eject_document (dev); - } - } - - DBG (DBG_proc, "genesys_read_ordered_data: completed, %lu bytes read\n", - (u_long) bytes); - return SANE_STATUS_GOOD; -} - - - -/* ------------------------------------------------------------------------ */ -/* Start of higher level functions */ -/* ------------------------------------------------------------------------ */ - -static size_t -max_string_size (const SANE_String_Const strings[]) -{ - size_t size, max_size = 0; - SANE_Int i; - - for (i = 0; strings[i]; ++i) - { - size = strlen (strings[i]) + 1; - if (size > max_size) - max_size = size; - } - return max_size; -} - -static SANE_Status -calc_parameters (Genesys_Scanner * s) -{ - SANE_String mode, source, color_filter; - SANE_Status status = SANE_STATUS_GOOD; - SANE_Int depth = 0, resolution = 0; - double tl_x = 0, tl_y = 0, br_x = 0, br_y = 0; - - mode = s->val[OPT_MODE].s; - source = s->val[OPT_SOURCE].s; - color_filter = s->val[OPT_COLOR_FILTER].s; - depth = s->val[OPT_BIT_DEPTH].w; - resolution = s->val[OPT_RESOLUTION].w; - tl_x = SANE_UNFIX (s->val[OPT_TL_X].w); - tl_y = SANE_UNFIX (s->val[OPT_TL_Y].w); - br_x = SANE_UNFIX (s->val[OPT_BR_X].w); - br_y = SANE_UNFIX (s->val[OPT_BR_Y].w); - - s->params.last_frame = SANE_TRUE; /* only single pass scanning supported */ - - if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0 - || strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) - s->params.format = SANE_FRAME_GRAY; - else /* Color */ - s->params.format = SANE_FRAME_RGB; - - if (strcmp (mode, SANE_VALUE_SCAN_MODE_LINEART) == 0) - s->params.depth = 1; - else - s->params.depth = depth; - s->dev->settings.depth = depth; - - /* interpolation */ - s->dev->settings.disable_interpolation = - s->val[OPT_DISABLE_INTERPOLATION].w == SANE_TRUE; - - /* hardware settings */ - if (resolution > s->dev->sensor.optical_res && - s->dev->settings.disable_interpolation) - s->dev->settings.xres = s->dev->sensor.optical_res; - else - s->dev->settings.xres = resolution; - s->dev->settings.yres = resolution; - - s->params.lines = ((br_y - tl_y) * s->dev->settings.yres) / MM_PER_INCH; - s->params.pixels_per_line = - ((br_x - tl_x) * resolution) / 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 == GENESYS_GL847 - || s->dev->model->asic_type == GENESYS_GL124 - || s->dev->model->asic_type == GENESYS_GL845 - || s->dev->model->asic_type == GENESYS_GL846 - || s->dev->model->asic_type == GENESYS_GL843) - { - if (s->dev->settings.xres <= 1200) - s->params.pixels_per_line = (s->params.pixels_per_line/4)*4; - else - s->params.pixels_per_line = (s->params.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 == GENESYS_GL124 - || s->dev->model->asic_type == GENESYS_GL847 - || s->dev->current_setup.xres < s->dev->current_setup.yres - ) - ) - { - s->params.pixels_per_line = (s->params.pixels_per_line/16)*16; - } - - s->params.bytes_per_line = s->params.pixels_per_line; - if (s->params.depth > 8) - { - s->params.depth = 16; - s->params.bytes_per_line *= 2; - } - else if (s->params.depth == 1) - { - s->params.bytes_per_line /= 8; - /* round down pixel number - really? rounding down means loss of at most 7 pixels! -- pierre */ - s->params.pixels_per_line = 8 * s->params.bytes_per_line; - } - - if (s->params.format == SANE_FRAME_RGB) - s->params.bytes_per_line *= 3; - - if (strcmp (mode, SANE_VALUE_SCAN_MODE_COLOR) == 0) - s->dev->settings.scan_mode = SCAN_MODE_COLOR; - else if (strcmp (mode, SANE_VALUE_SCAN_MODE_GRAY) == 0) - s->dev->settings.scan_mode = SCAN_MODE_GRAY; - else if (strcmp (mode, SANE_TITLE_HALFTONE) == 0) - s->dev->settings.scan_mode = SCAN_MODE_HALFTONE; - else /* Lineart */ - s->dev->settings.scan_mode = SCAN_MODE_LINEART; - - /* TODO: change and check */ - if (strcmp (source, FLATBED) == 0) - s->dev->settings.scan_method = SCAN_METHOD_FLATBED; - else /* transparency */ - s->dev->settings.scan_method = SCAN_METHOD_TRANSPARENCY; - - s->dev->settings.lines = s->params.lines; - s->dev->settings.pixels = s->params.pixels_per_line; - s->dev->settings.tl_x = tl_x; - s->dev->settings.tl_y = tl_y; - - /* threshold setting */ - s->dev->settings.threshold = 2.55 * (SANE_UNFIX (s->val[OPT_THRESHOLD].w)); - - /* color filter */ - if (strcmp (color_filter, "Red") == 0) - s->dev->settings.color_filter = 0; - else if (strcmp (color_filter, "Green") == 0) - s->dev->settings.color_filter = 1; - else if (strcmp (color_filter, "Blue") == 0) - s->dev->settings.color_filter = 2; - else - s->dev->settings.color_filter = 3; - - /* true gray */ - if (strcmp (color_filter, "None") == 0) - s->dev->settings.true_gray = 1; - else - s->dev->settings.true_gray = 0; - - /* dynamic lineart */ - s->dev->settings.dynamic_lineart = SANE_FALSE; - s->dev->settings.threshold_curve=0; - if(s->val[OPT_DISABLE_DYNAMIC_LINEART].w ==SANE_FALSE - &&s->dev->settings.scan_mode == SCAN_MODE_LINEART) - { - s->dev->settings.dynamic_lineart = SANE_TRUE; - } - - /* hardware lineart works only when we don't have interleave data - * for GL847 scanners, ie up to 600 DPI, then we have to rely on - * dynamic_lineart */ - if(s->dev->settings.xres > 600 - && s->dev->model->asic_type==GENESYS_GL847 - && s->dev->settings.scan_mode == SCAN_MODE_LINEART) - { - s->dev->settings.dynamic_lineart = SANE_TRUE; - } - - /* threshold curve for dynamic rasterization */ - s->dev->settings.threshold_curve=s->val[OPT_THRESHOLD_CURVE].w; - - /* 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->val[OPT_SWDESPECK].b - || s->val[OPT_SWCROP].b - || s->val[OPT_SWDESKEW].b - || s->val[OPT_SWDEROTATE].b - ||(SANE_UNFIX(s->val[OPT_SWSKIP].w)>0)) - && (!s->val[OPT_PREVIEW].b) - && (s->val[OPT_BIT_DEPTH].w <= 8)) - { - s->dev->buffer_image=SANE_TRUE; - } - else - { - s->dev->buffer_image=SANE_FALSE; - } - - /* brigthness and contrast only for for 8 bit scans */ - if(s->val[OPT_BIT_DEPTH].w <= 8) - { - s->dev->settings.contrast=(s->val[OPT_CONTRAST].w*127)/100; - s->dev->settings.brightness=(s->val[OPT_BRIGHTNESS].w*127)/100; - } - else - { - s->dev->settings.contrast=0; - s->dev->settings.brightness=0; - } - - /* cache expiration time */ - s->dev->settings.expiration_time=s->val[OPT_EXPIRATION_TIME].w; - - return status; -} - - -static SANE_Status -create_bpp_list (Genesys_Scanner * s, SANE_Int * bpp) -{ - int count; - - for (count = 0; bpp[count] != 0; count++) - ; - s->bpp_list[0] = count; - for (count = 0; bpp[count] != 0; count++) - { - s->bpp_list[s->bpp_list[0] - count] = bpp[count]; - } - return SANE_STATUS_GOOD; -} - -/** @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 - * gl84x: 16 bits - * gl12x: 16 bits - * @param scanner pointer to scanner session to get options - * @param option option number of the gamma table to set - */ -static void -init_gamma_vector_option (Genesys_Scanner * scanner, int option) -{ - /* the option is inactive until the custom gamma control - * is enabled */ - scanner->opt[option].type = SANE_TYPE_INT; - scanner->opt[option].cap |= SANE_CAP_INACTIVE | SANE_CAP_ADVANCED; - scanner->opt[option].unit = SANE_UNIT_NONE; - scanner->opt[option].constraint_type = SANE_CONSTRAINT_RANGE; - if (scanner->dev->model->asic_type == GENESYS_GL646) - { - if ((scanner->dev->model->flags & GENESYS_FLAG_14BIT_GAMMA) != 0) - { - scanner->opt[option].size = 16384 * sizeof (SANE_Word); - scanner->opt[option].constraint.range = &u14_range; - } - else - { /* 12 bits gamma tables */ - scanner->opt[option].size = 4096 * sizeof (SANE_Word); - scanner->opt[option].constraint.range = &u12_range; - } - } - else - { /* other asics have 16 bits words gamma table */ - scanner->opt[option].size = 256 * sizeof (SANE_Word); - scanner->opt[option].constraint.range = &u16_range; - } - /* default value is NULL */ - scanner->val[option].wa = NULL; -} - -/** - * allocate a geometry range - * @param size maximum size of the range - * @return a pointer to a valid range or NULL - */ -static SANE_Range *create_range(SANE_Fixed size) -{ -SANE_Range *range=NULL; - - range=(SANE_Range *)malloc(sizeof(SANE_Range)); - if(range!=NULL) - { - range->min = SANE_FIX (0.0); - range->max = size; - range->quant = SANE_FIX (0.0); - } - return range; -} - -/** @brief generate calibration cache file nam - * Generates the calibration cache file name to use. - * Tries to store the chache in $HOME/.sane or - * then fallbacks to $TMPDIR or TMP. The filename - * uses the model name if only one scanner is plugged - * else is uses the device name when several identical - * scanners are in use. - * @param currdev current scanner device - * @return an allocated string containing a file name - */ -GENESYS_STATIC char *calibration_filename(Genesys_Device *currdev) -{ - char *tmpstr; - char *ptr; - char filename[80]; - Genesys_Device *dev; - unsigned int count; - unsigned int i; - - /* allocate space for result */ - tmpstr=malloc(PATH_MAX); - if(tmpstr==NULL) - { - return NULL; - } - - /* first compute the DIR where we can store cache: - * 1 - home dir - * 2 - $TMPDIR - * 3 - $TMP - * 4 - tmp dir - * 5 - temp dir - * 6 - then resort to current dir - */ - ptr = getenv ("HOME"); - if(ptr==NULL) - { - ptr = getenv ("USERPROFILE"); - } - if(ptr==NULL) - { - ptr = getenv ("TMPDIR"); - } - if(ptr==NULL) - { - ptr = getenv ("TMP"); - } - - /* now choose filename: - * 1 - if only one scanner, name of the model - * 2 - if several scanners of the same model, use device name, - * replacing special chars - */ - count=0; - /* count models of the same names if several scanners attached */ - if(num_devices>1) - { - for (dev = first_dev; dev; dev = dev->next) - { - if(strcmp(dev->model->name,currdev->model->name)==0) - { - count++; - } - } - } - if(count>1) - { - snprintf(filename,sizeof(filename),"%s.cal",currdev->file_name); - for(i=0;i<strlen(filename);i++) - { - if(filename[i]==':'||filename[i]==PATH_SEP) - { - filename[i]='_'; - } - } - } - else - { - snprintf(filename,sizeof(filename),"%s.cal",currdev->model->name); - } - - /* build final final name : store dir + filename */ - if (NULL == ptr) - { - snprintf (tmpstr, PATH_MAX, "%s", filename); - } - else - { -#ifdef HAVE_MKDIR - /* make sure .sane directory exists in existing store dir */ - snprintf (tmpstr, PATH_MAX, "%s%c.sane", ptr, PATH_SEP); - mkdir(tmpstr,0700); -#endif - snprintf (tmpstr, PATH_MAX, "%s%c.sane%c%s", ptr, PATH_SEP, PATH_SEP, filename); - } - - DBG (DBG_info, "%s: calibration filename >%s<\n", __func__, tmpstr); - - return tmpstr; -} - - -static SANE_Status -init_options (Genesys_Scanner * s) -{ - SANE_Int option, count, min_dpi; - SANE_Status status; - SANE_Word *dpi_list; - Genesys_Model *model = s->dev->model; - SANE_Range *x_range, *y_range; - - DBGSTART; - - memset (s->opt, 0, sizeof (s->opt)); - memset (s->val, 0, sizeof (s->val)); - - for (option = 0; option < NUM_OPTIONS; ++option) - { - s->opt[option].size = sizeof (SANE_Word); - s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - } - s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS; - s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS; - s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS; - s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT; - s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT; - s->val[OPT_NUM_OPTS].w = NUM_OPTIONS; - - /* "Mode" group: */ - s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode"); - s->opt[OPT_MODE_GROUP].desc = ""; - s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_MODE_GROUP].size = 0; - s->opt[OPT_MODE_GROUP].cap = 0; - s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - - /* scan mode */ - s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE; - s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE; - s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE; - s->opt[OPT_MODE].type = SANE_TYPE_STRING; - s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - s->opt[OPT_MODE].size = max_string_size (mode_list); - s->opt[OPT_MODE].constraint.string_list = mode_list; - s->val[OPT_MODE].s = strdup (SANE_VALUE_SCAN_MODE_GRAY); - - /* scan source */ - s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE; - s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE; - s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE; - s->opt[OPT_SOURCE].type = SANE_TYPE_STRING; - s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST; - s->opt[OPT_SOURCE].size = max_string_size (source_list); - s->opt[OPT_SOURCE].constraint.string_list = source_list; - s->val[OPT_SOURCE].s = strdup (FLATBED); - if (model->flags & GENESYS_FLAG_HAS_UTA) - { - ENABLE (OPT_SOURCE); - } - else - { - DISABLE (OPT_SOURCE); - } - - /* preview */ - s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW; - s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW; - s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW; - s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL; - s->opt[OPT_PREVIEW].unit = SANE_UNIT_NONE; - s->opt[OPT_PREVIEW].constraint_type = SANE_CONSTRAINT_NONE; - s->val[OPT_PREVIEW].w = SANE_FALSE; - - /* bit depth */ - s->opt[OPT_BIT_DEPTH].name = SANE_NAME_BIT_DEPTH; - s->opt[OPT_BIT_DEPTH].title = SANE_TITLE_BIT_DEPTH; - s->opt[OPT_BIT_DEPTH].desc = SANE_DESC_BIT_DEPTH; - s->opt[OPT_BIT_DEPTH].type = SANE_TYPE_INT; - s->opt[OPT_BIT_DEPTH].constraint_type = SANE_CONSTRAINT_WORD_LIST; - s->opt[OPT_BIT_DEPTH].size = sizeof (SANE_Word); - s->opt[OPT_BIT_DEPTH].constraint.word_list = 0; - s->opt[OPT_BIT_DEPTH].constraint.word_list = s->bpp_list; - create_bpp_list (s, model->bpp_gray_values); - s->val[OPT_BIT_DEPTH].w = 8; - if (s->opt[OPT_BIT_DEPTH].constraint.word_list[0] < 2) - DISABLE (OPT_BIT_DEPTH); - - /* resolution */ - min_dpi=200000; - for (count = 0; model->xdpi_values[count] != 0; count++) - { - if(model->xdpi_values[count]<min_dpi) - { - min_dpi=model->xdpi_values[count]; - } - } - dpi_list = malloc ((count + 1) * sizeof (SANE_Word)); - if (!dpi_list) - return SANE_STATUS_NO_MEM; - dpi_list[0] = count; - for (count = 0; model->xdpi_values[count] != 0; count++) - dpi_list[count + 1] = model->xdpi_values[count]; - s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION; - s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION; - s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION; - s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT; - s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI; - s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST; - s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list; - s->val[OPT_RESOLUTION].w = min_dpi; - - /* "Geometry" group: */ - s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry"); - s->opt[OPT_GEOMETRY_GROUP].desc = ""; - s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED; - s->opt[OPT_GEOMETRY_GROUP].size = 0; - s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - - x_range=create_range(model->x_size); - if(x_range==NULL) - { - return SANE_STATUS_NO_MEM; - } - - y_range=create_range(model->y_size); - if(y_range==NULL) - { - return SANE_STATUS_NO_MEM; - } - - /* top-left x */ - s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X; - s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X; - s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X; - s->opt[OPT_TL_X].type = SANE_TYPE_FIXED; - s->opt[OPT_TL_X].unit = SANE_UNIT_MM; - s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_TL_X].constraint.range = x_range; - s->val[OPT_TL_X].w = 0; - - /* top-left y */ - s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y; - s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y; - s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y; - s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED; - s->opt[OPT_TL_Y].unit = SANE_UNIT_MM; - s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_TL_Y].constraint.range = y_range; - s->val[OPT_TL_Y].w = 0; - - /* bottom-right x */ - s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X; - s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X; - s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X; - s->opt[OPT_BR_X].type = SANE_TYPE_FIXED; - s->opt[OPT_BR_X].unit = SANE_UNIT_MM; - s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_BR_X].constraint.range = x_range; - s->val[OPT_BR_X].w = x_range->max; - - /* bottom-right y */ - s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y; - s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y; - s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y; - s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED; - s->opt[OPT_BR_Y].unit = SANE_UNIT_MM; - s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_BR_Y].constraint.range = y_range; - s->val[OPT_BR_Y].w = y_range->max; - - /* "Enhancement" group: */ - s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement"); - s->opt[OPT_ENHANCEMENT_GROUP].desc = ""; - s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_ENHANCEMENT_GROUP].cap = SANE_CAP_ADVANCED; - s->opt[OPT_ENHANCEMENT_GROUP].size = 0; - s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - - /* custom-gamma table */ - s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA; - s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA; - s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA; - s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL; - s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_ADVANCED; - s->val[OPT_CUSTOM_GAMMA].b = SANE_FALSE; - - /* grayscale gamma vector */ - s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_GAMMA_VECTOR; - s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_GAMMA_VECTOR; - s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_GAMMA_VECTOR; - init_gamma_vector_option (s, OPT_GAMMA_VECTOR); - - /* red gamma vector */ - s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_GAMMA_VECTOR_R; - s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_GAMMA_VECTOR_R; - s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_GAMMA_VECTOR_R; - init_gamma_vector_option (s, OPT_GAMMA_VECTOR_R); - - /* green gamma vector */ - s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_GAMMA_VECTOR_G; - s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_GAMMA_VECTOR_G; - s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_GAMMA_VECTOR_G; - init_gamma_vector_option (s, OPT_GAMMA_VECTOR_G); - - /* blue gamma vector */ - s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_GAMMA_VECTOR_B; - s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_GAMMA_VECTOR_B; - s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_GAMMA_VECTOR_B; - init_gamma_vector_option (s, OPT_GAMMA_VECTOR_B); - - /* 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)) - { - s->opt[OPT_ENHANCEMENT_GROUP].cap |= SANE_CAP_INACTIVE; - s->opt[OPT_CUSTOM_GAMMA].cap |= SANE_CAP_INACTIVE; - DBG (DBG_info, "init_options: custom gamma disabled\n"); - } - - /* software base image enhancements, these are consuming as many - * 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->val[OPT_SWDESKEW].b = SANE_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->val[OPT_SWDESPECK].b = SANE_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->val[OPT_DESPECK].w = 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->val[OPT_SWCROP].b = SANE_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; - /* disable by default */ - s->val[OPT_SWSKIP].w = 0; - - /* 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->val[OPT_SWDEROTATE].b = SANE_FALSE; - - /* Software brightness */ - s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS; - s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS; - s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS; - s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT; - s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE; - s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_BRIGHTNESS].constraint.range = &(enhance_range); - s->opt[OPT_BRIGHTNESS].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - /* disable by default */ - s->val[OPT_BRIGHTNESS].w = 0; - - /* Sowftware contrast */ - s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST; - s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST; - s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST; - s->opt[OPT_CONTRAST].type = SANE_TYPE_INT; - s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE; - s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_CONTRAST].constraint.range = &(enhance_range); - s->opt[OPT_CONTRAST].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT; - /* disable by default */ - s->val[OPT_CONTRAST].w = 0; - - /* "Extras" group: */ - s->opt[OPT_EXTRAS_GROUP].title = SANE_I18N ("Extras"); - s->opt[OPT_EXTRAS_GROUP].desc = ""; - s->opt[OPT_EXTRAS_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_EXTRAS_GROUP].cap = SANE_CAP_ADVANCED; - 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->val[OPT_THRESHOLD].w = 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->val[OPT_THRESHOLD_CURVE].w = 50; - - /* dynamic linart */ - s->opt[OPT_DISABLE_DYNAMIC_LINEART].name = "disable-dynamic-lineart"; - s->opt[OPT_DISABLE_DYNAMIC_LINEART].title = SANE_I18N ("Disable dynamic lineart"); - s->opt[OPT_DISABLE_DYNAMIC_LINEART].desc = - SANE_I18N ("Disable use of a software adaptive algorithm to generate lineart relying instead on hardware lineart."); - s->opt[OPT_DISABLE_DYNAMIC_LINEART].type = SANE_TYPE_BOOL; - s->opt[OPT_DISABLE_DYNAMIC_LINEART].unit = SANE_UNIT_NONE; - s->opt[OPT_DISABLE_DYNAMIC_LINEART].constraint_type = SANE_CONSTRAINT_NONE; - s->val[OPT_DISABLE_DYNAMIC_LINEART].w = SANE_FALSE; - - /* fastmod is required for hw lineart to work */ - if ((s->dev->model->asic_type == GENESYS_GL646) - &&(s->dev->model->motor_type != MOTOR_XP200)) - { - s->opt[OPT_DISABLE_DYNAMIC_LINEART].cap = SANE_CAP_INACTIVE; - } - - /* 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->val[OPT_DISABLE_INTERPOLATION].w = SANE_FALSE; - - /* color filter */ - s->opt[OPT_COLOR_FILTER].name = "color-filter"; - s->opt[OPT_COLOR_FILTER].title = SANE_I18N ("Color filter"); - s->opt[OPT_COLOR_FILTER].desc = - SANE_I18N - ("When using gray or lineart this option selects the used color."); - s->opt[OPT_COLOR_FILTER].type = SANE_TYPE_STRING; - s->opt[OPT_COLOR_FILTER].constraint_type = SANE_CONSTRAINT_STRING_LIST; - /* true gray not yet supported for GL847 and GL124 scanners */ - if(!model->is_cis || model->asic_type==GENESYS_GL847 || model->asic_type==GENESYS_GL124) - { - s->opt[OPT_COLOR_FILTER].size = max_string_size (color_filter_list); - s->opt[OPT_COLOR_FILTER].constraint.string_list = color_filter_list; - s->val[OPT_COLOR_FILTER].s = strdup (s->opt[OPT_COLOR_FILTER].constraint.string_list[1]); - } - else - { - s->opt[OPT_COLOR_FILTER].size = max_string_size (cis_color_filter_list); - s->opt[OPT_COLOR_FILTER].constraint.string_list = cis_color_filter_list; - /* default to "None" ie true gray */ - s->val[OPT_COLOR_FILTER].s = strdup (s->opt[OPT_COLOR_FILTER].constraint.string_list[3]); - } - - /* no support for color filter for cis+gl646 scanners */ - if (model->asic_type == GENESYS_GL646 && model->is_cis) - { - DISABLE (OPT_COLOR_FILTER); - } - - /* calibration store file name */ - s->opt[OPT_CALIBRATION_FILE].name = "calibration-file"; - s->opt[OPT_CALIBRATION_FILE].title = SANE_I18N ("Calibration file"); - s->opt[OPT_CALIBRATION_FILE].desc = SANE_I18N ("Specify the calibration file to use"); - s->opt[OPT_CALIBRATION_FILE].type = SANE_TYPE_STRING; - s->opt[OPT_CALIBRATION_FILE].unit = SANE_UNIT_NONE; - s->opt[OPT_CALIBRATION_FILE].size = PATH_MAX; - s->opt[OPT_CALIBRATION_FILE].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; - s->opt[OPT_CALIBRATION_FILE].constraint_type = SANE_CONSTRAINT_NONE; - s->val[OPT_CALIBRATION_FILE].s = NULL; - /* disable option if ran as root */ -#ifdef HAVE_GETUID - if(geteuid()==0) - { - DISABLE (OPT_CALIBRATION_FILE); - } -#endif - - /* expiration time for calibration cache entries */ - s->opt[OPT_EXPIRATION_TIME].name = "expiration-time"; - s->opt[OPT_EXPIRATION_TIME].title = SANE_I18N ("Calibration cache expiration time"); - s->opt[OPT_EXPIRATION_TIME].desc = SANE_I18N ("Time (in minutes) before a cached calibration expires. " - "A value of 0 means cache is not used. A negative value means cache never expires."); - s->opt[OPT_EXPIRATION_TIME].type = SANE_TYPE_INT; - s->opt[OPT_EXPIRATION_TIME].unit = SANE_UNIT_NONE; - s->opt[OPT_EXPIRATION_TIME].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_EXPIRATION_TIME].constraint.range = &expiration_range; - s->val[OPT_EXPIRATION_TIME].w = 60; /* 60 minutes by default */ - - /* Powersave time (turn lamp off) */ - s->opt[OPT_LAMP_OFF_TIME].name = "lamp-off-time"; - s->opt[OPT_LAMP_OFF_TIME].title = SANE_I18N ("Lamp off time"); - s->opt[OPT_LAMP_OFF_TIME].desc = - SANE_I18N - ("The lamp will be turned off after the given time (in minutes). " - "A value of 0 means, that the lamp won't be turned off."); - s->opt[OPT_LAMP_OFF_TIME].type = SANE_TYPE_INT; - s->opt[OPT_LAMP_OFF_TIME].unit = SANE_UNIT_NONE; - s->opt[OPT_LAMP_OFF_TIME].constraint_type = SANE_CONSTRAINT_RANGE; - s->opt[OPT_LAMP_OFF_TIME].constraint.range = &time_range; - s->val[OPT_LAMP_OFF_TIME].w = 15; /* 15 minutes */ - - /* turn lamp off during scan */ - s->opt[OPT_LAMP_OFF].name = "lamp-off-scan"; - s->opt[OPT_LAMP_OFF].title = SANE_I18N ("Lamp off during scan"); - s->opt[OPT_LAMP_OFF].desc = SANE_I18N ("The lamp will be turned off during scan. "); - s->opt[OPT_LAMP_OFF].type = SANE_TYPE_BOOL; - s->opt[OPT_LAMP_OFF].unit = SANE_UNIT_NONE; - s->opt[OPT_LAMP_OFF].constraint_type = SANE_CONSTRAINT_NONE; - s->val[OPT_LAMP_OFF].w = SANE_FALSE; - - s->opt[OPT_SENSOR_GROUP].title = SANE_TITLE_SENSORS; - s->opt[OPT_SENSOR_GROUP].desc = SANE_DESC_SENSORS; - s->opt[OPT_SENSOR_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_SENSOR_GROUP].cap = SANE_CAP_ADVANCED; - s->opt[OPT_SENSOR_GROUP].size = 0; - s->opt[OPT_SENSOR_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - - s->opt[OPT_SCAN_SW].name = SANE_NAME_SCAN; - s->opt[OPT_SCAN_SW].title = SANE_TITLE_SCAN; - s->opt[OPT_SCAN_SW].desc = SANE_DESC_SCAN; - s->opt[OPT_SCAN_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_SCAN_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_SCAN_SW) - s->opt[OPT_SCAN_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_SCAN_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_SCAN_SW].b = 0; - s->last_val[OPT_SCAN_SW].b = 0; - - /* SANE_NAME_FILE is not for buttons */ - s->opt[OPT_FILE_SW].name = "file"; - s->opt[OPT_FILE_SW].title = SANE_I18N ("File button"); - s->opt[OPT_FILE_SW].desc = SANE_I18N ("File button"); - s->opt[OPT_FILE_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_FILE_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_FILE_SW) - s->opt[OPT_FILE_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_FILE_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_FILE_SW].b = 0; - s->last_val[OPT_FILE_SW].b = 0; - - s->opt[OPT_EMAIL_SW].name = SANE_NAME_EMAIL; - s->opt[OPT_EMAIL_SW].title = SANE_TITLE_EMAIL; - s->opt[OPT_EMAIL_SW].desc = SANE_DESC_EMAIL; - s->opt[OPT_EMAIL_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_EMAIL_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_EMAIL_SW) - s->opt[OPT_EMAIL_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_EMAIL_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_EMAIL_SW].b = 0; - s->last_val[OPT_EMAIL_SW].b = 0; - - s->opt[OPT_COPY_SW].name = SANE_NAME_COPY; - s->opt[OPT_COPY_SW].title = SANE_TITLE_COPY; - s->opt[OPT_COPY_SW].desc = SANE_DESC_COPY; - s->opt[OPT_COPY_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_COPY_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_COPY_SW) - s->opt[OPT_COPY_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_COPY_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_COPY_SW].b = 0; - s->last_val[OPT_COPY_SW].b = 0; - - s->opt[OPT_PAGE_LOADED_SW].name = SANE_NAME_PAGE_LOADED; - s->opt[OPT_PAGE_LOADED_SW].title = SANE_TITLE_PAGE_LOADED; - s->opt[OPT_PAGE_LOADED_SW].desc = SANE_DESC_PAGE_LOADED; - s->opt[OPT_PAGE_LOADED_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_PAGE_LOADED_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_PAGE_LOADED_SW) - s->opt[OPT_PAGE_LOADED_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_PAGE_LOADED_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_PAGE_LOADED_SW].b = 0; - s->last_val[OPT_PAGE_LOADED_SW].b = 0; - - /* OCR button */ - s->opt[OPT_OCR_SW].name = "ocr"; - s->opt[OPT_OCR_SW].title = SANE_I18N ("OCR button"); - s->opt[OPT_OCR_SW].desc = SANE_I18N ("OCR button"); - s->opt[OPT_OCR_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_OCR_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_OCR_SW) - s->opt[OPT_OCR_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_OCR_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_OCR_SW].b = 0; - s->last_val[OPT_OCR_SW].b = 0; - - /* power button */ - s->opt[OPT_POWER_SW].name = "power"; - s->opt[OPT_POWER_SW].title = SANE_I18N ("Power button"); - s->opt[OPT_POWER_SW].desc = SANE_I18N ("Power button"); - s->opt[OPT_POWER_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_POWER_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_POWER_SW) - s->opt[OPT_POWER_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_POWER_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_POWER_SW].b = 0; - s->last_val[OPT_POWER_SW].b = 0; - - /* extra button */ - s->opt[OPT_EXTRA_SW].name = "extra"; - s->opt[OPT_EXTRA_SW].title = SANE_I18N ("Extra button"); - s->opt[OPT_EXTRA_SW].desc = SANE_I18N ("Extra button"); - s->opt[OPT_EXTRA_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_EXTRA_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_EXTRA_SW) - s->opt[OPT_EXTRA_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_EXTRA_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_EXTRA_SW].b = 0; - s->last_val[OPT_EXTRA_SW].b = 0; - - /* calibration needed */ - s->opt[OPT_NEED_CALIBRATION_SW].name = "need-calibration"; - s->opt[OPT_NEED_CALIBRATION_SW].title = SANE_I18N ("Need calibration"); - s->opt[OPT_NEED_CALIBRATION_SW].desc = SANE_I18N ("The scanner needs calibration for the current settings"); - s->opt[OPT_NEED_CALIBRATION_SW].type = SANE_TYPE_BOOL; - s->opt[OPT_NEED_CALIBRATION_SW].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_CALIBRATE) - s->opt[OPT_NEED_CALIBRATION_SW].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_HARD_SELECT | SANE_CAP_ADVANCED; - else - s->opt[OPT_NEED_CALIBRATION_SW].cap = SANE_CAP_INACTIVE; - s->val[OPT_NEED_CALIBRATION_SW].b = 0; - s->last_val[OPT_NEED_CALIBRATION_SW].b = 0; - - /* button group */ - s->opt[OPT_BUTTON_GROUP].title = SANE_I18N ("Buttons"); - s->opt[OPT_BUTTON_GROUP].desc = ""; - s->opt[OPT_BUTTON_GROUP].type = SANE_TYPE_GROUP; - s->opt[OPT_BUTTON_GROUP].cap = SANE_CAP_ADVANCED; - s->opt[OPT_BUTTON_GROUP].size = 0; - s->opt[OPT_BUTTON_GROUP].constraint_type = SANE_CONSTRAINT_NONE; - - /* calibrate button */ - s->opt[OPT_CALIBRATE].name = "calibrate"; - s->opt[OPT_CALIBRATE].title = SANE_I18N ("Calibrate"); - s->opt[OPT_CALIBRATE].desc = - SANE_I18N ("Start calibration using special sheet"); - s->opt[OPT_CALIBRATE].type = SANE_TYPE_BUTTON; - s->opt[OPT_CALIBRATE].unit = SANE_UNIT_NONE; - if (model->buttons & GENESYS_HAS_CALIBRATE) - s->opt[OPT_CALIBRATE].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED | - SANE_CAP_AUTOMATIC; - else - s->opt[OPT_CALIBRATE].cap = SANE_CAP_INACTIVE; - s->val[OPT_CALIBRATE].b = 0; - s->last_val[OPT_CALIBRATE].b = 0; - - /* clear calibration cache button */ - s->opt[OPT_CLEAR_CALIBRATION].name = "clear-calibration"; - s->opt[OPT_CLEAR_CALIBRATION].title = SANE_I18N ("Clear calibration"); - s->opt[OPT_CLEAR_CALIBRATION].desc = SANE_I18N ("Clear calibration cache"); - s->opt[OPT_CLEAR_CALIBRATION].type = SANE_TYPE_BUTTON; - s->opt[OPT_CLEAR_CALIBRATION].unit = SANE_UNIT_NONE; - s->opt[OPT_CLEAR_CALIBRATION].size = 0; - s->opt[OPT_CLEAR_CALIBRATION].constraint_type = SANE_CONSTRAINT_NONE; - s->opt[OPT_CLEAR_CALIBRATION].cap = - SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT | SANE_CAP_ADVANCED; - s->val[OPT_CLEAR_CALIBRATION].b = 0; - s->last_val[OPT_CLEAR_CALIBRATION].b = 0; - - RIE (calc_parameters (s)); - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -static SANE_Bool present; -static SANE_Status -check_present (SANE_String_Const devname) -{ - present=SANE_TRUE; - DBG (DBG_io, "check_present: %s detected.\n",devname); - return SANE_STATUS_GOOD; -} - -/** @brief add a scanner device - * Insert the given device into the backend list of devices. - * @param dev device to add - */ -GENESYS_STATIC void add_device(Genesys_Device *dev) -{ - ++num_devices; - dev->next = first_dev; - first_dev = dev; -} - -static SANE_Status -attach (SANE_String_Const devname, Genesys_Device ** devp, SANE_Bool may_wait) -{ - Genesys_Device *dev = 0; - SANE_Int dn, vendor, product; - SANE_Status status; - unsigned int i; - - - DBG (DBG_proc, "attach: start: devp %s NULL, may_wait = %d\n", - devp ? "!=" : "==", may_wait); - - if (devp) - *devp = 0; - - if (!devname) - { - DBG (DBG_error, "attach: devname == NULL\n"); - return SANE_STATUS_INVAL; - } - - for (dev = first_dev; dev; dev = dev->next) - { - if (strcmp (dev->file_name, devname) == 0) - { - if (devp) - *devp = dev; - DBG (DBG_info, "attach: device `%s' was already in device list\n", - devname); - return SANE_STATUS_GOOD; - } - } - - DBG (DBG_info, "attach: trying to open device `%s'\n", devname); - - status = sanei_usb_open (devname, &dn); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_warn, "attach: couldn't open device `%s': %s\n", devname, - sane_strstatus (status)); - return status; - } - else - DBG (DBG_info, "attach: device `%s' successfully opened\n", devname); - - status = sanei_usb_get_vendor_product (dn, &vendor, &product); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "attach: couldn't get vendor and product ids of device `%s': %s\n", - devname, sane_strstatus (status)); - return status; - } - - /* KV-SS080 is an auxiliary device which requires a master device to be here */ - if(vendor == 0x04da && product == 0x100f) - { - present=SANE_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); - if(present==SANE_FALSE) - { - DBG (DBG_error,"attach: master device not present\n"); - return SANE_STATUS_INVAL; - } - } - - for (i = 0; i < MAX_SCANNERS && genesys_usb_device_list[i].model != 0; i++) - { - if (vendor == genesys_usb_device_list[i].vendor && - product == genesys_usb_device_list[i].product) - { - dev = malloc (sizeof (*dev)); - if (!dev) - return SANE_STATUS_NO_MEM; - break; - } - } - - if (!dev) - { - DBG (DBG_error, - "attach: vendor %d product %d is not supported by this backend\n", - vendor, product); - return SANE_STATUS_INVAL; - } - - dev->file_name = strdup (devname); - if (!dev->file_name) - { - free(dev); - return SANE_STATUS_NO_MEM; - } - - dev->model = genesys_usb_device_list[i].model; - dev->vendorId = genesys_usb_device_list[i].vendor; - dev->productId = genesys_usb_device_list[i].product; - dev->already_initialized = SANE_FALSE; - - DBG (DBG_info, "attach: found %s flatbed scanner %s at %s\n", - dev->model->vendor, dev->model->model, dev->file_name); - add_device(dev); - - if (devp) - *devp = dev; - sanei_usb_close (dn); - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -static SANE_Status -attach_one_device (SANE_String_Const devname) -{ - Genesys_Device *dev; - SANE_Status status; - Genesys_Device **tmp_dev; - - RIE (attach (devname, &dev, SANE_FALSE)); - - if (dev) - { - /* Keep track of newly attached devices so we can set options as - necessary. */ - tmp_dev=NULL; - /* increase device list capacity if needed */ - if (new_dev_len >= new_dev_alloced) - { - new_dev_alloced += 4; - if (new_dev) - { - tmp_dev = new_dev; - new_dev = realloc (new_dev, new_dev_alloced * sizeof (new_dev[0])); - } - else - { - new_dev = malloc (new_dev_alloced * sizeof (new_dev[0])); - tmp_dev = NULL; - } - if (!new_dev) - { - FREE_IFNOT_NULL(tmp_dev) - DBG (DBG_error, "attach_one_device: out of memory\n"); - return SANE_STATUS_NO_MEM; - } - } - new_dev[new_dev_len++] = dev; - } - return SANE_STATUS_GOOD; -} - -/* configuration framework functions */ -static SANE_Status -config_attach_genesys (SANEI_Config __sane_unused__ *config, const char *devname) -{ - /* the devname has been processed and is ready to be used - * directly. Since the backend is an USB only one, we can - * call sanei_usb_attach_matching_devices straight */ - sanei_usb_attach_matching_devices (devname, attach_one_device); - - return SANE_STATUS_GOOD; -} - -/* probes for scanner to attach to the backend */ -#ifndef UNIT_TESTING -static -#endif -SANE_Status -probe_genesys_devices (void) -{ - SANEI_Config config; - SANE_Status status; - - DBGSTART; - - new_dev = 0; - new_dev_len = 0; - new_dev_alloced = 0; - - /* set configuration options structure : no option for this backend */ - config.descriptors = NULL; - config.values = NULL; - config.count = 0; - - /* generic configure and attach function */ - status = sanei_configure_attach (GENESYS_CONFIG_FILE, &config, - config_attach_genesys); - - if (new_dev_alloced > 0) - { - new_dev_len = new_dev_alloced = 0; - free (new_dev); - } - - DBG(DBG_info, "%s: %d devices currently attached\n", __func__, num_devices); - - DBGCOMPLETED; - - return status; -} - -/** - * This should be changed if one of the substructures of - Genesys_Calibration_Cache change, but it must be changed if there are - changes that don't change size -- at least for now, as we store most - of Genesys_Calibration_Cache as is. -*/ -#define CALIBRATION_VERSION 1 - -/** - * reads previously cached calibration data - * from file define in dev->calib_file - */ -SANE_Status -sanei_genesys_read_calibration (Genesys_Device * dev) -{ - FILE *fp; - uint8_t vers = 0; - uint32_t size = 0; - struct Genesys_Calibration_Cache *cache; - SANE_Status status=SANE_STATUS_GOOD; - - DBGSTART; - - /* open calibration cache file */ - fp = fopen (dev->calib_file, "rb"); - if (!fp) - { - DBG (DBG_info, "Calibration: Cannot open %s\n", dev->calib_file); - DBGCOMPLETED; - return SANE_STATUS_IO_ERROR; - } - - /* these two checks ensure that most bad things cannot happen */ - fread (&vers, 1, 1, fp); - if (vers != CALIBRATION_VERSION) - { - DBG (DBG_info, "Calibration: Bad version\n"); - fclose (fp); - DBGCOMPLETED; - return SANE_STATUS_INVAL; - } - fread (&size, 4, 1, fp); - if (size != sizeof (struct Genesys_Calibration_Cache)) - { - DBG (DBG_info, - "Calibration: Size of calibration cache struct differs\n"); - fclose (fp); - DBGCOMPLETED; - return SANE_STATUS_INVAL; - } - - /* clear device calibration cache */ - while(dev->calibration_cache!=NULL) - { - cache=dev->calibration_cache; - dev->calibration_cache=dev->calibration_cache->next; - free(cache); - } - - /* loop on cache records in file */ - while (!feof (fp) && status==SANE_STATUS_GOOD) - { - DBG (DBG_info, "sanei_genesys_read_calibration: reading one record\n"); - cache = (struct Genesys_Calibration_Cache *) malloc (sizeof (*cache)); - - if (!cache) - { - DBG (DBG_error, - "sanei_genesys_read_calibration: could not allocate cache struct\n"); - break; - } - -#define BILT1( x ) \ - do \ - { \ - if ((x) < 1) \ - { \ - free(cache); \ - DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n"); \ - status=SANE_STATUS_EOF; \ - break; \ - } \ - } while(0) - - - if (fread (&cache->used_setup, sizeof (cache->used_setup), 1, fp) < 1) - { /* eof is only detected here */ - free (cache); - status=SANE_STATUS_GOOD; - break; - } - BILT1 (fread (&cache->last_calibration, sizeof (cache->last_calibration), 1, fp)); - BILT1 (fread (&cache->frontend, sizeof (cache->frontend), 1, fp)); - /* the gamma (and later) fields are not stored */ - BILT1 (fread (&cache->sensor, offsetof (Genesys_Sensor, gamma[0]), 1, fp)); - BILT1 (fread (&cache->calib_pixels, sizeof (cache->calib_pixels), 1, fp)); - BILT1 (fread (&cache->calib_channels, sizeof (cache->calib_channels), 1, fp)); - BILT1 (fread (&cache->average_size, sizeof (cache->average_size), 1, fp)); - - cache->white_average_data = (uint8_t *) malloc (cache->average_size); - cache->dark_average_data = (uint8_t *) malloc (cache->average_size); - - if (!cache->white_average_data || !cache->dark_average_data) - { - status=SANE_STATUS_NO_MEM; - FREE_IFNOT_NULL (cache->white_average_data); - FREE_IFNOT_NULL (cache->dark_average_data); - free (cache); - DBG (DBG_error, - "sanei_genesys_read_calibration: could not allocate space for average data\n"); - break; - } - - if (fread (cache->white_average_data, cache->average_size, 1, fp) < 1) - { - status=SANE_STATUS_EOF; - DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n"); - free (cache->white_average_data); - free (cache->dark_average_data); - free (cache); - break; - } - if (fread (cache->dark_average_data, cache->average_size, 1, fp) < 1) - { - DBG (DBG_warn, "sanei_genesys_read_calibration: partial calibration record\n"); - free (cache->white_average_data); - free (cache->dark_average_data); - free (cache); - status=SANE_STATUS_EOF; - break; - } -#undef BILT1 - DBG (DBG_info, "sanei_genesys_read_calibration: adding record to list\n"); - cache->next = dev->calibration_cache; - dev->calibration_cache = cache; - } - - fclose (fp); - DBGCOMPLETED; - return status; -} - -static void -write_calibration (Genesys_Device * dev) -{ - FILE *fp; - uint8_t vers = 0; - uint32_t size = 0; - struct Genesys_Calibration_Cache *cache; - - DBGSTART; - fp = fopen (dev->calib_file, "wb"); - if (!fp) - { - DBG (DBG_info, "write_calibration: Cannot open %s for writing\n", dev->calib_file); - return; - } - - vers = CALIBRATION_VERSION; - fwrite (&vers, 1, 1, fp); - size = sizeof (struct Genesys_Calibration_Cache); - fwrite (&size, 4, 1, fp); - - for (cache = dev->calibration_cache; cache; cache = cache->next) - { - fwrite (&cache->used_setup, sizeof (cache->used_setup), 1, fp); - fwrite (&cache->last_calibration, sizeof (cache->last_calibration), 1, fp); - fwrite (&cache->frontend, sizeof (cache->frontend), 1, fp); - /* the gamma (and later) fields are not stored */ - fwrite (&cache->sensor, offsetof (Genesys_Sensor, gamma[0]), 1, fp); - - fwrite (&cache->calib_pixels, sizeof (cache->calib_pixels), 1, fp); - fwrite (&cache->calib_channels, sizeof (cache->calib_channels), 1, fp); - fwrite (&cache->average_size, sizeof (cache->average_size), 1, fp); - fwrite (cache->white_average_data, cache->average_size, 1, fp); - fwrite (cache->dark_average_data, cache->average_size, 1, fp); - } - DBGCOMPLETED; - fclose (fp); -} - -/** @brief buffer scanned picture - * In order to allow digital processing, we must be able to put all the - * scanned picture in a buffer. - */ -static SANE_Status -genesys_buffer_image(Genesys_Scanner *s) -{ - SANE_Status status = SANE_STATUS_GOOD; - 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; - SANE_Byte *lineart=NULL; - - /* compute maximum number of lines for the scan */ - if (s->params.lines > 0) - { - lines = s->params.lines; - } - else - { - lines = - (SANE_UNFIX (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.dynamic_lineart==SANE_TRUE) - { - 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; - - /* allocate memory */ - dev->img_buffer = (SANE_Byte *) malloc (size); - if (dev->img_buffer == NULL) - { - DBG (DBG_error, - "%s: digital processing requires too much memory.\nConsider disabling it\n", - __func__); - return SANE_STATUS_NO_MEM; - } - - /* loop reading data until we reach maximum or EOF */ - total = 0; - while (total < maximum && status != SANE_STATUS_EOF) - { - len = size - maximum; - if (len > read_size) - { - len = read_size; - } - status = genesys_read_ordered_data (dev, dev->img_buffer + total, &len); - if (status != SANE_STATUS_EOF && status != SANE_STATUS_GOOD) - { - free (s->dev->img_buffer); - DBG (DBG_error, "%s: %s buffering failed\n", __func__, - sane_strstatus (status)); - return status; - } - total += len; - - /* do we need to enlarge read buffer ? */ - if (total + read_size > size && status != SANE_STATUS_EOF) - { - size += read_size; - dev->img_buffer = (SANE_Byte *) realloc (dev->img_buffer, size); - if (dev->img_buffer == NULL) - { - DBG (DBG_error0, - "%s: digital processing requires too much memory.\nConsider disabling it\n", - __func__); - return SANE_STATUS_NO_MEM; - } - } - } - - /* 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 == SANE_FALSE && - dev->parking == SANE_FALSE) - { - dev->model->cmd_set->slow_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.dynamic_lineart==SANE_TRUE) - { - total/=8; - lineart=(SANE_Byte *)malloc(total); - if (lineart == NULL) - { - DBG (DBG_error0, - "%s: digital processing requires too much memory.\nConsider disabling it\n", - __func__); - return SANE_STATUS_NO_MEM; - } - genesys_gray_lineart (dev, - dev->img_buffer, - lineart, - dev->settings.pixels, - (total*8)/dev->settings.pixels, - dev->settings.threshold); - free(dev->img_buffer); - 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 ("unprocessed.pnm", - dev->img_buffer, - s->params.depth, - s->params.format==SANE_FRAME_RGB ? 3:1, - s->params.pixels_per_line, - s->params.lines); - } - - return SANE_STATUS_GOOD; -} - -/* -------------------------- SANE API functions ------------------------- */ - -SANE_Status -sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize) -{ - SANE_Status status; - - DBG_INIT (); - DBG (DBG_init, "SANE Genesys backend version %d.%d build %d from %s\n", - SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING); -#ifdef HAVE_LIBUSB - DBG (DBG_init, "SANE Genesys backend built with libusb-1.0\n"); -#endif -#ifdef HAVE_LIBUSB_LEGACY - DBG (DBG_init, "SANE Genesys backend built with libusb\n"); -#endif - - if (version_code) - *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD); - - DBG (DBG_proc, "sane_init: authorize %s null\n", authorize ? "!=" : "=="); - - /* init usb use */ - sanei_usb_init (); - - /* init sanei_magic */ - sanei_magic_init(); - - DBG (DBG_info, "sane_init: %s endian machine\n", -#ifdef WORDS_BIGENDIAN - "big" -#else - "little" -#endif - ); - - /* set up to no devices at first */ - num_devices = 0; - first_dev = 0; - first_handle = 0; - devlist = 0; - - /* cold-plug case :detection of allready connected scanners */ - status = probe_genesys_devices (); - - DBGCOMPLETED; - - return status; -} - -void -sane_exit (void) -{ - Genesys_Device *dev, *next; - - DBGSTART; - for (dev = first_dev; dev; dev = next) - { - /* sane_close() free many fields, not much things left to - * do here */ - next = dev->next; - free (dev->file_name); - free (dev); - } - first_dev = 0; - first_handle = 0; - if (devlist) - free (devlist); - devlist = 0; - - sanei_usb_exit(); - - DBGCOMPLETED; -} - -SANE_Status -sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only) -{ - Genesys_Device *dev, *prev; - SANE_Int index; - SANE_Device *sane_device; - - DBG (DBG_proc, "sane_get_devices: start: local_only = %s\n", - local_only == SANE_TRUE ? "true" : "false"); - - /* hot-plug case : detection of newly connected scanners */ - sanei_usb_scan_devices (); - probe_genesys_devices (); - - if (devlist) - free (devlist); - - devlist = malloc ((num_devices + 1) * sizeof (devlist[0])); - if (!devlist) - return SANE_STATUS_NO_MEM; - - prev = NULL; - index = 0; - dev = first_dev; - while (dev != NULL) - { - /* check if device removed */ - present = SANE_FALSE; - sanei_usb_find_devices (dev->vendorId, dev->productId, check_present); - if (present) - { - sane_device = malloc (sizeof (*sane_device)); - if (!sane_device) - return SANE_STATUS_NO_MEM; - sane_device->name = dev->file_name; - sane_device->vendor = dev->model->vendor; - sane_device->model = dev->model->model; - sane_device->type = strdup ("flatbed scanner"); - devlist[index] = sane_device; - index++; - prev = dev; - dev = dev->next; - } - else - { - /* remove device from internal list */ - /* case 1 : removed device is first_dev */ - if (prev == NULL) - { - /* test for another dev */ - if (dev->next == NULL) - { - /* empty the whole list */ - free (dev); - first_dev = NULL; - num_devices = 0; - dev = NULL; - } - else - { - /* assign new start */ - first_dev = dev->next; - num_devices--; - free (dev); - dev = first_dev; - } - } - /* case 2 : removed device is not first_dev */ - else - { - /* link previous dev to next dev */ - prev->next = dev->next; - free (dev); - num_devices--; - - /* next loop */ - dev = prev->next; - } - } - } - devlist[index] = 0; - - *device_list = devlist; - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -SANE_Status -sane_open (SANE_String_Const devicename, SANE_Handle * handle) -{ - Genesys_Device *dev; - SANE_Status status; - Genesys_Scanner *s; - char *tmpstr; - - DBG (DBG_proc, "sane_open: start (devicename = `%s')\n", devicename); - - /* devicename="" or devicename="genesys" are default values that use - * first available device - */ - if (devicename[0] && strcmp ("genesys", devicename) != 0) - { - /* search for the given devicename in the device list */ - for (dev = first_dev; dev; dev = dev->next) - if (strcmp (dev->file_name, devicename) == 0) - break; - - if (!dev) - { - DBG (DBG_info, - "sane_open: couldn't find `%s' in devlist, trying attach\n", - devicename); - RIE (attach (devicename, &dev, SANE_TRUE)); - } - else - DBG (DBG_info, "sane_open: found `%s' in devlist\n", - dev->model->name); - } - else - { - /* empty devicename or "genesys" -> use first device */ - dev = first_dev; - if (dev) - { - devicename = dev->file_name; - DBG (DBG_info, "sane_open: empty devicename, trying `%s'\n", - devicename); - } - } - - if (!dev) - return SANE_STATUS_INVAL; - - 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@lists.alioth.debian.org. 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"); - } - - status = sanei_usb_open (dev->file_name, &dev->dn); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_warn, "sane_open: couldn't open device `%s': %s\n", - dev->file_name, sane_strstatus (status)); - return status; - } - - - s = malloc (sizeof (*s)); - if (!s) - return SANE_STATUS_NO_MEM; - - s->dev = dev; - s->scanning = SANE_FALSE; - s->dev->read_buffer.buffer = NULL; - s->dev->lines_buffer.buffer = NULL; - s->dev->shrink_buffer.buffer = NULL; - s->dev->out_buffer.buffer = NULL; - s->dev->binarize_buffer.buffer = NULL; - s->dev->local_buffer.buffer = NULL; - s->dev->parking = SANE_FALSE; - s->dev->read_active = SANE_FALSE; - s->dev->white_average_data = NULL; - s->dev->dark_average_data = NULL; - s->dev->calibration_cache = NULL; - s->dev->calib_file = NULL; - s->dev->img_buffer = NULL; - s->dev->line_interp = 0; - s->dev->line_count = 0; - s->dev->segnb = 0; - s->dev->oe_buffer.buffer=NULL; - s->dev->binary=NULL; - - /* insert newly opened handle into list of open handles: */ - s->next = first_handle; - first_handle = s; - *handle = s; - - if (!dev->already_initialized) - sanei_genesys_init_structs (dev); - - RIE (init_options (s)); - - if (sanei_genesys_init_cmd_set (s->dev) != SANE_STATUS_GOOD) - { - DBG (DBG_error0, "This device doesn't have a valid command set!!\n"); - return SANE_STATUS_IO_ERROR; - } - - RIE (dev->model->cmd_set->init (dev)); - - /* some hardware capabilities are detected through sensors */ - RIE (s->dev->model->cmd_set->update_hardware_sensors (s)); - - /* here is the place to fetch a stored calibration cache */ - tmpstr=calibration_filename(s->dev); - s->val[OPT_CALIBRATION_FILE].s = strdup (tmpstr); - s->dev->calib_file = strdup (tmpstr); - DBG (DBG_info, "%s: Calibration filename set to:\n", __func__); - DBG (DBG_info, "%s: >%s<\n", __func__, s->dev->calib_file); - free(tmpstr); - - /* now open file, fetch calibration records */ - - sanei_genesys_read_calibration (s->dev); - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -void -sane_close (SANE_Handle handle) -{ - Genesys_Scanner *prev, *s; - Genesys_Calibration_Cache *cache, *next_cache; - SANE_Status status; - - DBGSTART; - - /* remove handle from list of open handles: */ - prev = 0; - for (s = first_handle; s; s = s->next) - { - if (s == handle) - break; - prev = s; - } - if (!s) - { - DBG (DBG_error, "sane_close: invalid handle %p\n", handle); - return; /* oops, not a handle we know about */ - } - - /* eject document for sheetfed scanners */ - if (s->dev->model->is_sheetfed == SANE_TRUE) - { - s->dev->model->cmd_set->eject_document (s->dev); - } - else - { - /* in case scanner is parking, wait for the head - * to reach home position */ - if(s->dev->parking==SANE_TRUE) - { - status = sanei_genesys_wait_for_home (s->dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "sane_close: failed to wait for head to park: %s\n", - sane_strstatus (status)); - } - } - } - - /* enable power saving before leaving */ - status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "sane_close: failed to enable power saving mode: %s\n", - sane_strstatus (status)); - } - - /* here is the place to store calibration cache */ - write_calibration (s->dev); - - for (cache = s->dev->calibration_cache; cache; cache = next_cache) - { - next_cache = cache->next; - free (cache->dark_average_data); - free (cache->white_average_data); - free (cache); - } - - sanei_genesys_buffer_free (&(s->dev->read_buffer)); - sanei_genesys_buffer_free (&(s->dev->lines_buffer)); - sanei_genesys_buffer_free (&(s->dev->shrink_buffer)); - sanei_genesys_buffer_free (&(s->dev->out_buffer)); - sanei_genesys_buffer_free (&(s->dev->binarize_buffer)); - sanei_genesys_buffer_free (&(s->dev->local_buffer)); - FREE_IFNOT_NULL (s->dev->white_average_data); - FREE_IFNOT_NULL (s->dev->dark_average_data); - FREE_IFNOT_NULL (s->dev->calib_file); - - /* free allocated gamma tables */ - FREE_IFNOT_NULL (s->dev->sensor.gamma_table[0]); - FREE_IFNOT_NULL (s->dev->sensor.gamma_table[1]); - FREE_IFNOT_NULL (s->dev->sensor.gamma_table[2]); - - s->dev->already_initialized = SANE_FALSE; - - /* for an handful of bytes .. */ - free ((void *)(size_t)s->opt[OPT_RESOLUTION].constraint.word_list); - free (s->val[OPT_SOURCE].s); - free (s->val[OPT_MODE].s); - free (s->val[OPT_COLOR_FILTER].s); - free ((void *)(size_t)s->opt[OPT_TL_X].constraint.range); - free ((void *)(size_t)s->opt[OPT_TL_Y].constraint.range); - - if (prev) - prev->next = s->next; - else - first_handle = s->next; - - /* LAMP OFF : same register across all the ASICs */ - sanei_genesys_write_register (s->dev, 0x03, 0x00); - - /* clear before closing */ - sanei_usb_clear_halt (s->dev->dn); - - /* we need this to avoid these ASIC getting stuck - * in bulk writes */ - sanei_usb_reset (s->dev->dn); - - sanei_usb_close (s->dev->dn); - free (s); - - DBGCOMPLETED; -} - -const SANE_Option_Descriptor * -sane_get_option_descriptor (SANE_Handle handle, SANE_Int option) -{ - Genesys_Scanner *s = handle; - - if ((unsigned) option >= NUM_OPTIONS) - return 0; - DBG (DBG_io2, "sane_get_option_descriptor: option = %s (%d)\n", - s->opt[option].name, option); - return s->opt + option; -} - -/* gets an option , called by sane_control_option */ -static SANE_Status -get_option_value (Genesys_Scanner * s, int option, void *val) -{ - unsigned int i; - SANE_Word *table ,tmp; - uint16_t *gamma; - SANE_Status status = SANE_STATUS_GOOD; - Genesys_Calibration_Cache *cache; - - switch (option) - { - /* geometry */ - case OPT_TL_X: - case OPT_TL_Y: - case OPT_BR_X: - case OPT_BR_Y: - *(SANE_Word *) val = s->val[option].w; - /* switch coordinate to keep them coherent */ - if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w) - { - tmp=s->val[OPT_BR_X].w; - s->val[OPT_BR_X].w=s->val[OPT_TL_X].w; - s->val[OPT_TL_X].w=tmp; - } - if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w) - { - tmp=s->val[OPT_BR_Y].w; - s->val[OPT_BR_Y].w=s->val[OPT_TL_Y].w; - s->val[OPT_TL_Y].w=tmp; - } - break; - /* word options: */ - case OPT_NUM_OPTS: - case OPT_RESOLUTION: - case OPT_BIT_DEPTH: - case OPT_PREVIEW: - case OPT_THRESHOLD: - case OPT_THRESHOLD_CURVE: - case OPT_DISABLE_DYNAMIC_LINEART: - case OPT_DISABLE_INTERPOLATION: - case OPT_LAMP_OFF: - case OPT_LAMP_OFF_TIME: - case OPT_SWDESKEW: - case OPT_SWCROP: - case OPT_SWDESPECK: - case OPT_SWDEROTATE: - case OPT_SWSKIP: - case OPT_DESPECK: - case OPT_CONTRAST: - case OPT_BRIGHTNESS: - case OPT_EXPIRATION_TIME: - *(SANE_Word *) val = s->val[option].w; - break; - case OPT_CUSTOM_GAMMA: - *(SANE_Word *) val = s->val[option].w; - break; - - /* string options: */ - case OPT_MODE: - case OPT_COLOR_FILTER: - case OPT_CALIBRATION_FILE: - case OPT_SOURCE: - strcpy (val, s->val[option].s); - break; - - /* word array options */ - case OPT_GAMMA_VECTOR: - table = (SANE_Word *) val; - if (strcmp (s->val[OPT_COLOR_FILTER].s, "Red") == 0) - { - gamma = s->dev->sensor.gamma_table[GENESYS_RED]; - } - else if (strcmp (s->val[OPT_COLOR_FILTER].s, "Blue") == 0) - { - gamma = s->dev->sensor.gamma_table[GENESYS_BLUE]; - } - else - { - gamma = s->dev->sensor.gamma_table[GENESYS_GREEN]; - } - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - table[i] = gamma[i]; - } - break; - case OPT_GAMMA_VECTOR_R: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - table[i] = s->dev->sensor.gamma_table[GENESYS_RED][i]; - } - break; - case OPT_GAMMA_VECTOR_G: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - table[i] = s->dev->sensor.gamma_table[GENESYS_GREEN][i]; - } - break; - case OPT_GAMMA_VECTOR_B: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - table[i] = s->dev->sensor.gamma_table[GENESYS_BLUE][i]; - } - break; - /* sensors */ - case OPT_SCAN_SW: - case OPT_FILE_SW: - case OPT_EMAIL_SW: - case OPT_COPY_SW: - case OPT_PAGE_LOADED_SW: - case OPT_OCR_SW: - case OPT_POWER_SW: - case OPT_EXTRA_SW: - RIE (s->dev->model->cmd_set->update_hardware_sensors (s)); - *(SANE_Bool *) val = s->val[option].b; - s->last_val[option].b = *(SANE_Bool *) val; - break; - case OPT_NEED_CALIBRATION_SW: - /* scanner needs calibration for current mode unless a matching - * calibration cache is found */ - *(SANE_Bool *) val = SANE_TRUE; - for (cache = s->dev->calibration_cache; cache; cache = cache->next) - { - if (s->dev->model-> - cmd_set->is_compatible_calibration (s->dev, cache, SANE_FALSE) == SANE_STATUS_GOOD) - { - *(SANE_Bool *) val = SANE_FALSE; - } - } - break; - default: - DBG (DBG_warn, "get_option_value: can't get unknown option %d\n", - option); - } - return status; -} - -/** @brief set calibration file value - * Set calibration file value. Load new cache values from file if it exists, - * else creates the file*/ -static SANE_Status set_calibration_value (Genesys_Scanner * s, int option, void *val) -{ - SANE_Status status=SANE_STATUS_GOOD; - char *tmp; - Genesys_Device *dev=s->dev; - - DBGSTART; - - /* try to load file */ - tmp=dev->calib_file; - dev->calib_file=val; - status=sanei_genesys_read_calibration (dev); - - /* file exists but is invalid, so fall back to previous cache file - * an re-read it */ - if (status!=SANE_STATUS_IO_ERROR && status!=SANE_STATUS_GOOD) - { - dev->calib_file=tmp; - status=sanei_genesys_read_calibration (dev); - return status; - } - - /* now we can set file name value */ - if (s->val[option].s) - free (s->val[option].s); - s->val[option].s = strdup (val); - if (tmp) - free (tmp); - dev->calib_file = strdup (val); - DBG (DBG_info, "%s: Calibration filename set to:\n", __func__); - DBG (DBG_info, "%s: >%s<\n", __func__, s->dev->calib_file); - - DBGCOMPLETED; - return SANE_STATUS_GOOD; -} - -/* sets an option , called by sane_control_option */ -static SANE_Status -set_option_value (Genesys_Scanner * s, int option, void *val, - SANE_Int * myinfo) -{ - SANE_Status status = SANE_STATUS_GOOD; - SANE_Word *table; - unsigned int i; - SANE_Range *x_range, *y_range; - Genesys_Calibration_Cache *cache, *next_cache; - - switch (option) - { - case OPT_TL_X: - case OPT_TL_Y: - case OPT_BR_X: - case OPT_BR_Y: - s->val[option].w = *(SANE_Word *) val; - RIE (calc_parameters (s)); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_RESOLUTION: - case OPT_THRESHOLD: - case OPT_THRESHOLD_CURVE: - case OPT_DISABLE_DYNAMIC_LINEART: - case OPT_SWCROP: - case OPT_SWDESKEW: - case OPT_DESPECK: - case OPT_SWDEROTATE: - case OPT_SWSKIP: - case OPT_DISABLE_INTERPOLATION: - case OPT_LAMP_OFF: - case OPT_PREVIEW: - case OPT_BRIGHTNESS: - case OPT_CONTRAST: - s->val[option].w = *(SANE_Word *) val; - RIE (calc_parameters (s)); - *myinfo |= SANE_INFO_RELOAD_PARAMS; - break; - case OPT_SWDESPECK: - s->val[option].w = *(SANE_Word *) val; - if (s->val[OPT_SWDESPECK].b == SANE_TRUE) - { - ENABLE(OPT_DESPECK); - } - else - { - DISABLE(OPT_DESPECK); - } - RIE (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->val[option].w = *(SANE_Word *) val; - if(s->val[OPT_BIT_DEPTH].w>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); - ENABLE(OPT_CONTRAST); - ENABLE(OPT_BRIGHTNESS); - } - RIE (calc_parameters (s)); - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; - case OPT_SOURCE: - if (strcmp (s->val[option].s, val) != 0) - { /* something changed */ - if (s->val[option].s) - free (s->val[option].s); - s->val[option].s = strdup (val); - - /* change geometry constraint to the new source value */ - if (strcmp (s->val[option].s, FLATBED) == 0) - { - x_range=create_range(s->dev->model->x_size); - y_range=create_range(s->dev->model->y_size); - } - else - { - x_range=create_range(s->dev->model->x_size_ta); - y_range=create_range(s->dev->model->y_size_ta); - } - if(x_range==NULL || y_range==NULL) - { - return SANE_STATUS_NO_MEM; - } - - /* assign new values */ - free((void *)(size_t)s->opt[OPT_TL_X].constraint.range); - free((void *)(size_t)s->opt[OPT_TL_Y].constraint.range); - s->opt[OPT_TL_X].constraint.range = x_range; - s->val[OPT_TL_X].w = 0; - s->opt[OPT_TL_Y].constraint.range = y_range; - s->val[OPT_TL_Y].w = 0; - s->opt[OPT_BR_X].constraint.range = x_range; - s->val[OPT_BR_Y].w = y_range->max; - s->opt[OPT_BR_Y].constraint.range = y_range; - s->val[OPT_BR_X].w = x_range->max; - - /* signals reload */ - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - } - break; - case OPT_MODE: - if (s->val[option].s) - free (s->val[option].s); - s->val[option].s = strdup (val); - - if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0) - { - ENABLE (OPT_THRESHOLD); - ENABLE (OPT_THRESHOLD_CURVE); - DISABLE (OPT_BIT_DEPTH); - if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis) - { - ENABLE (OPT_COLOR_FILTER); - } - ENABLE (OPT_DISABLE_DYNAMIC_LINEART); - } - else - { - DISABLE (OPT_THRESHOLD); - DISABLE (OPT_THRESHOLD_CURVE); - DISABLE (OPT_DISABLE_DYNAMIC_LINEART); - if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_GRAY) == 0) - { - if (s->dev->model->asic_type != GENESYS_GL646 || !s->dev->model->is_cis) - { - ENABLE (OPT_COLOR_FILTER); - } - create_bpp_list (s, s->dev->model->bpp_gray_values); - } - else - { - DISABLE (OPT_COLOR_FILTER); - create_bpp_list (s, s->dev->model->bpp_color_values); - } - if (s->bpp_list[0] < 2) - DISABLE (OPT_BIT_DEPTH); - else - ENABLE (OPT_BIT_DEPTH); - } - RIE (calc_parameters (s)); - - /* if custom gamma, toggle gamma table options according to the mode */ - if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE) - { - if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) - { - DISABLE (OPT_GAMMA_VECTOR); - ENABLE (OPT_GAMMA_VECTOR_R); - ENABLE (OPT_GAMMA_VECTOR_G); - ENABLE (OPT_GAMMA_VECTOR_B); - } - else - { - ENABLE (OPT_GAMMA_VECTOR); - DISABLE (OPT_GAMMA_VECTOR_R); - DISABLE (OPT_GAMMA_VECTOR_G); - DISABLE (OPT_GAMMA_VECTOR_B); - } - } - - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; - case OPT_COLOR_FILTER: - if (s->val[option].s) - free (s->val[option].s); - s->val[option].s = strdup (val); - RIE (calc_parameters (s)); - break; - case OPT_CALIBRATION_FILE: - RIE(set_calibration_value (s, option, val)); - break; - case OPT_LAMP_OFF_TIME: - case OPT_EXPIRATION_TIME: - if (*(SANE_Word *) val != s->val[option].w) - { - s->val[option].w = *(SANE_Word *) val; - RIE (s->dev->model->cmd_set-> - set_powersaving (s->dev, s->val[option].w)); - } - break; - - case OPT_CUSTOM_GAMMA: - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - s->val[OPT_CUSTOM_GAMMA].b = *(SANE_Bool *) val; - - if (s->val[OPT_CUSTOM_GAMMA].b == SANE_TRUE) - { - if (strcmp (s->val[OPT_MODE].s, SANE_VALUE_SCAN_MODE_COLOR) == 0) - { - DISABLE (OPT_GAMMA_VECTOR); - ENABLE (OPT_GAMMA_VECTOR_R); - ENABLE (OPT_GAMMA_VECTOR_G); - ENABLE (OPT_GAMMA_VECTOR_B); - } - else - { - ENABLE (OPT_GAMMA_VECTOR); - DISABLE (OPT_GAMMA_VECTOR_R); - DISABLE (OPT_GAMMA_VECTOR_G); - DISABLE (OPT_GAMMA_VECTOR_B); - } - } - else - { - DISABLE (OPT_GAMMA_VECTOR); - DISABLE (OPT_GAMMA_VECTOR_R); - DISABLE (OPT_GAMMA_VECTOR_G); - DISABLE (OPT_GAMMA_VECTOR_B); - /* restore default sensor gamma table */ - /* currently there is no sensor's specific gamma table, - * tables are built by sanei_genesys_create_gamma_table */ - sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_RED], - s->opt[OPT_GAMMA_VECTOR_R].size / sizeof (SANE_Word), - s->opt[OPT_GAMMA_VECTOR_R].constraint.range->max, - s->opt[OPT_GAMMA_VECTOR_R].constraint.range->max, - s->dev->sensor.gamma[GENESYS_RED]); - sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_GREEN], - s->opt[OPT_GAMMA_VECTOR_G].size / sizeof (SANE_Word), - s->opt[OPT_GAMMA_VECTOR_G].constraint.range->max, - s->opt[OPT_GAMMA_VECTOR_G].constraint.range->max, - s->dev->sensor.gamma[GENESYS_GREEN]); - sanei_genesys_create_gamma_table (s->dev->sensor.gamma_table[GENESYS_BLUE], - s->opt[OPT_GAMMA_VECTOR_B].size / sizeof (SANE_Word), - s->opt[OPT_GAMMA_VECTOR_B].constraint.range->max, - s->opt[OPT_GAMMA_VECTOR_B].constraint.range->max, - s->dev->sensor.gamma[GENESYS_BLUE]); - } - break; - - case OPT_GAMMA_VECTOR: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - s->dev->sensor.gamma_table[GENESYS_RED][i] = table[i]; - s->dev->sensor.gamma_table[GENESYS_GREEN][i] = table[i]; - s->dev->sensor.gamma_table[GENESYS_BLUE][i] = table[i]; - } - break; - case OPT_GAMMA_VECTOR_R: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - s->dev->sensor.gamma_table[GENESYS_RED][i] = table[i]; - } - break; - case OPT_GAMMA_VECTOR_G: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - s->dev->sensor.gamma_table[GENESYS_GREEN][i] = table[i]; - } - break; - case OPT_GAMMA_VECTOR_B: - table = (SANE_Word *) val; - for (i = 0; i < s->opt[option].size / sizeof (SANE_Word); i++) - { - s->dev->sensor.gamma_table[GENESYS_BLUE][i] = table[i]; - } - break; - case OPT_CALIBRATE: - status = s->dev->model->cmd_set->save_power (s->dev, SANE_FALSE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "%s: failed to disable power saving mode: %s\n", - __func__, sane_strstatus (status)); - } - else - status = genesys_scanner_calibration (s->dev); - /* not critical if this fails*/ - s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE); - /* signals that sensors will have to be read again */ - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; - case OPT_CLEAR_CALIBRATION: - /* clear calibration cache */ - if (s->dev->calibration_cache != NULL) - { - for (cache = s->dev->calibration_cache; cache; cache = next_cache) - { - next_cache = cache->next; - free (cache->dark_average_data); - free (cache->white_average_data); - free (cache); - } - } - s->dev->calibration_cache = NULL; - /* remove file */ - unlink (s->dev->calib_file); - /* signals that sensors will have to be read again */ - *myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS; - break; - - default: - DBG (DBG_warn, "set_option_value: can't set unknown option %d\n", - option); - } - return status; -} - - -/* sets and gets scanner option values */ -SANE_Status -sane_control_option (SANE_Handle handle, SANE_Int option, - SANE_Action action, void *val, SANE_Int * info) -{ - Genesys_Scanner *s = handle; - SANE_Status status = SANE_STATUS_GOOD; - SANE_Word cap; - SANE_Int myinfo = 0; - - DBG (DBG_io2, - "sane_control_option: start: action = %s, option = %s (%d)\n", - (action == SANE_ACTION_GET_VALUE) ? "get" : (action == - SANE_ACTION_SET_VALUE) ? - "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown", - s->opt[option].name, option); - - if (info) - *info = 0; - - if (s->scanning) - { - DBG (DBG_warn, "sane_control_option: don't call this function while " - "scanning (option = %s (%d))\n", s->opt[option].name, option); - - return SANE_STATUS_DEVICE_BUSY; - } - if (option >= NUM_OPTIONS || option < 0) - { - DBG (DBG_warn, - "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n", - option); - return SANE_STATUS_INVAL; - } - - cap = s->opt[option].cap; - - if (!SANE_OPTION_IS_ACTIVE (cap)) - { - DBG (DBG_warn, "sane_control_option: option %d is inactive\n", option); - return SANE_STATUS_INVAL; - } - - switch (action) - { - case SANE_ACTION_GET_VALUE: - status = get_option_value (s, option, val); - break; - - case SANE_ACTION_SET_VALUE: - if (!SANE_OPTION_IS_SETTABLE (cap)) - { - DBG (DBG_warn, "sane_control_option: option %d is not settable\n", - option); - return SANE_STATUS_INVAL; - } - - status = sanei_constrain_value (s->opt + option, val, &myinfo); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_warn, - "sane_control_option: sanei_constrain_value returned %s\n", - sane_strstatus (status)); - return status; - } - - status = set_option_value (s, option, val, &myinfo); - break; - - case SANE_ACTION_SET_AUTO: - DBG (DBG_error, - "sane_control_option: SANE_ACTION_SET_AUTO unsupported since no option has SANE_CAP_AUTOMATIC\n"); - status = SANE_STATUS_INVAL; - break; - - default: - DBG (DBG_warn, "sane_control_option: unknown action %d for option %d\n", - action, option); - status = SANE_STATUS_INVAL; - break; - } - - if (info) - *info = myinfo; - - DBG (DBG_io2, "sane_control_option: exit\n"); - return status; -} - - - -SANE_Status -sane_get_parameters (SANE_Handle handle, SANE_Parameters * params) -{ - Genesys_Scanner *s = handle; - SANE_Status status; - - DBGSTART; - - /* don't recompute parameters once data reading is active, ie during scan */ - if(s->dev->read_active == SANE_FALSE) - { - RIE (calc_parameters (s)); - } - if (params) - { - *params = s->params; - - /* in the case of a sheetfed scanner, when full height is specified - * we override the computed line number with -1 to signal that we - * don't know the real document height. - * We don't do that doing buffering image for digital processing - */ - if (s->dev->model->is_sheetfed == SANE_TRUE - && s->dev->buffer_image == SANE_FALSE - && s->val[OPT_BR_Y].w == s->opt[OPT_BR_Y].constraint.range->max) - { - params->lines = -1; - } - } - - DBGCOMPLETED; - - return SANE_STATUS_GOOD; -} - -SANE_Status -sane_start (SANE_Handle handle) -{ - Genesys_Scanner *s = handle; - SANE_Status status=SANE_STATUS_GOOD; - - DBGSTART; - - if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w) - { - DBG (DBG_error0, - "sane_start: top left x >= bottom right x --- exiting\n"); - return SANE_STATUS_INVAL; - } - if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w) - { - DBG (DBG_error0, - "sane_start: top left y >= bottom right y --- exiting\n"); - return SANE_STATUS_INVAL; - } - - /* First make sure we have a current parameter set. Some of the - parameters will be overwritten below, but that's OK. */ - - RIE (calc_parameters (s)); - RIE (genesys_start_scan (s->dev, s->val[OPT_LAMP_OFF].w)); - - s->scanning = SANE_TRUE; - - /* allocate intermediate buffer when doing dynamic lineart */ - if(s->dev->settings.dynamic_lineart==SANE_TRUE) - { - RIE (sanei_genesys_buffer_free (&(s->dev->binarize_buffer))); - RIE (sanei_genesys_buffer_alloc (&(s->dev->binarize_buffer), s->dev->settings.pixels)); - RIE (sanei_genesys_buffer_free (&(s->dev->local_buffer))); - RIE (sanei_genesys_buffer_alloc (&(s->dev->local_buffer), s->dev->binarize_buffer.size * 8)); - } - - /* 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) - { - RIE(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->val[OPT_SWSKIP].w && IS_ACTIVE(OPT_SWSKIP)) - { - status = sanei_magic_isBlank(&s->params, - s->dev->img_buffer, - SANE_UNFIX(s->val[OPT_SWSKIP].w)); - if(status == SANE_STATUS_NO_DOCS) - { - if (s->dev->model->is_sheetfed == SANE_TRUE) - { - DBG (DBG_info, "sane_start: blank page, recurse\n"); - return sane_start(handle); - } - return status; - } - } - - /* deskew image if required */ - if(s->val[OPT_SWDESKEW].b == SANE_TRUE) - { - RIE(genesys_deskew(s)); - } - - /* despeck image if required */ - if(s->val[OPT_SWDESPECK].b == SANE_TRUE) - { - RIE(genesys_despeck(s)); - } - - /* crop image if required */ - if(s->val[OPT_SWCROP].b == SANE_TRUE) - { - RIE(genesys_crop(s)); - } - - /* de-rotate image if required */ - if(s->val[OPT_SWDEROTATE].b == SANE_TRUE) - { - RIE(genesys_derotate(s)); - } - } - - DBGCOMPLETED; - return status; -} - -SANE_Status -sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len, - SANE_Int * len) -{ - Genesys_Scanner *s = handle; - Genesys_Device *dev; - SANE_Status status=SANE_STATUS_GOOD; - size_t local_len; - - if (!s) - { - DBG (DBG_error, "sane_read: handle is null!\n"); - return SANE_STATUS_INVAL; - } - - dev=s->dev; - if (!dev) - { - DBG (DBG_error, "sane_read: dev is null!\n"); - return SANE_STATUS_INVAL; - } - - if (!buf) - { - DBG (DBG_error, "sane_read: buf is null!\n"); - return SANE_STATUS_INVAL; - } - - if (!len) - { - DBG (DBG_error, "sane_read: len is null!\n"); - return SANE_STATUS_INVAL; - } - - *len = 0; - - if (!s->scanning) - { - DBG (DBG_warn, "sane_read: scan was cancelled, is over or has not been " - "initiated yet\n"); - return SANE_STATUS_CANCELLED; - } - - DBG (DBG_proc, "sane_read: start, %d maximum bytes required\n", max_len); - DBG (DBG_io2, "sane_read: bytes_to_read=%lu, total_bytes_read=%lu\n", - (u_long) dev->total_bytes_to_read, (u_long) dev->total_bytes_read); - DBG (DBG_io2, "sane_read: physical bytes to read = %lu\n", (u_long) dev->read_bytes_left); - - if(dev->total_bytes_read>=dev->total_bytes_to_read) - { - DBG (DBG_proc, "sane_read: nothing more to scan: EOF\n"); - - /* issue park command immediatly in case scanner can handle it - * so we save time */ - if (dev->model->is_sheetfed == SANE_FALSE - && !(dev->model->flags & GENESYS_FLAG_MUST_WAIT) - && dev->parking == SANE_FALSE) - { - dev->model->cmd_set->slow_back_home (dev, SANE_FALSE); - dev->parking = SANE_TRUE; - } - 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.dynamic_lineart==SANE_TRUE) - { - /* 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; - status = genesys_read_ordered_data (dev, dev->local_buffer.buffer, &local_len); - - /* binarize data is read successful */ - if(status==SANE_STATUS_GOOD) - { - dev->local_buffer.avail=local_len; - dev->local_buffer.pos=0; - dev->binarize_buffer.avail=local_len/8; - dev->binarize_buffer.pos=0; - genesys_gray_lineart (dev, - dev->local_buffer.buffer, - dev->binarize_buffer.buffer, - dev->settings.pixels, - local_len/dev->settings.pixels, - dev->settings.threshold); - } - - } - - /* return data from lineart buffer if any, up to the available amount */ - local_len = max_len; - if((size_t)max_len>dev->binarize_buffer.avail) - { - local_len=dev->binarize_buffer.avail; - } - if(local_len) - { - memcpy(buf,sanei_genesys_buffer_get_read_pos (&(dev->binarize_buffer)),local_len); - RIE (sanei_genesys_buffer_consume (&(dev->binarize_buffer), local_len)); - } - } - else - { - /* most usual case, direct read of data from scanner */ - status = 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+dev->total_bytes_read,local_len); - dev->total_bytes_read+=local_len; - } - - *len = local_len; - if(local_len>(size_t)max_len) - { - fprintf (stderr, "[genesys] sane_read: returning incorrect length!!\n"); - } - DBG (DBG_proc, "sane_read: %d bytes returned\n", *len); - return status; -} - -void -sane_cancel (SANE_Handle handle) -{ - Genesys_Scanner *s = handle; - SANE_Status status = SANE_STATUS_GOOD; - - DBGSTART; - - /* end binary logging if needed */ - if (s->dev->binary!=NULL) - { - fclose(s->dev->binary); - s->dev->binary=NULL; - } - - s->scanning = SANE_FALSE; - s->dev->read_active = SANE_FALSE; - if(s->dev->img_buffer!=NULL) - { - free(s->dev->img_buffer); - s->dev->img_buffer=NULL; - } - - /* no need to end scan if we are parking the head */ - if(s->dev->parking==SANE_FALSE) - { - status = s->dev->model->cmd_set->end_scan (s->dev, s->dev->reg, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "sane_cancel: failed to end scan: %s\n", - sane_strstatus (status)); - return; - } - } - - /* park head if flatbed scanner */ - if (s->dev->model->is_sheetfed == SANE_FALSE) - { - if(s->dev->parking==SANE_FALSE) - { - status = s->dev->model->cmd_set->slow_back_home (s->dev, s->dev->model->flags & GENESYS_FLAG_MUST_WAIT); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, - "sane_cancel: failed to move scanhead to home position: %s\n", - sane_strstatus (status)); - return; - } - s->dev->parking = !(s->dev->model->flags & GENESYS_FLAG_MUST_WAIT); - } - } - else - { /* in case of sheetfed scanners, we have to eject the document if still present */ - status = s->dev->model->cmd_set->eject_document (s->dev); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "sane_cancel: failed to eject document: %s\n", - sane_strstatus (status)); - return; - } - } - - /* enable power saving mode unless we are parking .... */ - if(s->dev->parking==SANE_FALSE) - { - status = s->dev->model->cmd_set->save_power (s->dev, SANE_TRUE); - if (status != SANE_STATUS_GOOD) - { - DBG (DBG_error, "sane_cancel: failed to enable power saving mode: %s\n", - sane_strstatus (status)); - return; - } - } - - DBGCOMPLETED; - return; -} - -SANE_Status -sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking) -{ - Genesys_Scanner *s = handle; - - DBG (DBG_proc, "sane_set_io_mode: handle = %p, non_blocking = %s\n", - handle, non_blocking == SANE_TRUE ? "true" : "false"); - - if (!s->scanning) - { - DBG (DBG_error, "sane_set_io_mode: not scanning\n"); - return SANE_STATUS_INVAL; - } - if (non_blocking) - return SANE_STATUS_UNSUPPORTED; - return SANE_STATUS_GOOD; -} - -SANE_Status -sane_get_select_fd (SANE_Handle handle, SANE_Int * fd) -{ - Genesys_Scanner *s = handle; - - DBG (DBG_proc, "sane_get_select_fd: handle = %p, fd = %p\n", handle, - (void *) fd); - - if (!s->scanning) - { - DBG (DBG_error, "sane_get_select_fd: not scanning\n"); - return SANE_STATUS_INVAL; - } - return SANE_STATUS_UNSUPPORTED; -} - -/* vim: set sw=2 cino=>2se-1sn-1s{s^-1st0(0u0 smarttab expandtab: */ |