2015-02-20 19:52:08 +00:00
/* sane - Scanner Access Now Easy.
pieusb_specific . c
Copyright ( C ) 2012 - 2015 Jan Vleeshouwers , Michael Rickmann , Klaus Kaempf
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 . */
/* =========================================================================
*
* Various Pieusb backend specific functions
*
* Option handling , configuration file handling , post - processing
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
# define DEBUG_DECLARE_ONLY
# include "pieusb.h"
# include <stdlib.h>
# include <unistd.h>
# include <string.h>
# include "../include/sane/sane.h"
# include "../include/sane/saneopts.h"
# include "../include/sane/sanei_config.h"
# include <errno.h>
# include <math.h>
# include <time.h>
# include "pieusb_usb.h"
# include "pieusb_scancmd.h"
# include "pieusb_buffer.h"
# include "pieusb_specific.h"
/* Pieusb specific */
/* sub to sanei_pieusb_find_device_callback() */
static SANE_Status pieusb_initialize_device_definition ( Pieusb_Device_Definition * dev , Pieusb_Scanner_Properties * inq , const char * devicename , SANE_Word vendor_id , SANE_Word product_id ) ;
static void pieusb_print_inquiry ( Pieusb_Device_Definition * dev ) ;
/* sub to sane_start() */
static void pieusb_calculate_shading ( struct Pieusb_Scanner * scanner , SANE_Byte * buffer ) ;
/* MR */
/* sub to sanei_pieusb_post() */
static SANE_Status pieusb_write_pnm_file ( char * filename , uint16_t * data , int depth , int channels , int pixels_per_line , int lines ) ;
/* Auxilary */
static size_t max_string_size ( SANE_String_Const const strings [ ] ) ;
static double getGain ( int gain ) ;
static int getGainSetting ( double gain ) ;
/*
static void updateGain ( Pieusb_Scanner * scanner , int color_index ) ;
*/
static void updateGain2 ( Pieusb_Scanner * scanner , int color_index , double gain_increase ) ;
/* --------------------------------------------------------------------------
*
* SPECIFIC PIEUSB
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Settings for byte order */
# define SCAN_IMG_FMT_OKLINE 0x08
# define SCAN_IMG_FMT_BLK_ONE 0x04
# define SCAN_IMG_FMT_MOTOROLA 0x02
# define SCAN_IMG_FMT_INTEL 0x01
/* Settings for scanner capabilities */
# define SCAN_CAP_PWRSAV 0x80
# define SCAN_CAP_EXT_CAL 0x40
# define SCAN_CAP_FAST_PREVIEW 0x10
# define SCAN_CAP_DISABLE_CAL 0x08
# define SCAN_CAP_SPEEDS 0x07
/* Available scanner options */
# define SCAN_OPT_DEV_MPCL 0x80
# define SCAN_OPT_DEV_TP1 0x04
# define SCAN_OPT_DEV_TP 0x02
# define SCAN_OPT_DEV_ADF 0x01
/* Options */
# define SANE_NAME_EXPOSURE_R "exposure-time-r"
# define SANE_TITLE_EXPOSURE_R "Exposure time red"
# define SANE_DESC_EXPOSURE_R "The time the red color filter of the CCD is exposed"
# define SANE_NAME_EXPOSURE_G "exposure-time-g"
# define SANE_TITLE_EXPOSURE_G "Exposure time green"
# define SANE_DESC_EXPOSURE_G "The time the green color filter of the CCD is exposed"
# define SANE_NAME_EXPOSURE_B "exposure-time-b"
# define SANE_TITLE_EXPOSURE_B "Exposure time blue"
# define SANE_DESC_EXPOSURE_B "The time the blue color filter of the CCD is exposed"
# define SANE_NAME_EXPOSURE_I "exposure-time-i"
# define SANE_TITLE_EXPOSURE_I "Exposure time infrared"
# define SANE_DESC_EXPOSURE_I "The time the infrared color filter of the CCD is exposed"
# define SANE_EXPOSURE_DEFAULT DEFAULT_EXPOSURE
# if 1
# define SANE_NAME_GAIN_R "gain-r"
# define SANE_TITLE_GAIN_R "Gain red"
# define SANE_DESC_GAIN_R "The gain of the signal processor for red"
# define SANE_NAME_GAIN_G "gain-g"
# define SANE_TITLE_GAIN_G "Gain green"
# define SANE_DESC_GAIN_G "The gain of the signal processor for green"
# define SANE_NAME_GAIN_B "gain-b"
# define SANE_TITLE_GAIN_B "Gain blue"
# define SANE_DESC_GAIN_B "The gain of the signal processor for blue"
# define SANE_NAME_GAIN_I "gain-i"
# define SANE_TITLE_GAIN_I "Gain infrared"
# define SANE_DESC_GAIN_I "The gain of the signal processor for infrared"
# define SANE_GAIN_DEFAULT DEFAULT_GAIN
# define SANE_NAME_OFFSET_R "offset-r"
# define SANE_TITLE_OFFSET_R "Offset red"
# define SANE_DESC_OFFSET_R "The offset of the signal processor for red"
# define SANE_NAME_OFFSET_G "offset-g"
# define SANE_TITLE_OFFSET_G "Offset greed"
# define SANE_DESC_OFFSET_G "The offset of the signal processor for green"
# define SANE_NAME_OFFSET_B "offset-b"
# define SANE_TITLE_OFFSET_B "Offset blue"
# define SANE_DESC_OFFSET_B "The offset of the signal processor for blue"
# define SANE_NAME_OFFSET_I "offset-i"
# define SANE_TITLE_OFFSET_I "Offset infrared"
# define SANE_DESC_OFFSET_I "The offset of the signal processor for infrared"
# define SANE_OFFSET_DEFAULT DEFAULT_OFFSET
# else
# define SANE_NAME_GAIN "gain"
# define SANE_TITLE_GAIN "Gain"
# define SANE_DESC_GAIN "The gain of the signal processor for the 4 CCD color filters (R,G,B,I)"
# define SANE_GAIN_DEFAULT 0x13
# define SANE_NAME_OFFSET "offset"
# define SANE_TITLE_OFFSET "Offset"
# define SANE_DESC_OFFSET "The offset of the signal processor for the 4 CCD color filters (R,G,B,I)"
# define SANE_OFFSET_DEFAULT 0
# endif
# define min(a,b) (((a)<(b))?(a):(b))
# define max(a,b) (((a)>(b))?(a):(b))
static const SANE_Range percentage_range_100 = {
0 < < SANE_FIXED_SCALE_SHIFT , /* minimum */
100 < < SANE_FIXED_SCALE_SHIFT , /* maximum */
0 < < SANE_FIXED_SCALE_SHIFT /* quantization */
} ;
/* From the firmware disassembly */
static const SANE_Range gain_range = {
0 , /* minimum */
63 , /* maximum */
0 /* quantization */
} ;
/* From the firmware disassembly */
static const SANE_Range offset_range = {
0 , /* minimum */
255 , /* maximum */
0 /* quantization */
} ;
static const double gains [ ] = {
1.000 , 1.075 , 1.154 , 1.251 , 1.362 , 1.491 , 1.653 , /* 0, 5, 10, 15, 20, 25, 30 */
1.858 , 2.115 , 2.458 , 2.935 , 3.638 , 4.627 /* 35, 40, 45, 50, 55, 60 */
} ;
/**
* Callback called whenever a connected USB device reports a supported vendor
* and product id combination .
* Used by sane_init ( ) and by sane_open ( ) .
*
* @ param name Device name which has required vendor and product id
* @ return SANE_STATUS_GOOD
*/
SANE_Status
sanei_pieusb_find_device_callback ( const char * devicename )
{
struct Pieusb_Command_Status status ;
SANE_Status r ;
Pieusb_Device_Definition * dev ;
int device_number ; /* index in usb devices list maintained by sani_usb */
Pieusb_Scanner_Properties inq ;
int retry ;
DBG ( DBG_info_proc , " sanei_pieusb_find_device_callback: %s \n " , devicename ) ;
/* Check if device is present in the Pieusb device list */
for ( dev = pieusb_definition_list_head ; dev ; dev = dev - > next ) {
if ( strcmp ( dev - > sane . name , devicename ) = = 0 ) {
return SANE_STATUS_GOOD ;
}
}
/* If not, create a new device struct */
dev = malloc ( sizeof ( * dev ) ) ;
if ( ! dev ) {
return SANE_STATUS_NO_MEM ;
}
/* Get device number: index of the device in the sanei_usb devices list */
r = sanei_usb_open ( devicename , & device_number ) ;
if ( r ! = SANE_STATUS_GOOD ) {
free ( dev ) ;
DBG ( DBG_error , " sanei_pieusb_find_device_callback: sanei_usb_open failed for device %s: %s \n " , devicename , sane_strstatus ( r ) ) ;
return r ;
}
/* Get device properties */
retry = 2 ;
while ( retry > 0 ) {
retry - - ;
/* get inquiry data length */
sanei_pieusb_cmd_inquiry ( device_number , & inq , 5 , & status ) ;
if ( status . pieusb_status = = PIEUSB_STATUS_GOOD ) {
break ;
}
else if ( status . pieusb_status = = PIEUSB_STATUS_IO_ERROR ) {
if ( retry > 0 ) {
DBG ( DBG_info_proc , " inquiry failed, resetting usb \n " ) ;
if ( sanei_pieusb_usb_reset ( device_number ) = = SANE_STATUS_GOOD ) {
continue ; /* retry after IEEE1284 reset */
}
if ( sanei_usb_reset ( device_number ) = = SANE_STATUS_GOOD ) {
continue ; /* retry after USB reset */
}
}
}
free ( dev ) ;
DBG ( DBG_error , " sanei_pieusb_find_device_callback: get scanner properties (5 bytes) failed with %d \n " , status . pieusb_status ) ;
sanei_usb_close ( device_number ) ;
return status . pieusb_status ;
}
/* get full inquiry data */
sanei_pieusb_cmd_inquiry ( device_number , & inq , inq . additionalLength + 4 , & status ) ;
if ( status . pieusb_status ! = PIEUSB_STATUS_GOOD ) {
free ( dev ) ;
DBG ( DBG_error , " sanei_pieusb_find_device_callback: get scanner properties failed \n " ) ;
sanei_usb_close ( device_number ) ;
return status . pieusb_status ;
}
/* Close the device again */
sanei_usb_close ( device_number ) ;
/* Initialize device definition */
r = pieusb_initialize_device_definition ( dev , & inq , devicename , pieusb_supported_usb_device . vendor , pieusb_supported_usb_device . product ) ;
if ( r ! = SANE_STATUS_GOOD ) {
return r ;
}
/* Output */
pieusb_print_inquiry ( dev ) ;
/* Check model number */
if ( inq . model ! = pieusb_supported_usb_device . model ) {
free ( dev ) ;
DBG ( DBG_error , " sanei_pieusb_find_device_callback: wrong model number %d \n " , inq . model ) ;
return SANE_STATUS_INVAL ;
}
2017-05-03 15:52:15 +00:00
dev - > flags = pieusb_supported_usb_device . flags ;
2015-02-20 19:52:08 +00:00
/* Found a supported scanner, put it in the definitions list*/
DBG ( DBG_info_proc , " sanei_pieusb_find_device_callback: success \n " ) ;
dev - > next = pieusb_definition_list_head ;
pieusb_definition_list_head = dev ;
return SANE_STATUS_GOOD ;
}
/**
* Full initialization of a Pieusb_Device structure from INQUIRY data .
* The function is used in find_device_callback ( ) , so when sane_init ( ) or
* sane_open ( ) is called .
*
* @ param dev
*/
static SANE_Status
pieusb_initialize_device_definition ( Pieusb_Device_Definition * dev , Pieusb_Scanner_Properties * inq , const char * devicename ,
SANE_Word vendor_id , SANE_Word product_id )
{
char * pp , * buf ;
/* Initialize device definition */
dev - > next = NULL ;
dev - > sane . name = strdup ( devicename ) ;
/* Create 0-terminated string without trailing spaces for vendor */
buf = malloc ( 9 ) ;
if ( buf = = NULL )
return SANE_STATUS_NO_MEM ;
strncpy ( buf , inq - > vendor , 8 ) ;
pp = buf + 8 ;
* pp - - = ' \0 ' ;
while ( * pp = = ' ' ) * pp - - = ' \0 ' ;
dev - > sane . vendor = buf ;
/* Create 0-terminated string without trailing spaces for model */
buf = malloc ( 17 ) ;
if ( buf = = NULL )
return SANE_STATUS_NO_MEM ;
strncpy ( buf , inq - > product , 16 ) ;
pp = buf + 16 ;
* pp - - = ' \0 ' ;
while ( * pp = = ' ' ) * pp - - = ' \0 ' ;
dev - > sane . model = buf ;
dev - > sane . type = " film scanner " ;
dev - > vendorId = vendor_id ;
dev - > productId = product_id ;
/* Create 0-terminated strings without trailing spaces for revision */
buf = malloc ( 5 ) ;
if ( buf = = NULL )
return SANE_STATUS_NO_MEM ;
strncpy ( buf , inq - > productRevision , 4 ) ;
pp = buf + 4 ;
* pp - - = ' \0 ' ;
while ( * pp = = ' ' ) * pp - - = ' \0 ' ;
dev - > version = buf ;
dev - > model = inq - > model ;
/* Maximum resolution values */
dev - > maximum_resolution_x = inq - > maxResolutionX ;
dev - > maximum_resolution_y = inq - > maxResolutionY ;
if ( dev - > maximum_resolution_y < 256 ) {
/* y res is a multiplier */
dev - > maximum_resolution = dev - > maximum_resolution_x ;
dev - > maximum_resolution_x * = dev - > maximum_resolution_y ;
dev - > maximum_resolution_y = dev - > maximum_resolution_x ;
} else {
/* y res really is resolution */
dev - > maximum_resolution = min ( dev - > maximum_resolution_x , dev - > maximum_resolution_y ) ;
}
/* Geometry */
dev - > scan_bed_width = ( double ) inq - > maxScanWidth / dev - > maximum_resolution ;
dev - > scan_bed_height = ( double ) inq - > maxScanHeight / dev - > maximum_resolution ;
dev - > slide_top_left_x = inq - > x0 ;
dev - > slide_top_left_y = inq - > y0 ;
dev - > slide_width = ( double ) ( inq - > x1 - inq - > x0 ) / dev - > maximum_resolution ;
dev - > slide_height = ( double ) ( inq - > y1 - inq - > y0 ) / dev - > maximum_resolution ;
/* Integer and bit-encoded properties */
dev - > halftone_patterns = inq - > halftones & 0x0f ;
dev - > color_filters = inq - > filters ;
dev - > color_depths = inq - > colorDepths ;
dev - > color_formats = inq - > colorFormat ;
dev - > image_formats = inq - > imageFormat ;
dev - > scan_capabilities = inq - > scanCapability ;
dev - > optional_devices = inq - > optionalDevices ;
dev - > enhancements = inq - > enhancements ;
dev - > gamma_bits = inq - > gammaBits ;
dev - > fast_preview_resolution = inq - > previewScanResolution ;
dev - > minimum_highlight = inq - > minumumHighlight ;
dev - > maximum_shadow = inq - > maximumShadow ;
dev - > calibration_equation = inq - > calibrationEquation ;
dev - > minimum_exposure = inq - > minimumExposure ;
dev - > maximum_exposure = inq - > maximumExposure * 4 ; /* *4 to solve the strange situation that the default value is out of range */
dev - > x0 = inq - > x0 ;
dev - > y0 = inq - > y0 ;
dev - > x1 = inq - > x1 ;
dev - > y1 = inq - > y1 ;
dev - > production = strndup ( inq - > production , 4 ) ;
dev - > timestamp = strndup ( inq - > timestamp , 20 ) ;
dev - > signature = ( char * ) strndup ( ( char * ) inq - > signature , 40 ) ;
/* Ranges for various quantities */
dev - > x_range . min = SANE_FIX ( 0 ) ;
dev - > x_range . quant = SANE_FIX ( 0 ) ;
dev - > x_range . max = SANE_FIX ( dev - > scan_bed_width * MM_PER_INCH ) ;
dev - > y_range . min = SANE_FIX ( 0 ) ;
dev - > y_range . quant = SANE_FIX ( 0 ) ;
dev - > y_range . max = SANE_FIX ( dev - > scan_bed_height * MM_PER_INCH ) ;
dev - > dpi_range . min = SANE_FIX ( 25 ) ;
dev - > dpi_range . quant = SANE_FIX ( 1 ) ;
dev - > dpi_range . max = SANE_FIX ( max ( dev - > maximum_resolution_x , dev - > maximum_resolution_y ) ) ;
dev - > shadow_range . min = SANE_FIX ( 0 ) ;
dev - > shadow_range . quant = SANE_FIX ( 1 ) ;
dev - > shadow_range . max = SANE_FIX ( dev - > maximum_shadow ) ;
dev - > highlight_range . min = SANE_FIX ( dev - > minimum_highlight ) ;
dev - > highlight_range . quant = SANE_FIX ( 1 ) ;
dev - > highlight_range . max = SANE_FIX ( 100 ) ;
dev - > exposure_range . min = dev - > minimum_exposure ;
dev - > exposure_range . quant = 1 ;
dev - > exposure_range . max = dev - > maximum_exposure ;
dev - > dust_range . min = 0 ;
dev - > dust_range . quant = 1 ;
dev - > dust_range . max = 100 ;
/* Enumerated ranges vor various quantities */
/*TODO: create from inq->filters */
dev - > scan_mode_list [ 0 ] = SANE_VALUE_SCAN_MODE_LINEART ;
dev - > scan_mode_list [ 1 ] = SANE_VALUE_SCAN_MODE_HALFTONE ;
dev - > scan_mode_list [ 2 ] = SANE_VALUE_SCAN_MODE_GRAY ;
dev - > scan_mode_list [ 3 ] = SANE_VALUE_SCAN_MODE_COLOR ;
dev - > scan_mode_list [ 4 ] = SANE_VALUE_SCAN_MODE_RGBI ;
dev - > scan_mode_list [ 5 ] = 0 ;
dev - > calibration_mode_list [ 0 ] = SCAN_CALIBRATION_DEFAULT ;
dev - > calibration_mode_list [ 1 ] = SCAN_CALIBRATION_AUTO ;
dev - > calibration_mode_list [ 2 ] = SCAN_CALIBRATION_PREVIEW ;
dev - > calibration_mode_list [ 3 ] = SCAN_CALIBRATION_OPTIONS ;
dev - > calibration_mode_list [ 4 ] = 0 ;
dev - > gain_adjust_list [ 0 ] = SCAN_GAIN_ADJUST_03 ;
dev - > gain_adjust_list [ 1 ] = SCAN_GAIN_ADJUST_05 ;
dev - > gain_adjust_list [ 2 ] = SCAN_GAIN_ADJUST_08 ;
dev - > gain_adjust_list [ 3 ] = SCAN_GAIN_ADJUST_10 ;
dev - > gain_adjust_list [ 4 ] = SCAN_GAIN_ADJUST_12 ;
dev - > gain_adjust_list [ 5 ] = SCAN_GAIN_ADJUST_16 ;
dev - > gain_adjust_list [ 6 ] = SCAN_GAIN_ADJUST_19 ;
dev - > gain_adjust_list [ 7 ] = SCAN_GAIN_ADJUST_24 ;
dev - > gain_adjust_list [ 8 ] = SCAN_GAIN_ADJUST_30 ;
dev - > gain_adjust_list [ 9 ] = 0 ;
/*TODO: create from inq->colorDepths? Maybe not: didn't experiment with
* 4 and 12 bit depths . Don ; t know how they behave . */
dev - > bpp_list [ 0 ] = 3 ; /* count */
dev - > bpp_list [ 1 ] = 1 ;
dev - > bpp_list [ 2 ] = 8 ;
dev - > bpp_list [ 3 ] = 16 ;
/* Infrared */
dev - > ir_sw_list [ 0 ] = " None " ;
dev - > ir_sw_list [ 1 ] = " Reduce red overlap " ;
dev - > ir_sw_list [ 2 ] = " Remove dirt " ;
dev - > ir_sw_list [ 3 ] = 0 ;
dev - > grain_sw_list [ 0 ] = 4 ;
dev - > grain_sw_list [ 1 ] = 0 ;
dev - > grain_sw_list [ 2 ] = 1 ;
dev - > grain_sw_list [ 3 ] = 2 ;
dev - > grain_sw_list [ 4 ] = 3 ;
dev - > grain_sw_list [ 5 ] = 0 ;
dev - > crop_sw_list [ 0 ] = " None " ;
dev - > crop_sw_list [ 1 ] = " Outside " ;
dev - > crop_sw_list [ 2 ] = " Inside " ;
dev - > crop_sw_list [ 3 ] = 0 ;
/* halftone_list */
dev - > halftone_list [ 0 ] = " 53lpi 45d ROUND " ; /* 8x8 pattern */
dev - > halftone_list [ 1 ] = " 70lpi 45d ROUND " ; /* 6x6 pattern */
dev - > halftone_list [ 2 ] = " 75lpi Hori. Line " ; /* 4x4 pattern */
dev - > halftone_list [ 3 ] = " 4X4 BAYER " ; /* 4x4 pattern */
dev - > halftone_list [ 4 ] = " 4X4 SCROLL " ; /* 4x4 pattern */
dev - > halftone_list [ 5 ] = " 5x5 26 Levels " ; /* 5x5 pattern */
dev - > halftone_list [ 6 ] = " 4x4 SQUARE " ; /* 4x4 pattern */
dev - > halftone_list [ 7 ] = " 5x5 TILE " ; /* 5x5 pattern */
dev - > halftone_list [ 8 ] = 0 ;
return SANE_STATUS_GOOD ;
}
/**
* Output device definition .
* The function is used in find_device_callback ( ) , so when sane_init ( ) or
* sane_open ( ) is called .
*
* @ param dev Device to output
*/
static void
pieusb_print_inquiry ( Pieusb_Device_Definition * dev )
{
DBG ( DBG_inquiry , " INQUIRY: \n " ) ;
DBG ( DBG_inquiry , " ======== \n " ) ;
DBG ( DBG_inquiry , " \n " ) ;
DBG ( DBG_inquiry , " vendor........................: '%s' \n " , dev - > sane . vendor ) ;
DBG ( DBG_inquiry , " product.......................: '%s' \n " , dev - > sane . model ) ;
DBG ( DBG_inquiry , " model .......................: 0x%04x \n " , dev - > model ) ;
DBG ( DBG_inquiry , " version.......................: '%s' \n " , dev - > version ) ;
DBG ( DBG_inquiry , " X resolution..................: %d dpi \n " ,
dev - > maximum_resolution_x ) ;
DBG ( DBG_inquiry , " Y resolution..................: %d dpi \n " ,
dev - > maximum_resolution_y ) ;
DBG ( DBG_inquiry , " pixel resolution..............: %d dpi \n " ,
dev - > maximum_resolution ) ;
DBG ( DBG_inquiry , " fb width......................: %f in \n " ,
dev - > scan_bed_width ) ;
DBG ( DBG_inquiry , " fb length.....................: %f in \n " ,
dev - > scan_bed_height ) ;
DBG ( DBG_inquiry , " transparency width............: %f in \n " ,
dev - > slide_width ) ;
DBG ( DBG_inquiry , " transparency length...........: %f in \n " ,
dev - > slide_height ) ;
DBG ( DBG_inquiry , " transparency offset...........: %d,%d \n " ,
dev - > slide_top_left_x , dev - > slide_top_left_y ) ;
DBG ( DBG_inquiry , " # of halftones................: %d \n " ,
dev - > halftone_patterns ) ;
DBG ( DBG_inquiry , " One pass color................: %s \n " ,
dev - > color_filters & SCAN_ONE_PASS_COLOR ? " yes " : " no " ) ;
DBG ( DBG_inquiry , " Filters.......................: %s%s%s%s%s (%02x) \n " ,
dev - > color_filters & SCAN_FILTER_INFRARED ? " Infrared " : " " ,
dev - > color_filters & SCAN_FILTER_RED ? " Red " : " " ,
dev - > color_filters & SCAN_FILTER_GREEN ? " Green " : " " ,
dev - > color_filters & SCAN_FILTER_BLUE ? " Blue " : " " ,
dev - > color_filters & SCAN_FILTER_NEUTRAL ? " Neutral " : " " ,
dev - > color_filters ) ;
DBG ( DBG_inquiry , " Color depths..................: %s%s%s%s%s%s (%02x) \n " ,
dev - > color_depths & SCAN_COLOR_DEPTH_16 ? " 16 bit " : " " ,
dev - > color_depths & SCAN_COLOR_DEPTH_12 ? " 12 bit " : " " ,
dev - > color_depths & SCAN_COLOR_DEPTH_10 ? " 10 bit " : " " ,
dev - > color_depths & SCAN_COLOR_DEPTH_8 ? " 8 bit " : " " ,
dev - > color_depths & SCAN_COLOR_DEPTH_4 ? " 4 bit " : " " ,
dev - > color_depths & SCAN_COLOR_DEPTH_1 ? " 1 bit " : " " ,
dev - > color_depths ) ;
DBG ( DBG_inquiry , " Color Format..................: %s%s%s (%02x) \n " ,
dev - > color_formats & SCAN_COLOR_FORMAT_INDEX ? " Indexed " : " " ,
dev - > color_formats & SCAN_COLOR_FORMAT_LINE ? " Line " : " " ,
dev - > color_formats & SCAN_COLOR_FORMAT_PIXEL ? " Pixel " : " " ,
dev - > color_formats ) ;
DBG ( DBG_inquiry , " Image Format..................: %s%s%s%s (%02x) \n " ,
dev - > image_formats & SCAN_IMG_FMT_OKLINE ? " OKLine " : " " ,
dev - > image_formats & SCAN_IMG_FMT_BLK_ONE ? " BlackOne " : " " ,
dev - > image_formats & SCAN_IMG_FMT_MOTOROLA ? " Motorola " : " " ,
dev - > image_formats & SCAN_IMG_FMT_INTEL ? " Intel " : " " ,
dev - > image_formats ) ;
DBG ( DBG_inquiry ,
" Scan Capability...............: %s%s%s%s%d speeds (%02x) \n " ,
dev - > scan_capabilities & SCAN_CAP_PWRSAV ? " PowerSave " : " " ,
dev - > scan_capabilities & SCAN_CAP_EXT_CAL ? " ExtCal " : " " ,
dev - > scan_capabilities & SCAN_CAP_FAST_PREVIEW ? " FastPreview " :
" " ,
dev - > scan_capabilities & SCAN_CAP_DISABLE_CAL ? " DisCal " : " " ,
dev - > scan_capabilities & SCAN_CAP_SPEEDS ,
dev - > scan_capabilities ) ;
DBG ( DBG_inquiry , " Optional Devices..............: %s%s%s%s (%02x) \n " ,
dev - > optional_devices & SCAN_OPT_DEV_MPCL ? " MultiPageLoad " :
" " ,
dev - > optional_devices & SCAN_OPT_DEV_TP1 ? " TransModule1 " : " " ,
dev - > optional_devices & SCAN_OPT_DEV_TP ? " TransModule " : " " ,
dev - > optional_devices & SCAN_OPT_DEV_ADF ? " ADF " : " " ,
dev - > optional_devices ) ;
DBG ( DBG_inquiry , " Enhancement...................: %02x \n " ,
dev - > enhancements ) ;
DBG ( DBG_inquiry , " Gamma bits....................: %d \n " ,
dev - > gamma_bits ) ;
DBG ( DBG_inquiry , " Fast Preview Resolution.......: %d \n " ,
dev - > fast_preview_resolution ) ;
DBG ( DBG_inquiry , " Min Highlight.................: %d \n " ,
dev - > minimum_highlight ) ;
DBG ( DBG_inquiry , " Max Shadow....................: %d \n " ,
dev - > maximum_shadow ) ;
DBG ( DBG_inquiry , " Cal Eqn.......................: %d \n " ,
dev - > calibration_equation ) ;
DBG ( DBG_inquiry , " Min Exposure..................: %d \n " ,
dev - > minimum_exposure ) ;
DBG ( DBG_inquiry , " Max Exposure..................: %d \n " ,
dev - > maximum_exposure ) ;
DBG ( DBG_inquiry , " x0,y0 x1,y1...................: %d,%d %d,%d \n " ,
dev - > x0 , dev - > y0 , dev - > x1 , dev - > y1 ) ;
DBG ( DBG_inquiry , " production....................: '%s' \n " ,
dev - > production ) ;
DBG ( DBG_inquiry , " timestamp.....................: '%s' \n " ,
dev - > timestamp ) ;
DBG ( DBG_inquiry , " signature.....................: '%s' \n " ,
dev - > signature ) ;
}
/**
* Initiaize scanner options from the device definition and from exposure ,
* gain and offset defaults . The function is called by sane_open ( ) , when no
* optimized settings are available yet . The scanner object is fully
* initialized in sane_start ( ) .
*
* @ param scanner Scanner to initialize
* @ return SANE_STATUS_GOOD
*/
SANE_Status
sanei_pieusb_init_options ( Pieusb_Scanner * scanner )
{
int i ;
DBG ( DBG_info_proc , " sanei_pieusb_init_options \n " ) ;
memset ( scanner - > opt , 0 , sizeof ( scanner - > opt ) ) ;
memset ( scanner - > val , 0 , sizeof ( scanner - > val ) ) ;
for ( i = 0 ; i < NUM_OPTIONS ; + + i ) {
scanner - > opt [ i ] . size = sizeof ( SANE_Word ) ;
scanner - > opt [ i ] . cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT ;
}
/* Number of options (a pseudo-option) */
scanner - > opt [ OPT_NUM_OPTS ] . name = SANE_NAME_NUM_OPTIONS ;
scanner - > opt [ OPT_NUM_OPTS ] . title = SANE_TITLE_NUM_OPTIONS ;
scanner - > opt [ OPT_NUM_OPTS ] . desc = SANE_DESC_NUM_OPTIONS ;
scanner - > opt [ OPT_NUM_OPTS ] . type = SANE_TYPE_INT ;
scanner - > opt [ OPT_NUM_OPTS ] . cap = SANE_CAP_SOFT_DETECT ;
scanner - > val [ OPT_NUM_OPTS ] . w = NUM_OPTIONS ;
/* "Mode" group: */
scanner - > opt [ OPT_MODE_GROUP ] . title = " Scan Mode " ;
scanner - > opt [ OPT_MODE_GROUP ] . desc = " " ;
scanner - > opt [ OPT_MODE_GROUP ] . type = SANE_TYPE_GROUP ;
scanner - > opt [ OPT_MODE_GROUP ] . cap = 0 ;
scanner - > opt [ OPT_MODE_GROUP ] . constraint_type = SANE_CONSTRAINT_NONE ;
/* scan mode */
scanner - > opt [ OPT_MODE ] . name = SANE_NAME_SCAN_MODE ;
scanner - > opt [ OPT_MODE ] . title = SANE_TITLE_SCAN_MODE ;
scanner - > opt [ OPT_MODE ] . desc = SANE_DESC_SCAN_MODE ;
scanner - > opt [ OPT_MODE ] . type = SANE_TYPE_STRING ;
scanner - > opt [ OPT_MODE ] . size = max_string_size ( ( SANE_String_Const const * ) scanner - > device - > scan_mode_list ) ;
scanner - > opt [ OPT_MODE ] . constraint_type = SANE_CONSTRAINT_STRING_LIST ;
scanner - > opt [ OPT_MODE ] . constraint . string_list = ( SANE_String_Const const * ) scanner - > device - > scan_mode_list ;
scanner - > val [ OPT_MODE ] . s = ( SANE_Char * ) strdup ( scanner - > device - > scan_mode_list [ 3 ] ) ; /* default RGB */
/* bit depth */
scanner - > opt [ OPT_BIT_DEPTH ] . name = SANE_NAME_BIT_DEPTH ;
scanner - > opt [ OPT_BIT_DEPTH ] . title = SANE_TITLE_BIT_DEPTH ;
scanner - > opt [ OPT_BIT_DEPTH ] . desc = SANE_DESC_BIT_DEPTH ;
scanner - > opt [ OPT_BIT_DEPTH ] . type = SANE_TYPE_INT ;
scanner - > opt [ OPT_BIT_DEPTH ] . constraint_type = SANE_CONSTRAINT_WORD_LIST ;
scanner - > opt [ OPT_BIT_DEPTH ] . size = sizeof ( SANE_Word ) ;
scanner - > opt [ OPT_BIT_DEPTH ] . constraint . word_list = scanner - > device - > bpp_list ;
scanner - > val [ OPT_BIT_DEPTH ] . w = scanner - > device - > bpp_list [ 2 ] ;
/* resolution */
scanner - > opt [ OPT_RESOLUTION ] . name = SANE_NAME_SCAN_RESOLUTION ;
scanner - > opt [ OPT_RESOLUTION ] . title = SANE_TITLE_SCAN_RESOLUTION ;
scanner - > opt [ OPT_RESOLUTION ] . desc = SANE_DESC_SCAN_RESOLUTION ;
scanner - > opt [ OPT_RESOLUTION ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_RESOLUTION ] . unit = SANE_UNIT_DPI ;
scanner - > opt [ OPT_RESOLUTION ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_RESOLUTION ] . constraint . range = & scanner - > device - > dpi_range ;
scanner - > val [ OPT_RESOLUTION ] . w = scanner - > device - > fast_preview_resolution < < SANE_FIXED_SCALE_SHIFT ;
/* halftone pattern */
scanner - > opt [ OPT_HALFTONE_PATTERN ] . name = SANE_NAME_HALFTONE_PATTERN ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . title = SANE_TITLE_HALFTONE_PATTERN ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . desc = SANE_DESC_HALFTONE_PATTERN ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . type = SANE_TYPE_STRING ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . size = max_string_size ( ( SANE_String_Const const * ) scanner - > device - > halftone_list ) ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . constraint_type = SANE_CONSTRAINT_STRING_LIST ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . constraint . string_list = ( SANE_String_Const const * ) scanner - > device - > halftone_list ;
scanner - > val [ OPT_HALFTONE_PATTERN ] . s = ( SANE_Char * ) strdup ( scanner - > device - > halftone_list [ 6 ] ) ;
scanner - > opt [ OPT_HALFTONE_PATTERN ] . cap | = SANE_CAP_INACTIVE ; /* Not implemented, and only meaningful at depth 1 */
/* lineart threshold */
scanner - > opt [ OPT_THRESHOLD ] . name = SANE_NAME_THRESHOLD ;
scanner - > opt [ OPT_THRESHOLD ] . title = SANE_TITLE_THRESHOLD ;
scanner - > opt [ OPT_THRESHOLD ] . desc = SANE_DESC_THRESHOLD ;
scanner - > opt [ OPT_THRESHOLD ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_THRESHOLD ] . unit = SANE_UNIT_PERCENT ;
scanner - > opt [ OPT_THRESHOLD ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_THRESHOLD ] . constraint . range = & percentage_range_100 ;
scanner - > val [ OPT_THRESHOLD ] . w = SANE_FIX ( 50 ) ;
/* scanner->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE; Not implemented, and only meaningful at depth 1 */
/* create a sharper scan at the cost of scan time */
scanner - > opt [ OPT_SHARPEN ] . name = " sharpen " ;
scanner - > opt [ OPT_SHARPEN ] . title = " Sharpen scan " ;
scanner - > opt [ OPT_SHARPEN ] . desc = " Sharpen scan by taking more time to discharge the CCD. " ;
scanner - > opt [ OPT_SHARPEN ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_SHARPEN ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ OPT_SHARPEN ] . constraint_type = SANE_CONSTRAINT_NONE ;
scanner - > val [ OPT_SHARPEN ] . b = SANE_FALSE ;
scanner - > opt [ OPT_SHARPEN ] . cap | = SANE_CAP_SOFT_SELECT ;
/* skip the auto-calibration phase before the scan */
scanner - > opt [ OPT_SHADING_ANALYSIS ] . name = " shading-analysis " ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . title = " Perform shading analysis " ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . desc = " Collect shading reference data before scanning the image. If set to 'no', this option may be overridden by the scanner. " ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . constraint_type = SANE_CONSTRAINT_NONE ;
scanner - > val [ OPT_SHADING_ANALYSIS ] . b = SANE_FALSE ;
scanner - > opt [ OPT_SHADING_ANALYSIS ] . cap | = SANE_CAP_SOFT_SELECT ;
/* use auto-calibration settings for scan */
scanner - > opt [ OPT_CALIBRATION_MODE ] . name = " calibration " ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . title = " Calibration mode " ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . desc = " How to calibrate the scanner. " ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . type = SANE_TYPE_STRING ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . size = max_string_size ( ( SANE_String_Const const * ) scanner - > device - > calibration_mode_list ) ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . constraint_type = SANE_CONSTRAINT_STRING_LIST ;
scanner - > opt [ OPT_CALIBRATION_MODE ] . constraint . string_list = ( SANE_String_Const const * ) scanner - > device - > calibration_mode_list ;
scanner - > val [ OPT_CALIBRATION_MODE ] . s = ( SANE_Char * ) strdup ( scanner - > device - > calibration_mode_list [ 1 ] ) ; /* default auto */
/* OPT_GAIN_ADJUST */
scanner - > opt [ OPT_GAIN_ADJUST ] . name = " gain-adjust " ;
scanner - > opt [ OPT_GAIN_ADJUST ] . title = " Adjust gain " ;
scanner - > opt [ OPT_GAIN_ADJUST ] . desc = " Adjust gain determined by calibration procedure. " ;
scanner - > opt [ OPT_GAIN_ADJUST ] . type = SANE_TYPE_STRING ;
scanner - > opt [ OPT_GAIN_ADJUST ] . size = max_string_size ( ( SANE_String_Const const * ) scanner - > device - > gain_adjust_list ) ;
scanner - > opt [ OPT_GAIN_ADJUST ] . constraint_type = SANE_CONSTRAINT_STRING_LIST ;
scanner - > opt [ OPT_GAIN_ADJUST ] . constraint . string_list = ( SANE_String_Const const * ) scanner - > device - > gain_adjust_list ;
scanner - > val [ OPT_GAIN_ADJUST ] . s = ( SANE_Char * ) strdup ( scanner - > device - > gain_adjust_list [ 2 ] ) ; /* x 1.0 (no change) */
/* scan infrared channel faster but less accurate */
scanner - > opt [ OPT_FAST_INFRARED ] . name = " fast-infrared " ;
scanner - > opt [ OPT_FAST_INFRARED ] . title = " Fast infrared scan " ;
scanner - > opt [ OPT_FAST_INFRARED ] . desc = " Do not reposition scan head before scanning infrared line. Results in an infrared offset which may deteriorate IR dust and scratch removal. " ;
scanner - > opt [ OPT_FAST_INFRARED ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_FAST_INFRARED ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ OPT_FAST_INFRARED ] . constraint_type = SANE_CONSTRAINT_NONE ;
scanner - > val [ OPT_FAST_INFRARED ] . b = SANE_FALSE ;
scanner - > opt [ OPT_FAST_INFRARED ] . cap | = SANE_CAP_SOFT_SELECT ;
/* automatically advance to next slide after scan */
scanner - > opt [ OPT_ADVANCE_SLIDE ] . name = " advcane " ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . title = " Advance slide " ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . desc = " Automatically advance to next slide after scan " ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . constraint_type = SANE_CONSTRAINT_NONE ;
scanner - > val [ OPT_ADVANCE_SLIDE ] . w = SANE_TRUE ;
scanner - > opt [ OPT_ADVANCE_SLIDE ] . cap | = SANE_CAP_SOFT_SELECT ;
/* "Geometry" group: */
scanner - > opt [ OPT_GEOMETRY_GROUP ] . title = " Geometry " ;
scanner - > opt [ OPT_GEOMETRY_GROUP ] . desc = " " ;
scanner - > opt [ OPT_GEOMETRY_GROUP ] . type = SANE_TYPE_GROUP ;
scanner - > opt [ OPT_GEOMETRY_GROUP ] . cap = SANE_CAP_ADVANCED ;
scanner - > opt [ OPT_GEOMETRY_GROUP ] . constraint_type = SANE_CONSTRAINT_NONE ;
/* top-left x */
scanner - > opt [ OPT_TL_X ] . name = SANE_NAME_SCAN_TL_X ;
scanner - > opt [ OPT_TL_X ] . title = SANE_TITLE_SCAN_TL_X ;
scanner - > opt [ OPT_TL_X ] . desc = SANE_DESC_SCAN_TL_X ;
scanner - > opt [ OPT_TL_X ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_TL_X ] . unit = SANE_UNIT_MM ;
scanner - > opt [ OPT_TL_X ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_TL_X ] . constraint . range = & ( scanner - > device - > x_range ) ;
scanner - > val [ OPT_TL_X ] . w = 0 ;
/* top-left y */
scanner - > opt [ OPT_TL_Y ] . name = SANE_NAME_SCAN_TL_Y ;
scanner - > opt [ OPT_TL_Y ] . title = SANE_TITLE_SCAN_TL_Y ;
scanner - > opt [ OPT_TL_Y ] . desc = SANE_DESC_SCAN_TL_Y ;
scanner - > opt [ OPT_TL_Y ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_TL_Y ] . unit = SANE_UNIT_MM ;
scanner - > opt [ OPT_TL_Y ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_TL_Y ] . constraint . range = & ( scanner - > device - > y_range ) ;
scanner - > val [ OPT_TL_Y ] . w = 0 ;
/* bottom-right x */
scanner - > opt [ OPT_BR_X ] . name = SANE_NAME_SCAN_BR_X ;
scanner - > opt [ OPT_BR_X ] . title = SANE_TITLE_SCAN_BR_X ;
scanner - > opt [ OPT_BR_X ] . desc = SANE_DESC_SCAN_BR_X ;
scanner - > opt [ OPT_BR_X ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_BR_X ] . unit = SANE_UNIT_MM ;
scanner - > opt [ OPT_BR_X ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_BR_X ] . constraint . range = & ( scanner - > device - > x_range ) ;
scanner - > val [ OPT_BR_X ] . w = scanner - > device - > x_range . max ;
/* bottom-right y */
scanner - > opt [ OPT_BR_Y ] . name = SANE_NAME_SCAN_BR_Y ;
scanner - > opt [ OPT_BR_Y ] . title = SANE_TITLE_SCAN_BR_Y ;
scanner - > opt [ OPT_BR_Y ] . desc = SANE_DESC_SCAN_BR_Y ;
scanner - > opt [ OPT_BR_Y ] . type = SANE_TYPE_FIXED ;
scanner - > opt [ OPT_BR_Y ] . unit = SANE_UNIT_MM ;
scanner - > opt [ OPT_BR_Y ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ OPT_BR_Y ] . constraint . range = & ( scanner - > device - > y_range ) ;
scanner - > val [ OPT_BR_Y ] . w = scanner - > device - > y_range . max ;
/* "Enhancement" group: */
scanner - > opt [ OPT_ENHANCEMENT_GROUP ] . title = " Enhancement " ;
scanner - > opt [ OPT_ENHANCEMENT_GROUP ] . desc = " " ;
scanner - > opt [ OPT_ENHANCEMENT_GROUP ] . type = SANE_TYPE_GROUP ;
scanner - > opt [ OPT_ENHANCEMENT_GROUP ] . cap = 0 ;
scanner - > opt [ OPT_ENHANCEMENT_GROUP ] . constraint_type = SANE_CONSTRAINT_NONE ;
/* correct data for lamp variations (shading) */
scanner - > opt [ OPT_CORRECT_SHADING ] . name = " correct-shading " ;
scanner - > opt [ OPT_CORRECT_SHADING ] . title = " Correct shading " ;
scanner - > opt [ OPT_CORRECT_SHADING ] . desc = " Correct data for lamp variations (shading) " ;
scanner - > opt [ OPT_CORRECT_SHADING ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_CORRECT_SHADING ] . unit = SANE_UNIT_NONE ;
scanner - > val [ OPT_CORRECT_SHADING ] . w = SANE_TRUE ;
/* correct infrared for red crosstalk */
scanner - > opt [ OPT_CORRECT_INFRARED ] . name = " correct-infrared " ;
scanner - > opt [ OPT_CORRECT_INFRARED ] . title = " Correct infrared " ;
scanner - > opt [ OPT_CORRECT_INFRARED ] . desc = " Correct infrared for red crosstalk " ;
scanner - > opt [ OPT_CORRECT_INFRARED ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_CORRECT_INFRARED ] . unit = SANE_UNIT_NONE ;
scanner - > val [ OPT_CORRECT_INFRARED ] . w = SANE_FALSE ;
/* detect and remove dust and scratch artifacts */
scanner - > opt [ OPT_CLEAN_IMAGE ] . name = " clean-image " ;
scanner - > opt [ OPT_CLEAN_IMAGE ] . title = " Clean image " ;
scanner - > opt [ OPT_CLEAN_IMAGE ] . desc = " Detect and remove dust and scratch artifacts " ;
scanner - > opt [ OPT_CLEAN_IMAGE ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_CLEAN_IMAGE ] . unit = SANE_UNIT_NONE ;
scanner - > val [ OPT_CLEAN_IMAGE ] . w = SANE_FALSE ;
/* strength of grain filtering */
scanner - > opt [ OPT_SMOOTH_IMAGE ] . name = " smooth " ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . title = " Attenuate film grain " ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . desc = " Amount of smoothening " ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . type = SANE_TYPE_INT ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . constraint_type = SANE_CONSTRAINT_WORD_LIST ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . size = sizeof ( SANE_Word ) ;
scanner - > opt [ OPT_SMOOTH_IMAGE ] . constraint . word_list = scanner - > device - > grain_sw_list ;
scanner - > val [ OPT_SMOOTH_IMAGE ] . w = scanner - > device - > grain_sw_list [ 1 ] ;
if ( scanner - > opt [ OPT_SMOOTH_IMAGE ] . constraint . word_list [ 0 ] < 2 ) {
scanner - > opt [ OPT_SMOOTH_IMAGE ] . cap | = SANE_CAP_INACTIVE ;
}
/* gamma correction, to make image sRGB like */
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . name = " srgb " ;
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . title = " sRGB colors " ;
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . desc = " Transform image to approximate sRGB color space " ;
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . unit = SANE_UNIT_NONE ;
scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . w = SANE_FALSE ;
scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . cap | = SANE_CAP_INACTIVE ;
/* color correction for generic negative film */
scanner - > opt [ OPT_INVERT_IMAGE ] . name = " invert " ;
scanner - > opt [ OPT_INVERT_IMAGE ] . title = " Invert colors " ;
scanner - > opt [ OPT_INVERT_IMAGE ] . desc = " Correct for generic negative film " ;
scanner - > opt [ OPT_INVERT_IMAGE ] . type = SANE_TYPE_BOOL ;
scanner - > opt [ OPT_INVERT_IMAGE ] . unit = SANE_UNIT_NONE ;
scanner - > val [ OPT_INVERT_IMAGE ] . w = SANE_FALSE ;
scanner - > opt [ OPT_INVERT_IMAGE ] . cap | = SANE_CAP_INACTIVE ;
/* crop image */
scanner - > opt [ OPT_CROP_IMAGE ] . name = " crop " ;
scanner - > opt [ OPT_CROP_IMAGE ] . title = " Cropping " ;
scanner - > opt [ OPT_CROP_IMAGE ] . desc = " How to crop the image " ;
scanner - > opt [ OPT_CROP_IMAGE ] . type = SANE_TYPE_STRING ;
scanner - > opt [ OPT_CROP_IMAGE ] . size = max_string_size ( ( SANE_String_Const const * ) ( void * ) scanner - > device - > crop_sw_list ) ;
scanner - > opt [ OPT_CROP_IMAGE ] . constraint_type = SANE_CONSTRAINT_STRING_LIST ;
scanner - > opt [ OPT_CROP_IMAGE ] . constraint . string_list = ( SANE_String_Const const * ) ( void * ) scanner - > device - > crop_sw_list ;
scanner - > val [ OPT_CROP_IMAGE ] . s = ( SANE_Char * ) strdup ( scanner - > device - > crop_sw_list [ 2 ] ) ;
/* "Advanced" group: */
scanner - > opt [ OPT_ADVANCED_GROUP ] . title = " Advanced " ;
scanner - > opt [ OPT_ADVANCED_GROUP ] . desc = " " ;
scanner - > opt [ OPT_ADVANCED_GROUP ] . type = SANE_TYPE_GROUP ;
scanner - > opt [ OPT_ADVANCED_GROUP ] . cap = SANE_CAP_ADVANCED ;
scanner - > opt [ OPT_ADVANCED_GROUP ] . constraint_type = SANE_CONSTRAINT_NONE ;
/* preview */
scanner - > opt [ OPT_PREVIEW ] . name = SANE_NAME_PREVIEW ;
scanner - > opt [ OPT_PREVIEW ] . title = SANE_TITLE_PREVIEW ;
scanner - > opt [ OPT_PREVIEW ] . desc = SANE_DESC_PREVIEW ;
scanner - > opt [ OPT_PREVIEW ] . type = SANE_TYPE_BOOL ;
scanner - > val [ OPT_PREVIEW ] . w = SANE_FALSE ;
/* save shading data */
scanner - > opt [ OPT_SAVE_SHADINGDATA ] . name = " save-shading-data " ;
scanner - > opt [ OPT_SAVE_SHADINGDATA ] . title = " Save shading data " ;
scanner - > opt [ OPT_SAVE_SHADINGDATA ] . desc = " Save shading data in 'pieusb.shading' " ;
scanner - > opt [ OPT_SAVE_SHADINGDATA ] . type = SANE_TYPE_BOOL ;
scanner - > val [ OPT_SAVE_SHADINGDATA ] . w = SANE_FALSE ;
/* save CCD mask */
scanner - > opt [ OPT_SAVE_CCDMASK ] . name = " save-ccdmask " ;
scanner - > opt [ OPT_SAVE_CCDMASK ] . title = " Save CCD mask " ;
scanner - > opt [ OPT_SAVE_CCDMASK ] . desc = " Save CCD mask 'pieusb.ccd' " ;
scanner - > opt [ OPT_SAVE_CCDMASK ] . type = SANE_TYPE_BOOL ;
scanner - > val [ OPT_SAVE_CCDMASK ] . w = SANE_FALSE ;
scanner - > opt [ OPT_LIGHT ] . name = " light " ;
scanner - > opt [ OPT_LIGHT ] . title = " Light " ;
scanner - > opt [ OPT_LIGHT ] . desc = " Light " ;
scanner - > opt [ OPT_LIGHT ] . type = SANE_TYPE_INT ;
scanner - > opt [ OPT_LIGHT ] . unit = SANE_UNIT_MICROSECOND ;
scanner - > opt [ OPT_LIGHT ] . cap | = SANE_CAP_SOFT_SELECT ;
scanner - > opt [ OPT_LIGHT ] . size = sizeof ( SANE_Word ) ;
scanner - > val [ OPT_LIGHT ] . w = DEFAULT_LIGHT ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . name = " double-times " ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . title = " Double times " ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . desc = " Double times " ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . type = SANE_TYPE_INT ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . unit = SANE_UNIT_MICROSECOND ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . cap | = SANE_CAP_SOFT_SELECT ;
scanner - > opt [ OPT_DOUBLE_TIMES ] . size = sizeof ( SANE_Word ) ;
scanner - > val [ OPT_DOUBLE_TIMES ] . w = DEFAULT_DOUBLE_TIMES ;
/* exposure times for R, G, B and I */
scanner - > opt [ OPT_SET_EXPOSURE_R ] . name = SANE_NAME_EXPOSURE_R ;
scanner - > opt [ OPT_SET_EXPOSURE_R ] . title = SANE_TITLE_EXPOSURE_R ;
scanner - > opt [ OPT_SET_EXPOSURE_R ] . desc = SANE_DESC_EXPOSURE_R ;
scanner - > opt [ OPT_SET_EXPOSURE_G ] . name = SANE_NAME_EXPOSURE_G ;
scanner - > opt [ OPT_SET_EXPOSURE_G ] . title = SANE_TITLE_EXPOSURE_G ;
scanner - > opt [ OPT_SET_EXPOSURE_G ] . desc = SANE_DESC_EXPOSURE_G ;
scanner - > opt [ OPT_SET_EXPOSURE_B ] . name = SANE_NAME_EXPOSURE_B ;
scanner - > opt [ OPT_SET_EXPOSURE_B ] . title = SANE_TITLE_EXPOSURE_B ;
scanner - > opt [ OPT_SET_EXPOSURE_B ] . desc = SANE_DESC_EXPOSURE_B ;
scanner - > opt [ OPT_SET_EXPOSURE_I ] . name = SANE_NAME_EXPOSURE_I ;
scanner - > opt [ OPT_SET_EXPOSURE_I ] . title = SANE_TITLE_EXPOSURE_I ;
scanner - > opt [ OPT_SET_EXPOSURE_I ] . desc = SANE_DESC_EXPOSURE_I ;
for ( i = OPT_SET_EXPOSURE_R ; i < = OPT_SET_EXPOSURE_I ; + + i ) {
scanner - > opt [ i ] . type = SANE_TYPE_INT ;
scanner - > opt [ i ] . unit = SANE_UNIT_MICROSECOND ;
scanner - > opt [ i ] . cap | = SANE_CAP_SOFT_SELECT ;
scanner - > opt [ i ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ i ] . constraint . range = & ( scanner - > device - > exposure_range ) ;
scanner - > opt [ i ] . size = sizeof ( SANE_Word ) ;
scanner - > val [ i ] . w = SANE_EXPOSURE_DEFAULT ;
}
/* gain for R, G, B and I */
scanner - > opt [ OPT_SET_GAIN_R ] . name = SANE_NAME_GAIN_R ;
scanner - > opt [ OPT_SET_GAIN_R ] . title = SANE_TITLE_GAIN_R ;
scanner - > opt [ OPT_SET_GAIN_R ] . desc = SANE_DESC_GAIN_R ;
scanner - > opt [ OPT_SET_GAIN_G ] . name = SANE_NAME_GAIN_G ;
scanner - > opt [ OPT_SET_GAIN_G ] . title = SANE_TITLE_GAIN_G ;
scanner - > opt [ OPT_SET_GAIN_G ] . desc = SANE_DESC_GAIN_G ;
scanner - > opt [ OPT_SET_GAIN_B ] . name = SANE_NAME_GAIN_B ;
scanner - > opt [ OPT_SET_GAIN_B ] . title = SANE_TITLE_GAIN_B ;
scanner - > opt [ OPT_SET_GAIN_B ] . desc = SANE_DESC_GAIN_B ;
scanner - > opt [ OPT_SET_GAIN_I ] . name = SANE_NAME_GAIN_I ;
scanner - > opt [ OPT_SET_GAIN_I ] . title = SANE_TITLE_GAIN_I ;
scanner - > opt [ OPT_SET_GAIN_I ] . desc = SANE_DESC_GAIN_I ;
for ( i = OPT_SET_GAIN_R ; i < = OPT_SET_GAIN_I ; + + i ) {
scanner - > opt [ i ] . type = SANE_TYPE_INT ;
scanner - > opt [ i ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ i ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ i ] . constraint . range = & gain_range ;
scanner - > opt [ i ] . size = sizeof ( SANE_Word ) ;
scanner - > val [ i ] . w = SANE_GAIN_DEFAULT ;
}
/* offsets for R, G, B and I */
scanner - > opt [ OPT_SET_OFFSET_R ] . name = SANE_NAME_OFFSET_R ;
scanner - > opt [ OPT_SET_OFFSET_R ] . title = SANE_TITLE_OFFSET_R ;
scanner - > opt [ OPT_SET_OFFSET_R ] . desc = SANE_DESC_OFFSET_R ;
scanner - > opt [ OPT_SET_OFFSET_G ] . name = SANE_NAME_OFFSET_G ;
scanner - > opt [ OPT_SET_OFFSET_G ] . title = SANE_TITLE_OFFSET_G ;
scanner - > opt [ OPT_SET_OFFSET_G ] . desc = SANE_DESC_OFFSET_G ;
scanner - > opt [ OPT_SET_OFFSET_B ] . name = SANE_NAME_OFFSET_B ;
scanner - > opt [ OPT_SET_OFFSET_B ] . title = SANE_TITLE_OFFSET_B ;
scanner - > opt [ OPT_SET_OFFSET_B ] . desc = SANE_DESC_OFFSET_B ;
scanner - > opt [ OPT_SET_OFFSET_I ] . name = SANE_NAME_OFFSET_I ;
scanner - > opt [ OPT_SET_OFFSET_I ] . title = SANE_TITLE_OFFSET_I ;
scanner - > opt [ OPT_SET_OFFSET_I ] . desc = SANE_DESC_OFFSET_I ;
for ( i = OPT_SET_OFFSET_R ; i < = OPT_SET_OFFSET_I ; + + i ) {
scanner - > opt [ i ] . type = SANE_TYPE_INT ;
scanner - > opt [ i ] . unit = SANE_UNIT_NONE ;
scanner - > opt [ i ] . constraint_type = SANE_CONSTRAINT_RANGE ;
scanner - > opt [ i ] . constraint . range = & offset_range ;
scanner - > opt [ i ] . size = sizeof ( SANE_Word ) ;
scanner - > val [ i ] . w = SANE_OFFSET_DEFAULT ;
}
return SANE_STATUS_GOOD ;
}
/**
2017-05-03 15:52:15 +00:00
* Parse line from config file into a vendor id , product id , model number , and flags
2015-02-20 19:52:08 +00:00
*
* @ param config_line Text to parse
* @ param vendor_id
* @ param product_id
* @ param model_number
2017-05-03 15:52:15 +00:00
* @ param flags
2015-02-20 19:52:08 +00:00
* @ return SANE_STATUS_GOOD , or SANE_STATUS_INVAL in case of a parse error
*/
SANE_Status
2017-05-03 15:52:15 +00:00
sanei_pieusb_parse_config_line ( const char * config_line ,
SANE_Word * vendor_id ,
SANE_Word * product_id ,
SANE_Int * model_number ,
SANE_Int * flags )
2015-02-20 19:52:08 +00:00
{
2017-05-03 15:52:15 +00:00
char * vendor_id_string , * product_id_string , * model_number_string , * flags_string ;
2015-02-20 19:52:08 +00:00
if ( strncmp ( config_line , " usb " , 4 ) ! = 0 ) {
return SANE_STATUS_INVAL ;
}
/* Detect vendor-id */
config_line + = 4 ;
config_line = sanei_config_skip_whitespace ( config_line ) ;
if ( * config_line ) {
config_line = sanei_config_get_string ( config_line , & vendor_id_string ) ;
if ( vendor_id_string ) {
* vendor_id = strtol ( vendor_id_string , 0 , 0 ) ;
free ( vendor_id_string ) ;
} else {
return SANE_STATUS_INVAL ;
}
config_line = sanei_config_skip_whitespace ( config_line ) ;
} else {
return SANE_STATUS_INVAL ;
}
/* Detect product-id */
config_line = sanei_config_skip_whitespace ( config_line ) ;
if ( * config_line ) {
config_line = sanei_config_get_string ( config_line , & product_id_string ) ;
if ( product_id_string ) {
* product_id = strtol ( product_id_string , 0 , 0 ) ;
free ( product_id_string ) ;
} else {
return SANE_STATUS_INVAL ;
}
config_line = sanei_config_skip_whitespace ( config_line ) ;
} else {
return SANE_STATUS_INVAL ;
}
2017-05-03 15:52:15 +00:00
/* Detect model number */
2015-02-20 19:52:08 +00:00
config_line = sanei_config_skip_whitespace ( config_line ) ;
if ( * config_line ) {
config_line = sanei_config_get_string ( config_line , & model_number_string ) ;
if ( model_number_string ) {
2017-05-03 15:52:15 +00:00
* model_number = ( SANE_Int ) strtol ( model_number_string , 0 , 0 ) ;
2015-02-20 19:52:08 +00:00
free ( model_number_string ) ;
} else {
return SANE_STATUS_INVAL ;
}
config_line = sanei_config_skip_whitespace ( config_line ) ;
} else {
return SANE_STATUS_INVAL ;
}
2017-05-03 15:52:15 +00:00
/* Detect (optional) flags */
* flags = 0 ;
config_line = sanei_config_skip_whitespace ( config_line ) ;
if ( * config_line ) {
config_line = sanei_config_get_string ( config_line , & flags_string ) ;
if ( flags_string ) {
* flags = ( SANE_Int ) strtol ( flags_string , 0 , 0 ) ;
free ( flags_string ) ;
}
}
2015-02-20 19:52:08 +00:00
return SANE_STATUS_GOOD ;
}
/**
* Check if current list of supported devices contains the given specifications .
*
* @ param vendor_id
* @ param product_id
* @ param model_number
2017-05-03 15:52:15 +00:00
* @ param flags
2015-02-20 19:52:08 +00:00
* @ return
*/
SANE_Bool
2017-05-03 15:52:15 +00:00
sanei_pieusb_supported_device_list_contains ( SANE_Word vendor_id , SANE_Word product_id , SANE_Int model_number , SANE_Int flags )
2015-02-20 19:52:08 +00:00
{
int i = 0 ;
while ( pieusb_supported_usb_device_list [ i ] . vendor ! = 0 ) {
if ( pieusb_supported_usb_device_list [ i ] . vendor = = vendor_id
& & pieusb_supported_usb_device_list [ i ] . product = = product_id
2017-05-03 15:52:15 +00:00
& & pieusb_supported_usb_device_list [ i ] . model = = model_number
& & pieusb_supported_usb_device_list [ i ] . flags = = flags ) {
2015-02-20 19:52:08 +00:00
return SANE_TRUE ;
}
i + + ;
}
return SANE_FALSE ;
}
/**
* Add the given specifications to the current list of supported devices
* @ param vendor_id
* @ param product_id
* @ param model_number
2017-05-03 15:52:15 +00:00
* @ param flags
2015-02-20 19:52:08 +00:00
* @ return
*/
SANE_Status
2017-05-03 15:52:15 +00:00
sanei_pieusb_supported_device_list_add ( SANE_Word vendor_id , SANE_Word product_id , SANE_Int model_number , SANE_Int flags )
2015-02-20 19:52:08 +00:00
{
int i = 0 , k ;
struct Pieusb_USB_Device_Entry * dl ;
while ( pieusb_supported_usb_device_list [ i ] . vendor ! = 0 ) {
i + + ;
}
/* i is index of last entry */
for ( k = 0 ; k < = i ; k + + ) {
2017-05-03 15:52:15 +00:00
DBG ( DBG_info_proc , " sanei_pieusb_supported_device_list_add(): current %03d: %04x %04x %02x %02x \n " , i ,
2015-02-20 19:52:08 +00:00
pieusb_supported_usb_device_list [ k ] . vendor ,
pieusb_supported_usb_device_list [ k ] . product ,
2017-05-03 15:52:15 +00:00
pieusb_supported_usb_device_list [ k ] . model ,
pieusb_supported_usb_device_list [ k ] . flags ) ;
2015-02-20 19:52:08 +00:00
}
dl = realloc ( pieusb_supported_usb_device_list , ( i + 2 ) * sizeof ( struct Pieusb_USB_Device_Entry ) ) ; /* Add one entry to list */
if ( dl = = NULL ) {
return SANE_STATUS_INVAL ;
}
/* Copy values */
pieusb_supported_usb_device_list = dl ;
pieusb_supported_usb_device_list [ i ] . vendor = vendor_id ;
pieusb_supported_usb_device_list [ i ] . product = product_id ;
pieusb_supported_usb_device_list [ i ] . model = model_number ;
2017-05-03 15:52:15 +00:00
pieusb_supported_usb_device_list [ i ] . flags = flags ;
2015-02-20 19:52:08 +00:00
pieusb_supported_usb_device_list [ i + 1 ] . vendor = 0 ;
pieusb_supported_usb_device_list [ i + 1 ] . product = 0 ;
pieusb_supported_usb_device_list [ i + 1 ] . model = 0 ;
2017-05-03 15:52:15 +00:00
pieusb_supported_usb_device_list [ i + 1 ] . flags = 0 ;
2015-02-20 19:52:08 +00:00
for ( k = 0 ; k < = i + 1 ; k + + ) {
2017-05-03 15:52:15 +00:00
DBG ( DBG_info_proc , " sanei_pieusb_supported_device_list_add() add: %03d: %04x %04x %02x %02x \n " , i ,
2015-02-20 19:52:08 +00:00
pieusb_supported_usb_device_list [ k ] . vendor ,
pieusb_supported_usb_device_list [ k ] . product ,
2017-05-03 15:52:15 +00:00
pieusb_supported_usb_device_list [ k ] . model ,
pieusb_supported_usb_device_list [ k ] . flags ) ;
2015-02-20 19:52:08 +00:00
}
return SANE_STATUS_GOOD ;
}
/**
* Actions to perform when a cancel request has been received .
*
* @ param scanner scanner to stop scanning
* @ return SANE_STATUS_CANCELLED
*/
SANE_Status
sanei_pieusb_on_cancel ( Pieusb_Scanner * scanner )
{
struct Pieusb_Command_Status status ;
DBG ( DBG_info_proc , " sanei_pieusb_on_cancel() \n " ) ;
sanei_pieusb_cmd_stop_scan ( scanner - > device_number , & status ) ;
sanei_pieusb_cmd_set_scan_head ( scanner - > device_number , 1 , 0 , & status ) ;
sanei_pieusb_buffer_delete ( & scanner - > buffer ) ;
scanner - > scanning = SANE_FALSE ;
return SANE_STATUS_CANCELLED ;
}
/**
* Determine maximum lengt of a set of strings .
*
* @ param strings Set of strings
* @ return maximum length
*/
static size_t
max_string_size ( SANE_String_Const const strings [ ] )
{
size_t size , max_size = 0 ;
int i ;
for ( i = 0 ; strings [ i ] ; + + i ) {
size = strlen ( strings [ i ] ) + 1 ;
if ( size > max_size ) {
max_size = size ;
}
}
return max_size ;
}
/* From MR's pie.c */
/* ------------------------- PIEUSB_CORRECT_SHADING -------------------------- */
/**
* Correct the given buffer for shading using shading data in scanner .
* If the loop order is width - > color - > height , a 7200 dpi scan correction takes
* 45 minutes . If the loop order is color - > height - > width , this is less than 3
* minutes . So it is worthwhile to find the used pixels first ( array width_to_loc ) .
*
* @ param scanner Scanner
* @ param buffer Buffer to correct
*/
void
sanei_pieusb_correct_shading ( struct Pieusb_Scanner * scanner , struct Pieusb_Read_Buffer * buffer )
{
int i , j , c , k ;
SANE_Uint val , val_org , * p ;
int * width_to_loc ;
DBG ( DBG_info_proc , " sanei_pieusb_correct_shading() \n " ) ;
/* Loop through CCD-mask to find used pixels */
width_to_loc = calloc ( buffer - > width , sizeof ( int ) ) ;
j = 0 ;
for ( i = 0 ; i < scanner - > ccd_mask_size ; i + + ) {
if ( scanner - > ccd_mask [ i ] = = 0 ) {
width_to_loc [ j + + ] = i ;
}
}
/* Correct complete image */
for ( c = 0 ; c < buffer - > colors ; c + + ) {
DBG ( DBG_info , " sanei_pieusb_correct_shading() correct color %d \n " , c ) ;
for ( k = 0 ; k < buffer - > height ; k + + ) {
/* DBG(DBG_info,"Correct line %d\n",k); */
p = buffer - > data + c * buffer - > width * buffer - > height + k * buffer - > width ;
for ( j = 0 ; j < buffer - > width ; j + + ) {
val_org = * p ;
val = lround ( ( double ) scanner - > shading_mean [ c ] / scanner - > shading_ref [ c ] [ width_to_loc [ j ] ] * val_org ) ;
/* DBG(DBG_info,"Correct [%d,%d,%d] %d -> %d\n",k,j,c,val_org,val); */
* p + + = val ;
}
}
}
/* Free memory */
free ( width_to_loc ) ;
}
/* === functions copied from MR's code === */
/**
*
* @ param scanner
* @ param in_img
* @ param planes
* @ param out_planes
* @ return
*/
SANE_Status
sanei_pieusb_post ( Pieusb_Scanner * scanner , uint16_t * * in_img , int planes )
{
uint16_t * cplane [ PLANES ] ; /* R, G, B, I gray scale planes */
SANE_Parameters parameters ; /* describes the image */
int winsize_smooth ; /* for adapting replaced pixels */
char filename [ 64 ] ;
SANE_Status status ;
int smooth , i ;
memcpy ( & parameters , & scanner - > scan_parameters , sizeof ( SANE_Parameters ) ) ;
parameters . format = SANE_FRAME_GRAY ;
parameters . bytes_per_line = parameters . pixels_per_line ;
if ( parameters . depth > 8 )
parameters . bytes_per_line * = 2 ;
parameters . last_frame = 0 ;
DBG ( DBG_info , " pie_usb_post: %d ppl, %d lines, %d bits, %d planes, %d dpi \n " ,
parameters . pixels_per_line , parameters . lines ,
parameters . depth , planes , scanner - > mode . resolution ) ;
if ( planes > PLANES ) {
DBG ( DBG_error , " pie_usb_post: too many planes: %d (max %d) \n " , planes , PLANES ) ;
return SANE_STATUS_INVAL ;
}
for ( i = 0 ; i < planes ; i + + )
cplane [ i ] = in_img [ i ] ;
/* dirt is rather resolution invariant, so
* setup resolution dependent parameters
*/
/* film grain reduction */
smooth = scanner - > val [ OPT_SMOOTH_IMAGE ] . w ;
winsize_smooth = ( scanner - > mode . resolution / 540 ) | 1 ;
/* smoothen whole image or only replaced pixels */
if ( smooth )
{
winsize_smooth + = 2 * ( smooth - 3 ) ; /* even */
if ( winsize_smooth < 3 )
smooth = 0 ;
}
if ( winsize_smooth < 3 )
winsize_smooth = 3 ;
DBG ( DBG_info , " pie_usb_sw_post: winsize_smooth %d \n " , winsize_smooth ) ;
/* RGBI post-processing if selected:
* 1 ) remove spectral overlay from ired plane ,
* 2 ) remove dirt , smoothen if , crop if */
if ( scanner - > val [ OPT_CORRECT_INFRARED ] . b ) /* (scanner->processing & POST_SW_IRED_MASK) */
{
/* remove spectral overlay from ired plane */
status = sanei_ir_spectral_clean ( & parameters , scanner - > ln_lut , cplane [ 0 ] , cplane [ 3 ] ) ;
if ( status ! = SANE_STATUS_GOOD )
return status ;
if ( DBG_LEVEL > = 15 )
{
snprintf ( filename , 63 , " /tmp/ir-spectral.pnm " ) ;
pieusb_write_pnm_file ( filename , cplane [ 3 ] ,
parameters . depth , 1 ,
parameters . pixels_per_line , parameters . lines ) ;
}
if ( scanner - > cancel_request ) /* asynchronous cancel ? */
return SANE_STATUS_CANCELLED ;
} /* scanner-> processing & POST_SW_IRED_MASK */
/* remove dirt, smoothen if, crop if */
if ( scanner - > val [ OPT_CLEAN_IMAGE ] . b ) /* (scanner->processing & POST_SW_DIRT) */
{
double * norm_histo ;
uint16_t * thresh_data ;
int static_thresh , too_thresh ; /* static thresholds */
int winsize_filter ; /* primary size of filtering window */
int size_dilate ; /* the dirt mask */
/* size of filter detecting dirt */
winsize_filter = ( int ) ( 5.0 * ( double ) scanner - > mode . resolution / 300.0 ) | 1 ;
if ( winsize_filter < 3 )
winsize_filter = 3 ;
/* dirt usually has smooth edges which also need correction */
size_dilate = scanner - > mode . resolution / 1000 + 1 ;
/* first detect large dirt by a static threshold */
status = sanei_ir_create_norm_histogram ( & parameters , cplane [ 3 ] , & norm_histo ) ;
if ( status ! = SANE_STATUS_GOOD )
{
DBG ( DBG_error , " pie_usb_sw_post: no buffer \n " ) ;
return SANE_STATUS_NO_MEM ;
}
/* generate a "bimodal" static threshold */
status = sanei_ir_threshold_yen ( & parameters , norm_histo , & static_thresh ) ;
if ( status ! = SANE_STATUS_GOOD )
return status ;
/* generate traditional static threshold */
status = sanei_ir_threshold_otsu ( & parameters , norm_histo , & too_thresh ) ;
if ( status ! = SANE_STATUS_GOOD )
return status ;
/* choose lower one */
if ( too_thresh < static_thresh )
static_thresh = too_thresh ;
free ( norm_histo ) ;
/* then generate dirt mask with adaptive thresholding filter
* and add the dirt from the static threshold */
/* last two parameters: 10, 50 detects more, 20, 75 less */
status = sanei_ir_filter_madmean ( & parameters , cplane [ 3 ] , & thresh_data , winsize_filter , 20 , 100 ) ;
if ( status ! = SANE_STATUS_GOOD ) {
free ( thresh_data ) ;
return status ;
}
sanei_ir_add_threshold ( & parameters , cplane [ 3 ] , thresh_data , static_thresh ) ;
if ( DBG_LEVEL > = 15 )
{
snprintf ( filename , 63 , " /tmp/ir-threshold.pnm " ) ;
pieusb_write_pnm_file ( filename , thresh_data ,
8 , 1 , parameters . pixels_per_line ,
parameters . lines ) ;
}
if ( scanner - > cancel_request ) { /* asynchronous cancel ? */
free ( thresh_data ) ;
return SANE_STATUS_CANCELLED ;
}
/* replace the dirt and smoothen film grain and crop if possible */
status = sanei_ir_dilate_mean ( & parameters , cplane , thresh_data ,
500 , size_dilate , winsize_smooth , smooth ,
0 , NULL ) ;
if ( status ! = SANE_STATUS_GOOD ) {
free ( thresh_data ) ;
return status ;
}
smooth = 0 ;
free ( thresh_data ) ;
}
if ( DBG_LEVEL > = 15 )
{
pieusb_write_pnm_file ( " /tmp/RGBi-img.pnm " , scanner - > buffer . data ,
scanner - > scan_parameters . depth , 3 , scanner - > scan_parameters . pixels_per_line ,
scanner - > scan_parameters . lines ) ;
}
return status ;
}
/* ------------------------------ PIE_USB_WRITE_PNM_FILE ------------------------------- */
static SANE_Status
pieusb_write_pnm_file ( char * filename , SANE_Uint * data , int depth ,
int channels , int pixels_per_line , int lines )
{
FILE * out ;
int r , c , ch ;
SANE_Uint val ;
uint8_t b = 0 ;
DBG ( DBG_info_proc ,
" pie_usb_write_pnm_file: depth=%d, channels=%d, ppl=%d, lines=%d \n " ,
depth , channels , pixels_per_line , lines ) ;
out = fopen ( filename , " w " ) ;
if ( ! out )
{
DBG ( DBG_error ,
" pie_usb_write_pnm_file: could not open %s for writing: %s \n " ,
filename , strerror ( errno ) ) ;
return SANE_STATUS_INVAL ;
}
switch ( depth ) {
case 1 :
fprintf ( out , " P4 \n %d \n %d \n " , pixels_per_line , lines ) ;
for ( r = 0 ; r < lines ; r + + ) {
int i ;
i = 0 ;
b = 0 ;
for ( c = 0 ; c < pixels_per_line ; c + + ) {
val = * ( data + r * pixels_per_line + c ) ;
if ( val > 0 ) b | = ( 0x80 > > i ) ;
i + + ;
if ( i = = 7 ) {
fputc ( b , out ) ;
i = 0 ;
b = 0 ;
}
}
if ( i ! = 0 ) {
fputc ( b , out ) ;
}
}
break ;
case 8 :
fprintf ( out , " P%c \n %d \n %d \n %d \n " , channels = = 1 ? ' 5 ' : ' 6 ' , pixels_per_line , lines , 255 ) ;
for ( r = 0 ; r < lines ; r + + ) {
for ( c = 0 ; c < pixels_per_line ; c + + ) {
for ( ch = 0 ; ch < channels ; ch + + ) {
val = * ( data + ch * lines * pixels_per_line + r * pixels_per_line + c ) ;
b = val & 0xFF ;
fputc ( b , out ) ;
}
}
}
break ;
case 16 :
fprintf ( out , " P%c \n %d \n %d \n %d \n " , channels = = 1 ? ' 5 ' : ' 6 ' , pixels_per_line , lines , 65535 ) ;
for ( r = 0 ; r < lines ; r + + ) {
for ( c = 0 ; c < pixels_per_line ; c + + ) {
for ( ch = 0 ; ch < channels ; ch + + ) {
val = * ( data + ch * lines * pixels_per_line + r * pixels_per_line + c ) ;
b = ( val > > 8 ) & 0xFF ;
fputc ( b , out ) ;
b = val & 0xFF ;
fputc ( b , out ) ;
}
}
}
break ;
default :
DBG ( DBG_error , " pie_usb_write_pnm_file: depth %d not implemented \n " , depth ) ;
}
fclose ( out ) ;
DBG ( DBG_info , " pie_usb_write_pnm_file: finished \n " ) ;
return SANE_STATUS_GOOD ;
}
/**
* Check option inconsistencies .
* In most cases an inconsistency can be solved by ignoring an option setting .
* Message these situations and return 1 to indicate we can work with the
* current set op options . If the settings are really inconsistent , return 0.
*/
int
sanei_pieusb_analyse_options ( struct Pieusb_Scanner * scanner )
{
/* Checks*/
if ( scanner - > val [ OPT_TL_X ] . w > scanner - > val [ OPT_BR_X ] . w ) {
DBG ( DBG_error , " sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) -- aborting \n " ,
scanner - > opt [ OPT_TL_X ] . title ,
SANE_UNFIX ( scanner - > val [ OPT_TL_X ] . w ) ,
scanner - > opt [ OPT_BR_X ] . title ,
SANE_UNFIX ( scanner - > val [ OPT_BR_X ] . w ) ) ;
return 0 ;
}
if ( scanner - > val [ OPT_TL_Y ] . w > scanner - > val [ OPT_BR_Y ] . w ) {
DBG ( DBG_error , " sane_start: %s (%.1f mm) is bigger than %s (%.1f mm) -- aborting \n " ,
scanner - > opt [ OPT_TL_Y ] . title ,
SANE_UNFIX ( scanner - > val [ OPT_TL_Y ] . w ) ,
scanner - > opt [ OPT_BR_Y ] . title ,
SANE_UNFIX ( scanner - > val [ OPT_BR_Y ] . w ) ) ;
return 0 ;
}
/* Modes sometimes limit other choices */
if ( scanner - > val [ OPT_PREVIEW ] . b ) {
/* Preview uses its own specific settings */
if ( scanner - > val [ OPT_RESOLUTION ] . w ! = ( scanner - > device - > fast_preview_resolution < < SANE_FIXED_SCALE_SHIFT ) ) {
DBG ( DBG_info_sane , " Option %s = %f ignored during preview \n " , scanner - > opt [ OPT_RESOLUTION ] . name , SANE_UNFIX ( scanner - > val [ OPT_RESOLUTION ] . w ) ) ;
}
if ( scanner - > val [ OPT_SHARPEN ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_SHARPEN ] . name , scanner - > val [ OPT_SHARPEN ] . b ) ;
}
if ( ! scanner - > val [ OPT_FAST_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_FAST_INFRARED ] . name , scanner - > val [ OPT_FAST_INFRARED ] . b ) ;
}
if ( scanner - > val [ OPT_CORRECT_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_CORRECT_INFRARED ] . name , scanner - > val [ OPT_CORRECT_INFRARED ] . b ) ;
}
if ( scanner - > val [ OPT_CLEAN_IMAGE ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_CLEAN_IMAGE ] . name , scanner - > val [ OPT_CLEAN_IMAGE ] . b ) ;
}
if ( scanner - > val [ OPT_SMOOTH_IMAGE ] . w ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_SMOOTH_IMAGE ] . name , scanner - > val [ OPT_SMOOTH_IMAGE ] . w ) ;
}
if ( strcmp ( scanner - > val [ OPT_CROP_IMAGE ] . s , scanner - > device - > crop_sw_list [ 0 ] ) ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %s ignored during preview \n " , scanner - > opt [ OPT_CROP_IMAGE ] . name , scanner - > val [ OPT_CROP_IMAGE ] . s ) ;
}
if ( scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . name , scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . w ) ;
}
if ( scanner - > val [ OPT_INVERT_IMAGE ] . w ) {
DBG ( DBG_info_sane , " Option %s = %d ignored during preview \n " , scanner - > opt [ OPT_INVERT_IMAGE ] . name , scanner - > val [ OPT_INVERT_IMAGE ] . w ) ;
}
} else if ( strcmp ( scanner - > val [ OPT_MODE ] . s , SANE_VALUE_SCAN_MODE_LINEART ) = = 0 ) {
/* Can we do any post processing in lineart? Needs testing to see what's possible */
if ( scanner - > val [ OPT_BIT_DEPTH ] . w ! = 1 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (will use 1) \n " , scanner - > opt [ OPT_BIT_DEPTH ] . name , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
}
if ( ! scanner - > val [ OPT_FAST_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_FAST_INFRARED ] . name , scanner - > val [ OPT_FAST_INFRARED ] . b ) ;
}
if ( ! scanner - > val [ OPT_CORRECT_SHADING ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_CORRECT_SHADING ] . name , scanner - > val [ OPT_CORRECT_SHADING ] . b ) ;
}
if ( ! scanner - > val [ OPT_CORRECT_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_CORRECT_INFRARED ] . name , scanner - > val [ OPT_CORRECT_INFRARED ] . b ) ;
}
if ( scanner - > val [ OPT_CLEAN_IMAGE ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_CLEAN_IMAGE ] . name , scanner - > val [ OPT_CLEAN_IMAGE ] . b ) ;
}
if ( scanner - > val [ OPT_SMOOTH_IMAGE ] . w ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_SMOOTH_IMAGE ] . name , scanner - > val [ OPT_SMOOTH_IMAGE ] . w ) ;
}
if ( strcmp ( scanner - > val [ OPT_CROP_IMAGE ] . s , scanner - > device - > crop_sw_list [ 0 ] ) ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %s ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_CROP_IMAGE ] . name , scanner - > val [ OPT_CROP_IMAGE ] . s ) ;
}
if ( scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in lineart mode (irrelevant) \n " , scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . name , scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . w ) ;
}
} else if ( strcmp ( scanner - > val [ OPT_MODE ] . s , SANE_VALUE_SCAN_MODE_HALFTONE ) = = 0 ) {
/* Can we do any post processing in halftone? Needs testing to see what's possible */
if ( scanner - > val [ OPT_BIT_DEPTH ] . w ! = 1 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (will use 1) \n " , scanner - > opt [ OPT_BIT_DEPTH ] . name , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
}
if ( ! scanner - > val [ OPT_FAST_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_FAST_INFRARED ] . name , scanner - > val [ OPT_FAST_INFRARED ] . b ) ;
}
if ( ! scanner - > val [ OPT_CORRECT_SHADING ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_CORRECT_SHADING ] . name , scanner - > val [ OPT_CORRECT_SHADING ] . b ) ;
}
if ( ! scanner - > val [ OPT_CORRECT_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_CORRECT_INFRARED ] . name , scanner - > val [ OPT_CORRECT_INFRARED ] . b ) ;
}
if ( scanner - > val [ OPT_CLEAN_IMAGE ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_CLEAN_IMAGE ] . name , scanner - > val [ OPT_CLEAN_IMAGE ] . b ) ;
}
if ( scanner - > val [ OPT_SMOOTH_IMAGE ] . w ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_SMOOTH_IMAGE ] . name , scanner - > val [ OPT_SMOOTH_IMAGE ] . w ) ;
}
if ( strcmp ( scanner - > val [ OPT_CROP_IMAGE ] . s , scanner - > device - > crop_sw_list [ 0 ] ) ! = 0 ) {
DBG ( DBG_info_sane , " Option %s = %s ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_CROP_IMAGE ] . name , scanner - > val [ OPT_CROP_IMAGE ] . s ) ;
}
if ( scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in halftone mode (irrelevant) \n " , scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . name , scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . w ) ;
}
} else if ( strcmp ( scanner - > val [ OPT_MODE ] . s , SANE_VALUE_SCAN_MODE_GRAY ) = = 0 ) {
/* Can we do any post processing in gray mode? */
/* Can we obtain a single color channel in this mode? How? */
/* Is this just RGB with luminance trasformation? */
/* Needs testing to see what's possible */
/* Only do 8 or 16 bit scans */
if ( scanner - > val [ OPT_BIT_DEPTH ] . w = = 1 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in gray mode (will use 8) \n " , scanner - > opt [ OPT_BIT_DEPTH ] . name , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
}
if ( ! scanner - > val [ OPT_FAST_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in gray mode (irrelevant) \n " , scanner - > opt [ OPT_FAST_INFRARED ] . name , scanner - > val [ OPT_FAST_INFRARED ] . b ) ;
}
if ( ! scanner - > val [ OPT_CORRECT_INFRARED ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in gray mode (irrelevant) \n " , scanner - > opt [ OPT_CORRECT_INFRARED ] . name , scanner - > val [ OPT_CORRECT_INFRARED ] . b ) ;
}
if ( scanner - > val [ OPT_CLEAN_IMAGE ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in gray mode (irrelevant) \n " , scanner - > opt [ OPT_CLEAN_IMAGE ] . name , scanner - > val [ OPT_CLEAN_IMAGE ] . b ) ;
}
if ( scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . b ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in gray mode (irrelevant) \n " , scanner - > opt [ OPT_TRANSFORM_TO_SRGB ] . name , scanner - > val [ OPT_TRANSFORM_TO_SRGB ] . w ) ;
}
} else if ( strcmp ( scanner - > val [ OPT_MODE ] . s , SANE_VALUE_SCAN_MODE_COLOR ) = = 0 ) {
/* Some options require infrared data to be obtained, so all infrared options are relevant */
/* Only do 8 or 16 bit scans */
if ( scanner - > val [ OPT_BIT_DEPTH ] . w = = 1 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in color mode (will use 8) \n " , scanner - > opt [ OPT_BIT_DEPTH ] . name , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
}
} else if ( strcmp ( scanner - > val [ OPT_MODE ] . s , SANE_VALUE_SCAN_MODE_RGBI ) = = 0 ) {
/* Only do 8 or 16 bit scans */
if ( scanner - > val [ OPT_BIT_DEPTH ] . w = = 1 ) {
DBG ( DBG_info_sane , " Option %s = %d ignored in color mode (will use 8) \n " , scanner - > opt [ OPT_BIT_DEPTH ] . name , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
}
}
return 1 ;
}
/**
* Print options
*
* @ param scanner
*/
void
sanei_pieusb_print_options ( struct Pieusb_Scanner * scanner )
{
int k ;
/* List current options and values */
DBG ( DBG_info , " Num options = %d \n " , scanner - > val [ OPT_NUM_OPTS ] . w ) ;
for ( k = 1 ; k < scanner - > val [ OPT_NUM_OPTS ] . w ; k + + ) {
switch ( scanner - > opt [ k ] . type ) {
case SANE_TYPE_BOOL :
DBG ( DBG_info , " Option %d: %s = %d \n " , k , scanner - > opt [ k ] . name , scanner - > val [ k ] . b ) ;
break ;
case SANE_TYPE_INT :
DBG ( DBG_info , " Option %d: %s = %d \n " , k , scanner - > opt [ k ] . name , scanner - > val [ k ] . w ) ;
break ;
case SANE_TYPE_FIXED :
DBG ( DBG_info , " Option %d: %s = %f \n " , k , scanner - > opt [ k ] . name , SANE_UNFIX ( scanner - > val [ k ] . w ) ) ;
break ;
case SANE_TYPE_STRING :
DBG ( DBG_info , " Option %d: %s = %s \n " , k , scanner - > opt [ k ] . name , scanner - > val [ k ] . s ) ;
break ;
case SANE_TYPE_GROUP :
DBG ( DBG_info , " Option %d: %s = %s \n " , k , scanner - > opt [ k ] . title , scanner - > val [ k ] . s ) ;
break ;
default :
DBG ( DBG_info , " Option %d: %s unknown type %d \n " , k , scanner - > opt [ k ] . name , scanner - > opt [ k ] . type ) ;
break ;
}
}
}
/**
* Calculate reference values for each pixel , line means and line maxima .
* We have got 45 lines for all four colors and for each CCD pixel .
* The reference value for each pixel is the 45 - line average for that
* pixel , for each color separately .
*
* @ param scanner
* @ param buffer
*/
static void pieusb_calculate_shading ( struct Pieusb_Scanner * scanner , SANE_Byte * buffer )
{
int k , m ;
SANE_Byte * p ;
SANE_Int ci , val ;
SANE_Int shading_width = scanner - > device - > shading_parameters [ 0 ] . pixelsPerLine ;
SANE_Int shading_height = scanner - > device - > shading_parameters [ 0 ] . nLines ;
/* Initialze all to 0 */
for ( k = 0 ; k < SHADING_PARAMETERS_INFO_COUNT ; k + + ) {
scanner - > shading_max [ k ] = 0 ;
scanner - > shading_mean [ k ] = 0 ;
memset ( scanner - > shading_ref [ k ] , 0 , shading_width * sizeof ( SANE_Int ) ) ;
}
/* Process data from buffer */
p = buffer ;
switch ( scanner - > mode . colorFormat ) {
case 0x01 : /* Pixel */
/* Process pixel by pixel */
for ( k = 0 ; k < shading_height ; k + + ) {
for ( m = 0 ; m < shading_width ; m + + ) {
for ( ci = 0 ; ci < SHADING_PARAMETERS_INFO_COUNT ; ci + + ) {
val = * ( p ) + * ( p + 1 ) * 256 ;
scanner - > shading_ref [ ci ] [ m ] + = val ;
scanner - > shading_max [ ci ] = scanner - > shading_max [ ci ] < val ? val : scanner - > shading_max [ ci ] ;
p + = 2 ;
}
}
}
break ;
case 0x04 : /* Indexed */
/* Process each line in the sequence found in the buffer */
for ( k = 0 ; k < shading_height * 4 ; k + + ) {
/* Save at right color */
switch ( * p ) {
case ' R ' : ci = 0 ; break ;
case ' G ' : ci = 1 ; break ;
case ' B ' : ci = 2 ; break ;
case ' I ' : ci = 3 ; break ;
default : ci = - 1 ; break ; /* ignore line */
}
/* Add scanned data to reference line and keep track of maximum */
if ( ci ! = - 1 ) {
for ( m = 0 ; m < shading_width ; m + + ) {
val = * ( p + 2 + 2 * m ) + * ( p + 2 + 2 * m + 1 ) * 256 ;
scanner - > shading_ref [ ci ] [ m ] + = val ;
scanner - > shading_max [ ci ] = scanner - > shading_max [ ci ] < val ? val : scanner - > shading_max [ ci ] ;
/* DBG(DBG_error,"%02d Shading_ref[%d][%d] = %d\n",k,ci,m,scanner->shading_ref[ci][m]); */
}
}
/* Next line */
p + = 2 * shading_width + 2 ;
}
break ;
default :
DBG ( DBG_error , " sane_start(): color format %d not implemented \n " , scanner - > mode . colorFormat ) ;
return ;
}
/* Mean reference value needs division */
for ( k = 0 ; k < SHADING_PARAMETERS_INFO_COUNT ; k + + ) {
for ( m = 0 ; m < shading_width ; m + + ) {
scanner - > shading_ref [ k ] [ m ] = lround ( ( double ) scanner - > shading_ref [ k ] [ m ] / shading_height ) ;
/* DBG(DBG_error,"Shading_ref[%d][%d] = %d\n",k,m,scanner->shading_ref[k][m]); */
}
}
/* Overall means */
for ( k = 0 ; k < SHADING_PARAMETERS_INFO_COUNT ; k + + ) {
for ( m = 0 ; m < shading_width ; m + + ) {
scanner - > shading_mean [ k ] + = scanner - > shading_ref [ k ] [ m ] ;
}
scanner - > shading_mean [ k ] = lround ( ( double ) scanner - > shading_mean [ k ] / shading_width ) ;
DBG ( DBG_error , " Shading_mean[%d] = %d \n " , k , scanner - > shading_mean [ k ] ) ;
}
/* Set shading data present */
scanner - > shading_data_present = SANE_TRUE ;
/* Export shading data as TIFF */
# ifdef CAN_DO_4_CHANNEL_TIFF
if ( scanner - > val [ OPT_SAVE_SHADINGDATA ] . b ) {
struct Pieusb_Read_Buffer shading ;
SANE_Byte * lboff = buffer ;
SANE_Int bpl = shading_width * 2 ;
SANE_Int n ;
buffer_create ( & shading , shading_width , shading_height , 0x0F , 16 ) ;
for ( n = 0 ; n < 4 * shading_height ; n + + ) {
if ( buffer_put_single_color_line ( & shading , * lboff , lboff + 2 , bpl ) = = 0 ) {
break ;
}
lboff + = ( bpl + 2 ) ;
}
FILE * fs = fopen ( " pieusb.shading " , " w " ) ;
/* write_tiff_rgbi_header (fs, shading_width, shading_height, 16, 3600, NULL); */
fwrite ( shading . data , 1 , shading . image_size_bytes , fs ) ;
fclose ( fs ) ;
buffer_delete ( & shading ) ;
}
# endif
}
/*
* Set frame ( from scanner options )
*/
SANE_Status
sanei_pieusb_set_frame_from_options ( Pieusb_Scanner * scanner )
{
double dpmm ;
struct Pieusb_Command_Status status ;
dpmm = ( double ) scanner - > device - > maximum_resolution / MM_PER_INCH ;
scanner - > frame . x0 = SANE_UNFIX ( scanner - > val [ OPT_TL_X ] . w ) * dpmm ;
scanner - > frame . y0 = SANE_UNFIX ( scanner - > val [ OPT_TL_Y ] . w ) * dpmm ;
scanner - > frame . x1 = SANE_UNFIX ( scanner - > val [ OPT_BR_X ] . w ) * dpmm ;
scanner - > frame . y1 = SANE_UNFIX ( scanner - > val [ OPT_BR_Y ] . w ) * dpmm ;
scanner - > frame . index = 0x80 ; /* 0x80: value from cyberview */
sanei_pieusb_cmd_set_scan_frame ( scanner - > device_number , scanner - > frame . index , & ( scanner - > frame ) , & status ) ;
DBG ( DBG_info_sane , " sanei_pieusb_set_frame_from_options(): sanei_pieusb_cmd_set_scan_frame status %s \n " , sane_strstatus ( sanei_pieusb_convert_status ( status . pieusb_status ) ) ) ;
return status . pieusb_status ;
}
/*
* Set mode ( from scanner options )
*/
SANE_Status
sanei_pieusb_set_mode_from_options ( Pieusb_Scanner * scanner )
{
struct Pieusb_Command_Status status ;
const char * mode ;
SANE_Status res ;
mode = scanner - > val [ OPT_MODE ] . s ;
if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_LINEART ) = = 0 ) {
scanner - > mode . passes = SCAN_FILTER_GREEN ; /* G */
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_PIXEL ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_HALFTONE ) = = 0 ) {
scanner - > mode . passes = SCAN_FILTER_GREEN ; /* G */
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_PIXEL ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_GRAY ) = = 0 ) {
scanner - > mode . passes = SCAN_FILTER_GREEN ; /* G=gray; unable to get R & B & I to work */
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_PIXEL ;
} else if ( scanner - > val [ OPT_PREVIEW ] . b ) {
/* Catch preview here, otherwise next ifs get complicated */
scanner - > mode . passes = SCAN_ONE_PASS_COLOR ;
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_INDEX ; /* pixel format might be an alternative */
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_RGBI ) = = 0 ) {
scanner - > mode . passes = SCAN_ONE_PASS_RGBI ;
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_INDEX ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_COLOR ) = = 0 & & scanner - > val [ OPT_CLEAN_IMAGE ] . b ) {
scanner - > mode . passes = SCAN_ONE_PASS_RGBI ; /* Need infrared for cleaning */
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_INDEX ;
} else { /* SANE_VALUE_SCAN_MODE_COLOR */
scanner - > mode . passes = SCAN_ONE_PASS_COLOR ;
scanner - > mode . colorFormat = SCAN_COLOR_FORMAT_INDEX ; /* pixel format might be an alternative */
}
/* Resolution */
if ( scanner - > val [ OPT_PREVIEW ] . b ) {
scanner - > mode . resolution = scanner - > device - > fast_preview_resolution ;
DBG ( DBG_info_sane , " sanei_pieusb_set_mode_from_options(): resolution fast preview (%d) \n " , scanner - > mode . resolution ) ;
} else {
scanner - > mode . resolution = SANE_UNFIX ( scanner - > val [ OPT_RESOLUTION ] . w ) ;
DBG ( DBG_info_sane , " sanei_pieusb_set_mode_from_options(): resolution from option setting (%d) \n " , scanner - > mode . resolution ) ;
}
/* Bit depth: exit on untested values */
switch ( scanner - > val [ OPT_BIT_DEPTH ] . w ) {
case 1 : scanner - > mode . colorDepth = SCAN_COLOR_DEPTH_1 ; break ;
case 8 : scanner - > mode . colorDepth = SCAN_COLOR_DEPTH_8 ; break ;
case 16 : scanner - > mode . colorDepth = SCAN_COLOR_DEPTH_16 ; break ;
default : /* 4, 10 & 12 */
DBG ( DBG_error , " sanei_pieusb_set_mode_from_options(): sanei_pieusb_cmd_set_scan_frame untested bit depth %d \n " , scanner - > val [ OPT_BIT_DEPTH ] . w ) ;
return SANE_STATUS_INVAL ;
}
scanner - > mode . byteOrder = 0x01 ; /* 0x01 = Intel; only bit 0 used */
scanner - > mode . sharpen = scanner - > val [ OPT_SHARPEN ] . b & & ! scanner - > val [ OPT_PREVIEW ] . b ;
scanner - > mode . skipShadingAnalysis = ! scanner - > val [ OPT_SHADING_ANALYSIS ] . b ;
scanner - > mode . fastInfrared = scanner - > val [ OPT_FAST_INFRARED ] . b & & ! scanner - > val [ OPT_PREVIEW ] . b ;
if ( strcmp ( scanner - > val [ OPT_HALFTONE_PATTERN ] . s , " 53lpi 45d ROUND " ) = = 0 ) {
scanner - > mode . halftonePattern = 0 ;
} else { /*TODO: the others */
scanner - > mode . halftonePattern = 0 ;
}
scanner - > mode . lineThreshold = SANE_UNFIX ( scanner - > val [ OPT_THRESHOLD ] . w ) / 100 * 0xFF ; /* 0xFF = 100% */
sanei_pieusb_cmd_set_mode ( scanner - > device_number , & ( scanner - > mode ) , & status ) ;
res = sanei_pieusb_convert_status ( status . pieusb_status ) ;
if ( res = = SANE_STATUS_GOOD ) {
res = sanei_pieusb_wait_ready ( scanner , 0 ) ;
}
DBG ( DBG_info_sane , " sanei_pieusb_set_mode_from_options(): sanei_pieusb_cmd_set_mode status %s \n " , sane_strstatus ( res ) ) ;
return res ;
}
/**
* Set gains , exposure and offset , to :
* - values default ( pieusb_set_default_gain_offset )
* - values set by options
* - values set by auto - calibration procedure
* - values determined from preceeding preview
*
* @ param scanner
* @ return
*/
SANE_Status
sanei_pieusb_set_gain_offset ( Pieusb_Scanner * scanner , const char * calibration_mode )
{
struct Pieusb_Command_Status status ;
SANE_Status ret ;
double gain ;
DBG ( DBG_info , " sanei_pieusb_set_gain_offset(): mode = %s \n " , calibration_mode ) ;
if ( strcmp ( calibration_mode , SCAN_CALIBRATION_DEFAULT ) = = 0 ) {
/* Default values */
DBG ( DBG_info_sane , " sanei_pieusb_set_gain_offset(): get calibration data from defaults \n " ) ;
scanner - > settings . exposureTime [ 0 ] = DEFAULT_EXPOSURE ;
scanner - > settings . exposureTime [ 1 ] = DEFAULT_EXPOSURE ;
scanner - > settings . exposureTime [ 2 ] = DEFAULT_EXPOSURE ;
scanner - > settings . exposureTime [ 3 ] = DEFAULT_EXPOSURE ;
scanner - > settings . offset [ 0 ] = DEFAULT_OFFSET ;
scanner - > settings . offset [ 1 ] = DEFAULT_OFFSET ;
scanner - > settings . offset [ 2 ] = DEFAULT_OFFSET ;
scanner - > settings . offset [ 3 ] = DEFAULT_OFFSET ;
scanner - > settings . gain [ 0 ] = DEFAULT_GAIN ;
scanner - > settings . gain [ 1 ] = DEFAULT_GAIN ;
scanner - > settings . gain [ 2 ] = DEFAULT_GAIN ;
scanner - > settings . gain [ 3 ] = DEFAULT_GAIN ;
scanner - > settings . light = DEFAULT_LIGHT ;
scanner - > settings . extraEntries = DEFAULT_ADDITIONAL_ENTRIES ;
scanner - > settings . doubleTimes = DEFAULT_DOUBLE_TIMES ;
status . pieusb_status = PIEUSB_STATUS_GOOD ;
} else if ( ( strcmp ( calibration_mode , SCAN_CALIBRATION_PREVIEW ) = = 0 )
& & scanner - > preview_done ) {
/* If no preview data availble, do the auto-calibration. */
double dg , dgi ;
DBG ( DBG_info , " sanei_pieusb_set_gain_offset(): get calibration data from preview. scanner->mode.passes %d \n " , scanner - > mode . passes ) ;
switch ( scanner - > mode . passes ) {
case SCAN_ONE_PASS_RGBI :
dg = 3.00 ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 0 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 0 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 1 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 1 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 2 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 2 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
updateGain2 ( scanner , 0 , dg ) ;
updateGain2 ( scanner , 1 , dg ) ;
updateGain2 ( scanner , 2 , dg ) ;
break ;
case SCAN_ONE_PASS_COLOR :
dg = 3.00 ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 0 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 0 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 1 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 1 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 2 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 2 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
updateGain2 ( scanner , 0 , dg ) ;
updateGain2 ( scanner , 1 , dg ) ;
updateGain2 ( scanner , 2 , dg ) ;
break ;
case SCAN_FILTER_BLUE :
dg = 3.00 ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 2 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 2 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
updateGain2 ( scanner , 2 , dg ) ;
break ;
case SCAN_FILTER_GREEN :
dg = 3.00 ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 1 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 1 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
updateGain2 ( scanner , 1 , dg ) ;
break ;
case SCAN_FILTER_RED :
dg = 3.00 ;
dgi = ( ( double ) scanner - > settings . saturationLevel [ 0 ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ 0 ] / HISTOGRAM_SIZE ) ;
if ( dgi < dg ) dg = dgi ;
updateGain2 ( scanner , 0 , dg ) ;
break ;
case SCAN_FILTER_NEUTRAL :
break ;
}
status . pieusb_status = PIEUSB_STATUS_GOOD ;
} else if ( strcmp ( calibration_mode , SCAN_CALIBRATION_OPTIONS ) = = 0 ) {
DBG ( DBG_info_sane , " sanei_pieusb_set_gain_offset(): get calibration data from options \n " ) ;
/* Exposure times */
scanner - > settings . exposureTime [ 0 ] = scanner - > val [ OPT_SET_EXPOSURE_R ] . w ;
scanner - > settings . exposureTime [ 1 ] = scanner - > val [ OPT_SET_EXPOSURE_G ] . w ;
scanner - > settings . exposureTime [ 2 ] = scanner - > val [ OPT_SET_EXPOSURE_B ] . w ;
scanner - > settings . exposureTime [ 3 ] = scanner - > val [ OPT_SET_EXPOSURE_I ] . w ; /* Infrared */
/* Offsets */
scanner - > settings . offset [ 0 ] = scanner - > val [ OPT_SET_OFFSET_R ] . w ;
scanner - > settings . offset [ 1 ] = scanner - > val [ OPT_SET_OFFSET_G ] . w ;
scanner - > settings . offset [ 2 ] = scanner - > val [ OPT_SET_OFFSET_B ] . w ;
scanner - > settings . offset [ 3 ] = scanner - > val [ OPT_SET_OFFSET_I ] . w ; /* Infrared */
/* Gains */
scanner - > settings . gain [ 0 ] = scanner - > val [ OPT_SET_GAIN_R ] . w ;
scanner - > settings . gain [ 1 ] = scanner - > val [ OPT_SET_GAIN_G ] . w ;
scanner - > settings . gain [ 2 ] = scanner - > val [ OPT_SET_GAIN_B ] . w ;
scanner - > settings . gain [ 3 ] = scanner - > val [ OPT_SET_GAIN_I ] . w ; /* Infrared */
/* Light, extra entries and doubling */
scanner - > settings . light = scanner - > val [ OPT_LIGHT ] . w ;
scanner - > settings . extraEntries = DEFAULT_ADDITIONAL_ENTRIES ;
scanner - > settings . doubleTimes = scanner - > val [ OPT_DOUBLE_TIMES ] . w ;
status . pieusb_status = PIEUSB_STATUS_GOOD ;
} else { /* SCAN_CALIBRATION_AUTO */
DBG ( DBG_info_sane , " sanei_pieusb_set_gain_offset(): get calibration data from scanner \n " ) ;
sanei_pieusb_cmd_get_gain_offset ( scanner - > device_number , & scanner - > settings , & status ) ;
}
/* Check status */
if ( status . pieusb_status = = PIEUSB_STATUS_DEVICE_BUSY ) {
ret = sanei_pieusb_wait_ready ( scanner , 0 ) ;
if ( ret ! = SANE_STATUS_GOOD ) {
DBG ( DBG_error , " sanei_pieusb_set_gain_offset(): not ready after sanei_pieusb_cmd_get_gain_offset(): %d \n " , ret ) ;
return ret ;
}
}
else if ( status . pieusb_status ! = PIEUSB_STATUS_GOOD ) {
return SANE_STATUS_INVAL ;
}
/* Adjust gain */
gain = 1.0 ;
if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_03 ) = = 0 ) {
gain = 0.3 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_05 ) = = 0 ) {
gain = 0.5 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_08 ) = = 0 ) {
gain = 0.8 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_10 ) = = 0 ) {
gain = 1.0 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_12 ) = = 0 ) {
gain = 1.2 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_16 ) = = 0 ) {
gain = 1.6 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_19 ) = = 0 ) {
gain = 1.9 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_24 ) = = 0 ) {
gain = 2.4 ;
} else if ( strcmp ( scanner - > val [ OPT_GAIN_ADJUST ] . s , SCAN_GAIN_ADJUST_30 ) = = 0 ) {
gain = 3.0 ;
}
switch ( scanner - > mode . passes ) {
case SCAN_ONE_PASS_RGBI :
case SCAN_ONE_PASS_COLOR :
updateGain2 ( scanner , 0 , gain ) ;
updateGain2 ( scanner , 1 , gain ) ;
updateGain2 ( scanner , 2 , gain ) ;
/* Don't correct IR, hampers cleaning process... */
break ;
case SCAN_FILTER_INFRARED :
updateGain2 ( scanner , 3 , gain ) ;
break ;
case SCAN_FILTER_BLUE :
updateGain2 ( scanner , 2 , gain ) ;
break ;
case SCAN_FILTER_GREEN :
updateGain2 ( scanner , 1 , gain ) ;
break ;
case SCAN_FILTER_RED :
updateGain2 ( scanner , 0 , gain ) ;
break ;
case SCAN_FILTER_NEUTRAL :
break ;
}
/* Now set values for gain, offset and exposure */
sanei_pieusb_cmd_set_gain_offset ( scanner - > device_number , & ( scanner - > settings ) , & status ) ;
ret = sanei_pieusb_convert_status ( status . pieusb_status ) ;
DBG ( DBG_info_sane , " sanei_pieusb_set_gain_offset(): status %s \n " , sane_strstatus ( ret ) ) ;
return ret ;
}
/*
* get shading data
* must be called immediately after sanei_pieusb_set_gain_offset
*/
SANE_Status
sanei_pieusb_get_shading_data ( Pieusb_Scanner * scanner )
{
struct Pieusb_Command_Status status ;
SANE_Int shading_width ;
SANE_Int shading_height ;
SANE_Byte * buffer ;
SANE_Int lines ;
SANE_Int cols ;
SANE_Int size ;
SANE_Status res = SANE_STATUS_GOOD ;
DBG ( DBG_info_sane , " sanei_pieusb_get_shading_data() \n " ) ;
shading_width = scanner - > device - > shading_parameters [ 0 ] . pixelsPerLine ;
shading_height = scanner - > device - > shading_parameters [ 0 ] . nLines ;
if ( shading_height < 1 ) {
DBG ( DBG_error , " shading_height < 1 \n " ) ;
return SANE_STATUS_INVAL ;
}
switch ( scanner - > mode . colorFormat ) {
case SCAN_COLOR_FORMAT_PIXEL : /* Pixel */
lines = shading_height * 4 ;
cols = 2 * shading_width ;
break ;
case SCAN_COLOR_FORMAT_INDEX : /* Indexed */
lines = shading_height * 4 ;
cols = ( 2 * shading_width + 2 ) ;
break ;
default :
DBG ( DBG_error , " sanei_pieusb_get_shading_data(): color format %d not implemented \n " , scanner - > mode . colorFormat ) ;
return SANE_STATUS_INVAL ;
}
size = cols * lines ;
buffer = malloc ( size ) ;
if ( buffer = = NULL ) {
return SANE_STATUS_NO_MEM ;
}
sanei_pieusb_cmd_get_scanned_lines ( scanner - > device_number , buffer , 4 , cols * 4 , & status ) ;
if ( status . pieusb_status = = PIEUSB_STATUS_GOOD ) {
res = sanei_pieusb_wait_ready ( scanner , 0 ) ;
if ( res = = SANE_STATUS_GOOD ) {
sanei_pieusb_cmd_get_scanned_lines ( scanner - > device_number , buffer + cols * 4 , lines - 4 , ( lines - 4 ) * cols , & status ) ;
if ( status . pieusb_status = = PIEUSB_STATUS_GOOD ) {
pieusb_calculate_shading ( scanner , buffer ) ;
}
res = sanei_pieusb_convert_status ( status . pieusb_status ) ;
}
}
else {
res = sanei_pieusb_convert_status ( status . pieusb_status ) ;
}
free ( buffer ) ;
return res ;
}
/*
*
*/
SANE_Status
sanei_pieusb_get_ccd_mask ( Pieusb_Scanner * scanner )
{
struct Pieusb_Command_Status status ;
DBG ( DBG_info_proc , " sanei_pieusb_get_ccd_mask() \n " ) ;
sanei_pieusb_cmd_get_ccd_mask ( scanner - > device_number , scanner - > ccd_mask , scanner - > ccd_mask_size , & status ) ;
if ( status . pieusb_status = = PIEUSB_STATUS_GOOD ) {
/* Save CCD mask */
if ( scanner - > val [ OPT_SAVE_CCDMASK ] . b ) {
FILE * fs = fopen ( " pieusb.ccd " , " w " ) ;
fwrite ( scanner - > ccd_mask , 1 , scanner - > ccd_mask_size , fs ) ;
fclose ( fs ) ;
}
}
return sanei_pieusb_convert_status ( status . pieusb_status ) ;
}
/**
* Read parameters from scanner
* and initialize SANE parameters
*
* @ param scanner
* @ return parameter_bytes for use in get_scan_data ( )
*/
SANE_Status
sanei_pieusb_get_parameters ( Pieusb_Scanner * scanner , SANE_Int * parameter_bytes )
{
struct Pieusb_Command_Status status ;
struct Pieusb_Scan_Parameters parameters ;
const char * mode ;
DBG ( DBG_info_proc , " sanei_pieusb_get_parameters() \n " ) ;
sanei_pieusb_cmd_get_parameters ( scanner - > device_number , & parameters , & status ) ;
if ( status . pieusb_status ! = PIEUSB_STATUS_GOOD ) {
return sanei_pieusb_convert_status ( status . pieusb_status ) ;
}
* parameter_bytes = parameters . bytes ;
/* Use response from sanei_pieusb_cmd_get_parameters() for initialization of SANE parameters.
* Note the weird values of the bytes - field : this is because of the colorFormat
* setting in sanei_pieusb_cmd_set_mode ( ) . The single - color modes all use the pixel format ,
* which makes sanei_pieusb_cmd_get_parameters ( ) return a full color line although just
* one color actually contains data . For the index format , the bytes field
* gives the size of a single color line . */
mode = scanner - > val [ OPT_MODE ] . s ;
if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_LINEART ) = = 0 ) {
scanner - > scan_parameters . format = SANE_FRAME_GRAY ;
scanner - > scan_parameters . depth = 1 ;
scanner - > scan_parameters . bytes_per_line = parameters . bytes / 3 ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_HALFTONE ) = = 0 ) {
scanner - > scan_parameters . format = SANE_FRAME_GRAY ;
scanner - > scan_parameters . depth = 1 ;
scanner - > scan_parameters . bytes_per_line = parameters . bytes / 3 ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_GRAY ) = = 0 ) {
scanner - > scan_parameters . format = SANE_FRAME_GRAY ;
scanner - > scan_parameters . depth = scanner - > val [ OPT_BIT_DEPTH ] . w ;
scanner - > scan_parameters . bytes_per_line = parameters . bytes / 3 ;
} else if ( strcmp ( mode , SANE_VALUE_SCAN_MODE_RGBI ) = = 0 ) {
scanner - > scan_parameters . format = SANE_FRAME_RGB ; /* was: SANE_FRAME_RGBI */
scanner - > scan_parameters . depth = scanner - > val [ OPT_BIT_DEPTH ] . w ;
scanner - > scan_parameters . bytes_per_line = 4 * parameters . bytes ;
} else { /* SANE_VALUE_SCAN_MODE_COLOR, with and without option clean image set */
scanner - > scan_parameters . format = SANE_FRAME_RGB ;
scanner - > scan_parameters . depth = scanner - > val [ OPT_BIT_DEPTH ] . w ;
scanner - > scan_parameters . bytes_per_line = 3 * parameters . bytes ;
}
scanner - > scan_parameters . lines = parameters . lines ;
scanner - > scan_parameters . pixels_per_line = parameters . width ;
scanner - > scan_parameters . last_frame = SANE_TRUE ;
DBG ( DBG_info_sane , " sanei_pieusb_get_parameters(): mode '%s' \n " , mode ) ;
DBG ( DBG_info_sane , " format = %d \n " , scanner - > scan_parameters . format ) ;
DBG ( DBG_info_sane , " depth = %d \n " , scanner - > scan_parameters . depth ) ;
DBG ( DBG_info_sane , " bytes_per_line = %d \n " , scanner - > scan_parameters . bytes_per_line ) ;
DBG ( DBG_info_sane , " lines = %d \n " , scanner - > scan_parameters . lines ) ;
DBG ( DBG_info_sane , " pixels_per_line = %d \n " , scanner - > scan_parameters . pixels_per_line ) ;
DBG ( DBG_info_sane , " last_frame = %d \n " , scanner - > scan_parameters . last_frame ) ;
return SANE_STATUS_GOOD ;
}
SANE_Status
sanei_pieusb_get_scan_data ( Pieusb_Scanner * scanner , SANE_Int parameter_bytes )
{
struct Pieusb_Command_Status status ;
SANE_Parameters * parameters = & scanner - > scan_parameters ;
SANE_Int lines_to_read , lines_remaining ;
SANE_Int ppl , bpl ;
SANE_Byte * linebuf , * lboff ;
SANE_Bool compress ;
int n , k , i ;
switch ( scanner - > mode . colorFormat ) {
case SCAN_COLOR_FORMAT_PIXEL : /* Pixel */
lines_to_read = scanner - > buffer . height ;
break ;
case SCAN_COLOR_FORMAT_INDEX : /* Indexed */
lines_to_read = scanner - > buffer . colors * scanner - > buffer . height ;
break ;
default :
DBG ( DBG_error , " sanei_pieusb_get_scan_data(): color format %d not implemented \n " , scanner - > mode . colorFormat ) ;
return SANE_STATUS_INVAL ;
}
lines_remaining = lines_to_read ;
DBG ( DBG_info_proc , " sanei_pieusb_get_scan_data(colorFormat %d), lines_to_read %d, bytes %d \n " , scanner - > mode . colorFormat , lines_to_read , parameter_bytes ) ;
/*
fdraw = open ( " /tmp/pieusb.raw " , O_WRONLY | O_CREAT | O_TRUNC , ( mode_t ) 0600 ) ;
if ( fdraw = = - 1 ) {
perror ( " error opening raw image buffer file " ) ;
}
*/
while ( lines_remaining > 0 ) {
SANE_Int lines ;
/* Read lines */
/* The amount of bytes_per_line varies with color format setting; only 'pixel' and 'index' implemented */
ppl = parameters - > pixels_per_line ;
switch ( scanner - > mode . colorFormat ) {
case SCAN_COLOR_FORMAT_PIXEL : /* Pixel */
bpl = parameter_bytes ;
break ;
case SCAN_COLOR_FORMAT_INDEX : /* Indexed */
bpl = parameter_bytes + 2 ; /* Index bytes! */
break ;
default :
DBG ( DBG_error , " sanei_pieusb_get_scan_data(): color format %d not implemented \n " , scanner - > mode . colorFormat ) ;
return SANE_STATUS_INVAL ;
}
lines = ( lines_remaining < 256 ) ? lines_remaining : 255 ;
DBG ( DBG_info_sane , " sanei_pieusb_get_scan_data(): reading lines: now %d, bytes per line = %d \n " , lines , bpl ) ;
linebuf = malloc ( lines * bpl ) ;
sanei_pieusb_cmd_get_scanned_lines ( scanner - > device_number , linebuf , lines , lines * bpl , & status ) ;
if ( status . pieusb_status ! = PIEUSB_STATUS_GOOD ) {
/* Error, return */
free ( linebuf ) ;
return SANE_STATUS_INVAL ;
}
/* Save raw data */
/*
if ( fdraw ! = - 1 ) {
wcnt = write ( fdraw , linebuf , parameters . lines * bpl ) ;
DBG ( DBG_info_sane , " Raw written %d \n " , wcnt ) ;
}
*/
/* Copy into official buffer
* Sometimes the scanner returns too many lines . Take care not to
* overflow the buffer . */
lboff = linebuf ;
switch ( scanner - > mode . colorFormat ) {
case SCAN_COLOR_FORMAT_PIXEL :
/* The scanner may return lines with 3 colors even though only
* one color is actually scanned . Detect this situation and
* eliminate the excess samples from the line buffer before
* handing it to buffer_put_full_color_line ( ) . */
compress = SANE_FALSE ;
if ( scanner - > buffer . colors = = 1
& & ( bpl * scanner - > buffer . packing_density / ppl ) = = ( 3 * scanner - > buffer . packet_size_bytes ) ) {
compress = SANE_TRUE ;
}
for ( n = 0 ; n < lines ; n + + ) {
if ( compress ) {
/* Move samples to fill up all unused locations */
int ps = scanner - > buffer . packet_size_bytes ;
for ( k = 0 ; k < scanner - > buffer . line_size_packets ; k + + ) {
for ( i = 0 ; i < ps ; i + + ) {
lboff [ ps * k + i ] = lboff [ 3 * ps * k + i ] ;
}
}
}
if ( sanei_pieusb_buffer_put_full_color_line ( & scanner - > buffer , lboff , bpl / 3 ) = = 0 ) {
/* Error, return */
return SANE_STATUS_INVAL ;
}
lboff + = bpl ;
}
break ;
case SCAN_COLOR_FORMAT_INDEX :
/* Indexed data */
for ( n = 0 ; n < lines ; n + + ) {
if ( sanei_pieusb_buffer_put_single_color_line ( & scanner - > buffer , * lboff , lboff + 2 , bpl - 2 ) = = 0 ) {
/* Error, return */
return SANE_STATUS_INVAL ;
}
lboff + = bpl ;
}
break ;
default :
DBG ( DBG_error , " sanei_pieusb_get_scan_data(): store color format %d not implemented \n " , scanner - > mode . colorFormat ) ;
free ( linebuf ) ;
return SANE_STATUS_INVAL ;
}
free ( linebuf ) ;
lines_remaining - = lines ; /* Note: excess discarded */
DBG ( DBG_info_sane , " sanei_pieusb_get_scan_data(): reading lines: remaining %d \n " , lines_remaining ) ;
}
/*
if ( fdraw ! = - 1 ) close ( fdraw ) ;
*/
return SANE_STATUS_GOOD ;
}
/**
* Wait for scanner to get ready
*
* loop of test_ready / read_state
*
* @ param scanner
* @ param device_number , used if scanner = = NULL
* @ return SANE_Status
*/
SANE_Status
sanei_pieusb_wait_ready ( Pieusb_Scanner * scanner , SANE_Int device_number )
{
struct Pieusb_Command_Status status ;
struct Pieusb_Scanner_State state ;
time_t start , elapsed ;
DBG ( DBG_info_proc , " sanei_pieusb_wait_ready() \n " ) ;
start = time ( NULL ) ;
if ( scanner )
device_number = scanner - > device_number ;
for ( ; ; ) {
sanei_pieusb_cmd_test_unit_ready ( device_number , & status ) ;
DBG ( DBG_info_proc , " -> sanei_pieusb_cmd_test_unit_ready: %d \n " , status . pieusb_status ) ;
if ( status . pieusb_status = = PIEUSB_STATUS_GOOD )
break ;
if ( status . pieusb_status = = PIEUSB_STATUS_IO_ERROR )
break ;
sanei_pieusb_cmd_read_state ( device_number , & state , & status ) ;
DBG ( DBG_info_proc , " -> sanei_pieusb_cmd_read_state: %d \n " , status . pieusb_status ) ;
if ( status . pieusb_status ! = PIEUSB_STATUS_DEVICE_BUSY )
break ;
sleep ( 2 ) ;
elapsed = time ( NULL ) - start ;
if ( elapsed > 120 ) { /* 2 minute overall timeout */
DBG ( DBG_error , " scanner not ready after 2 minutes \n " ) ;
break ;
}
if ( elapsed % 2 ) {
DBG ( DBG_info , " still waiting for scanner to get ready \n " ) ;
}
}
return sanei_pieusb_convert_status ( status . pieusb_status ) ;
}
SANE_Status sanei_pieusb_analyze_preview ( Pieusb_Scanner * scanner )
{
int k , n ;
SANE_Parameters params ;
SANE_Int N ;
double * norm_histo ;
double level ;
DBG ( DBG_info , " sanei_pieusb_analyze_preview(): saving preview data \n " ) ;
/* Settings */
scanner - > preview_done = SANE_TRUE ;
for ( k = 0 ; k < 4 ; k + + ) {
scanner - > preview_exposure [ k ] = scanner - > settings . exposureTime [ k ] ;
scanner - > preview_gain [ k ] = scanner - > settings . gain [ k ] ;
scanner - > preview_offset [ k ] = scanner - > settings . offset [ k ] ;
}
/* Analyze color planes */
N = scanner - > buffer . width * scanner - > buffer . height ;
params . format = SANE_FRAME_GRAY ;
params . depth = scanner - > buffer . depth ;
params . pixels_per_line = scanner - > buffer . width ;
params . lines = scanner - > buffer . height ;
for ( k = 0 ; k < scanner - > buffer . colors ; k + + ) {
/* Create histogram for color k */
sanei_ir_create_norm_histogram ( & params , scanner - > buffer . data + k * N , & norm_histo ) ;
/* Find 1% and 99% limits */
level = 0 ;
for ( n = 0 ; n < HISTOGRAM_SIZE ; n + + ) {
level + = norm_histo [ n ] ;
if ( level < 0.01 ) {
scanner - > preview_lower_bound [ k ] = n ;
}
if ( level < 0.99 ) {
scanner - > preview_upper_bound [ k ] = n ;
}
}
DBG ( DBG_info , " sanei_pieusb_analyze_preview(): 1%%-99%% levels for color %d: %d - %d \n " , k , scanner - > preview_lower_bound [ k ] , scanner - > preview_upper_bound [ k ] ) ;
}
/* Disable remaining color planes */
for ( k = scanner - > buffer . colors ; k < 4 ; k + + ) {
scanner - > preview_lower_bound [ k ] = 0 ;
scanner - > preview_upper_bound [ k ] = 0 ;
}
return SANE_STATUS_GOOD ;
}
/**
* Return actual gain at given gain setting
*
* @ param gain Gain setting ( 0 - 63 )
* @ return
*/
static double getGain ( int gain )
{
int k ;
/* Actually an error, but don't be picky */
if ( gain < = 0 ) {
return gains [ 0 ] ;
}
/* A gain > 63 is also an error, but don't be picky */
if ( gain > = 60 ) {
return ( gain - 55 ) * ( gains [ 12 ] - gains [ 11 ] ) / 5 + gains [ 11 ] ;
}
/* Interpolate other values */
k = gain / 5 ; /* index of array value just below given gain */
return ( gain - 5 * k ) * ( gains [ k + 1 ] - gains [ k ] ) / 5 + gains [ k ] ;
}
static int getGainSetting ( double gain )
{
int k , m ;
/* Out of bounds */
if ( gain < 1.0 ) {
return 0 ;
}
if ( gain > = gains [ 12 ] ) {
m = 60 + lround ( ( gain - gains [ 11 ] ) / ( gains [ 12 ] - gains [ 11 ] ) * 5 ) ;
if ( m > 63 ) m = 63 ;
return m ;
}
/* Interpolate the rest */
m = 0 ;
for ( k = 0 ; k < = 11 ; k + + ) {
if ( gains [ k ] < = gain & & gain < gains [ k + 1 ] ) {
m = 5 * k + lround ( ( gain - gains [ k ] ) / ( gains [ k + 1 ] - gains [ k ] ) * 5 ) ;
}
}
return m ;
}
/**
* Modify gain and exposure times in order to make maximal use of the scan depth .
* Each color treated separately , infrared excluded .
*
* This may be too aggressive = > leads to a noisy whitish border instead of the orange .
* In a couuple of tries , gain was set to values of 60 and above , which introduces
* the noise ?
* The whitish border is logical since the brightest parts of the negative , the
* unexposed borders , are amplified to values near CCD saturation , which is white .
* Maybe a uniform gain increase for each color is more appropriate ? Somewhere
* between 2.5 and 3 seems worthwhile trying , see updateGain2 ( ) .
*
switch ( scanner - > mode . passes ) {
case SCAN_ONE_PASS_RGBI :
updateGain ( scanner , 0 ) ;
updateGain ( scanner , 1 ) ;
updateGain ( scanner , 2 ) ;
updateGain ( scanner , 3 ) ;
break ;
case SCAN_ONE_PASS_COLOR :
updateGain ( scanner , 0 ) ;
updateGain ( scanner , 1 ) ;
updateGain ( scanner , 2 ) ;
break ;
case SCAN_FILTER_INFRARED :
updateGain ( scanner , 3 ) ;
break ;
case SCAN_FILTER_BLUE :
updateGain ( scanner , 2 ) ;
break ;
case SCAN_FILTER_GREEN :
updateGain ( scanner , 1 ) ;
break ;
case SCAN_FILTER_RED :
updateGain ( scanner , 0 ) ;
break ;
case SCAN_FILTER_NEUTRAL :
break ;
}
* @ param scanner
*/
/*
static void updateGain ( Pieusb_Scanner * scanner , int color_index )
{
double g , dg ;
DBG ( DBG_info_sane , " updateGain(): color %d preview used G=%d Exp=%d \n " , color_index , scanner - > preview_gain [ color_index ] , scanner - > preview_exposure [ color_index ] ) ;
// Additional gain to obtain
dg = ( ( double ) scanner - > settings . saturationLevel [ color_index ] / 65536 ) / ( ( double ) scanner - > preview_upper_bound [ color_index ] / HISTOGRAM_SIZE ) ;
DBG ( DBG_info_sane , " updateGain(): additional gain %f \n " , dg ) ;
// Achieve this by modifying gain and exposure
// Gain used for preview
g = getGain ( scanner - > preview_gain [ color_index ] ) ;
DBG ( DBG_info_sane , " updateGain(): preview had gain %d => %f \n " , scanner - > preview_gain [ color_index ] , g ) ;
// Look up new gain setting g*sqrt(dg)
DBG ( DBG_info_sane , " updateGain(): optimized gain * %f = %f \n " , sqrt ( dg ) , sqrt ( dg ) * g ) ;
scanner - > settings . gain [ color_index ] = getGainSetting ( g * sqrt ( dg ) ) ;
DBG ( DBG_info_sane , " updateGain(): optimized gain setting %d => %f \n " , scanner - > settings . gain [ color_index ] , getGain ( scanner - > settings . gain [ color_index ] ) ) ;
// Exposure change is straightforward
DBG ( DBG_info_sane , " updateGain(): remains for exposure %f \n " , dg / ( getGain ( scanner - > settings . gain [ color_index ] ) / g ) ) ;
scanner - > settings . exposureTime [ color_index ] = lround ( g / getGain ( scanner - > settings . gain [ color_index ] ) * dg * scanner - > preview_exposure [ color_index ] ) ;
DBG ( DBG_info_sane , " updateGain(): new setting G=%d Exp=%d \n " , scanner - > settings . gain [ color_index ] , scanner - > settings . exposureTime [ color_index ] ) ;
}
*/
static void updateGain2 ( Pieusb_Scanner * scanner , int color_index , double gain_increase )
{
double g ;
DBG ( DBG_info , " updateGain2(): color %d preview used G=%d Exp=%d \n " , color_index , scanner - > settings . gain [ color_index ] , scanner - > settings . exposureTime [ color_index ] ) ;
/* Additional gain to obtain */
DBG ( DBG_info , " updateGain2(): additional gain %f \n " , gain_increase ) ;
/* Achieve this by modifying gain and exposure */
/* Gain used for preview */
g = getGain ( scanner - > settings . gain [ color_index ] ) ;
DBG ( DBG_info , " updateGain2(): preview had gain %d => %f \n " , scanner - > settings . gain [ color_index ] , g ) ;
/* Look up new gain setting g*sqrt(dg) */
DBG ( DBG_info , " updateGain2(): optimized gain * %f = %f \n " , sqrt ( gain_increase ) , sqrt ( gain_increase ) * g ) ;
scanner - > settings . gain [ color_index ] = getGainSetting ( g * sqrt ( gain_increase ) ) ;
DBG ( DBG_info , " updateGain2(): optimized gain setting %d => %f \n " , scanner - > settings . gain [ color_index ] , getGain ( scanner - > settings . gain [ color_index ] ) ) ;
/* Exposure change is straightforward */
DBG ( DBG_info , " updateGain2(): remains for exposure %f \n " , gain_increase / ( getGain ( scanner - > settings . gain [ color_index ] ) / g ) ) ;
scanner - > settings . exposureTime [ color_index ] = lround ( g / getGain ( scanner - > settings . gain [ color_index ] ) * gain_increase * scanner - > settings . exposureTime [ color_index ] ) ;
DBG ( DBG_info , " updateGain2(): new setting G=%d Exp=%d \n " , scanner - > settings . gain [ color_index ] , scanner - > settings . exposureTime [ color_index ] ) ;
}