sane-project-backends/backend/canon.c

1705 wiersze
53 KiB
C
Czysty Zwykły widok Historia

1999-08-09 18:06:01 +00:00
/* sane - Scanner Access Now Easy.
Copyright (C) 1997 BYTEC GmbH Germany
Written by Helmut Koeberle, Email: helmut.koeberle@bytec.de
Modified by Manuel Panea <Manuel.Panea@rzg.mpg.de>
and Markus Mertinat <Markus.Mertinat@Physik.Uni-Augsburg.DE>
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. */
/* This file implements the sane-api */
/* SANE-FLOW-DIAGRAMM
- sane_init() : initialize backend, attach scanners(devicename,0)
. - sane_get_devices() : query list of scanner-devices
. - sane_open() : open a particular scanner-device and attach_scanner(devicename,&dev)
. . - sane_set_io_mode : set blocking-mode
. . - sane_get_select_fd : get scanner-fd
. . - sane_get_option_descriptor() : get option informations
. . - sane_control_option() : change option values
. .
. . - sane_start() : start image aquisition
. . - sane_get_parameters() : returns actual scan-parameters
. . - sane_read() : read image-data (from pipe)
. . - sane_cancel() : cancel operation, kill reader_process
. - sane_close() : close opened scanner-device, do_cancel, free buffer and handle
- sane_exit() : terminate use of backend, free devicename and device-struture
*/
/* This driver's flow:
- sane_init
. - attach_one
. . - inquiry
. . - test_unit_ready
. . - medium_position
. . - extended inquiry
. . - mode sense
. . - get_density_curve
- sane_get_devices
- sane_open
. - init_options
- sane_set_io_mode : set blocking-mode
- sane_get_select_fd : get scanner-fd
- sane_get_option_descriptor() : get option informations
- sane_control_option() : change option values
- sane_start() : start image aquisition
- sane_get_parameters() : returns actual scan-parameters
- sane_read() : read image-data (from pipe)
- sane_cancel() : cancel operation, kill reader_process
- sane_close() : close opened scanner-device, do_cancel, free buffer and handle
- sane_exit() : terminate use of backend, free devicename and device-struture
*/
#include <sane/config.h>
#include <limits.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>
#include <sane/sane.h>
#include <sane/saneopts.h>
#include <sane/sanei_scsi.h>
#include <canon.h>
#define BACKEND_NAME canon
#include <sane/sanei_backend.h>
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
#include <sane/sanei_config.h>
#define CANON_CONFIG_FILE "canon.conf"
#define MM_PER_INCH 25.4
static int num_devices = 0;
static CANON_Device *first_dev = NULL;
static CANON_Scanner *first_handle = NULL;
static const SANE_String_Const mode_list[] =
{
"Lineart", "Halftone", "Gray", "Color",
0
};
static const SANE_String_Const tpu_dc_mode_list[] =
{
"No transparency correction",
"Correction according to Film type",
"Correction according to Transparency Ratio",
0
};
static const SANE_String_Const page_list[] =
{
"Show normal options",
"Show advanced options",
"Show all options",
0
};
static const SANE_String_Const filmtype_list[] =
{
"Negatives",
"Slides",
0
};
static const SANE_String_Const tpu_filmtype_list[] =
{
"Film 0", "Film 1", "Film 2", "Film 3",
0
};
static const SANE_String_Const papersize_list[] =
{
"A4", "Letter", "B5", "Maximal",
0
};
static const SANE_Range u8_range =
{
0, /* minimum */
255, /* maximum */
0 /* quantization */
};
#include "canon-scsi.c"
/**************************************************************************/
static size_t
max_string_size (const SANE_String_Const strings[])
{
size_t size, max_size = 0;
int i;
DBG (11, ">> max_string_size\n");
for (i = 0; strings[i]; ++i)
{
size = strlen (strings[i]) + 1;
if (size > max_size)
max_size = size;
}
DBG (11, "<< max_string_size\n");
return max_size;
}
/**************************************************************************/
static void
get_tpu_stat(int fd, CANON_Device *dev)
{
unsigned char tbuf[12+5];
size_t buf_size;
SANE_Status status;
int i;
DBG (3, ">> get tpu stat\n");
/* Check wether it is a CS-600 at all */
if (strncmp(dev->sane.model, "IX-06015", 8))
{
dev->tpu.Status = TPU_STAT_NONE;
return;
}
memset (tbuf, 0, sizeof (tbuf));
buf_size = sizeof (tbuf);
status = get_scan_mode (fd, TRANSPARENCY_UNIT, tbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "get scan mode failed: %s\n", sane_strstatus (status));
return;
}
for (i=0; i<buf_size; i++)
{
DBG(3, "scan mode control byte[%d] = %d\n", i, tbuf[i]);
}
dev->tpu.Status = ( tbuf[2 + 4 + 5] >> 7 ) ?
TPU_STAT_INACTIVE : TPU_STAT_NONE;
if ( dev->tpu.Status == SANE_TRUE ) /* TPU available */
{
dev->tpu.Status = ( tbuf[2 + 4 + 5] && 0x04 ) ?
TPU_STAT_INACTIVE : TPU_STAT_ACTIVE;
}
dev->tpu.ControlMode = tbuf[3 + 4 + 5] && 0x03;
dev->tpu.Transparency = tbuf[4 + 4 + 5] * 256 + tbuf[5 + 4 + 5];
dev->tpu.PosNeg = tbuf[6 + 4 + 5] && 0x01;
dev->tpu.FilmType = tbuf[7 + 4 + 5];
DBG(11,"TPU Status: %d\n", dev->tpu.Status);
DBG(11,"TPU ControlMode: %d\n", dev->tpu.ControlMode);
DBG(11,"TPU Transparency: %d\n", dev->tpu.Transparency);
DBG(11,"TPU PosNeg: %d\n", dev->tpu.PosNeg);
DBG(11,"TPU FilmType: %d\n", dev->tpu.FilmType);
DBG (3, "<< get tpu stat\n");
return;
}
/**************************************************************************/
static SANE_Status
sense_handler (int scsi_fd, u_char * result, void *arg)
{
static char me[] = "canon_sense_handler";
CANON_Scanner *s = (CANON_Scanner *) arg;
u_char sense;
char *sense_str = NULL;
SANE_Status status = SANE_STATUS_GOOD;
DBG (1, ">> sense_handler\n");
DBG (11, "%s(%ld, %p, %p)\n", me, (long) scsi_fd,
(void *) result, (void *) arg);
sense = result[0];
switch (sense)
{
case 0x00: /* Good */
sense_str = "Good. Command has executed normally";
break;
case 0x02: /* Check Condition */
sense_str = "Check Condition";
break;
case 0x08: /* Busy */
sense_str = "Scanner is busy";
break;
case 0x18: /* Reservation Conflict */
sense_str = "The scanner is reserved by another SCSI device";
break;
default:
DBG (5, "%s: no handling for sense %x.\n", me, sense);
break;
}
if (s)
{
s->sense_str = sense_str;
}
DBG (1, "<< sense_handler\n");
return status;
}
/**************************************************************************/
static SANE_Status
do_gamma(CANON_Scanner *s)
{
SANE_Status status;
u_char gbuf[256];
size_t buf_size;
int i, j, neg;
DBG (7, "sending SET_DENSITY_CURVE\n");
buf_size = 256 * sizeof (u_char);
neg = (s->hw->info.model == CS2700) ?
s->val[OPT_NEGATIVE].w :
s->val[OPT_HNEGATIVE].w;
if (!strcmp(s->val[OPT_MODE].s, "Gray"))
{
/* If scanning in gray mode, use the first curve for the
scanner's monochrome gamma component */
for (j = 0; j < 256; j++)
{
if(neg == SANE_FALSE)
{
gbuf[j] = (u_char)(255.0/256.0 * s->gamma_table[0][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, j, gbuf[j]);
}
else
{
gbuf[255-j] = (u_char)(255-255.0/256.0*s->gamma_table[0][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", 0, 255-j, gbuf[255-j]);
}
}
status = set_density_curve (s->fd, 0, gbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (7, "SET_DENSITY_CURVE\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
}
else if (s->val[OPT_CUSTOM_GAMMA_BIND].w == SANE_TRUE)
{
/* If in RGB mode but with gamma bind, use the first curve
for all 3 colors red, green, blue */
for (i = 1; i < 4; i++)
{
for (j = 0; j < 256; j++)
{
if(neg == SANE_FALSE)
{
gbuf[j] = (u_char)(255.0/256.0 * s->gamma_table[0][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, j, gbuf[j]);
}
else
{
gbuf[255-j] = (u_char)(255 - 255.0/256.0 * s->gamma_table[0][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, 255-j, gbuf[255-j]);
}
}
status = set_density_curve (s->fd, i, gbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (7, "SET_DENSITY_CURVE\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
}
}
else
{
/* If in RGB mode without gamma bind, use the 3 curves for RGB */
for (i = 1; i < 4; i++)
{
for (j = 0; j < 256; j++)
{
if(neg == SANE_FALSE)
{
gbuf[j] = (u_char)(255.0/256.0 * s->gamma_table[i][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, j, gbuf[j]);
}
else
{
gbuf[255-j] = (u_char)(255 - 255.0/256.0 * s->gamma_table[i][j]);
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, 255-j, gbuf[255-j]);
}
}
status = set_density_curve (s->fd, i, gbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (7, "SET_DENSITY_CURVE\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
}
}
return(SANE_STATUS_GOOD);
}
/**************************************************************************/
static SANE_Status
attach (const char *devnam, CANON_Device ** devp)
{
SANE_Status status;
CANON_Device *dev;
int fd;
u_char ibuf[36], ebuf[74], mbuf[12], sbuf[14];
size_t buf_size;
char *str;
DBG (1, ">> attach\n");
for (dev = first_dev; dev; dev = dev->next)
{
if (strcmp (dev->sane.name, devnam) == 0)
{
if (devp)
*devp = dev;
return (SANE_STATUS_GOOD);
}
}
DBG (3, "attach: opening %s\n", devnam);
status = sanei_scsi_open (devnam, &fd, sense_handler, 0);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: open failed: %s\n", sane_strstatus (status));
return (status);
}
DBG (3, "attach: sending (standard) INQUIRY\n");
memset (ibuf, 0, sizeof (ibuf));
buf_size = sizeof (ibuf);
status = inquiry (fd, 0, ibuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: inquiry failed: %s\n", sane_strstatus (status));
sanei_scsi_close (fd);
fd = -1;
return (status);
}
if (ibuf[0] != 6
|| strncmp (ibuf + 8, "CANON", 5) != 0
|| strncmp (ibuf + 16, "IX-", 3) != 0)
{
DBG (1, "attach: device doesn't look like a Canon scanner\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
DBG (3, "attach: sending TEST_UNIT_READY\n");
status = test_unit_ready (fd);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: test unit ready failed (%s)\n",
sane_strstatus (status));
sanei_scsi_close (fd);
fd = -1;
return (status);
}
DBG (3, "attach: sending REQUEST SENSE\n");
memset (sbuf, 0, sizeof (sbuf));
buf_size = sizeof (sbuf);
status = request_sense (fd, sbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: REQUEST_SENSE failed\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
DBG (3, "attach: sending MEDIUM POSITION\n");
status = medium_position (fd);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: MEDIUM POSTITION failed\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
/* s->val[OPT_AF_NOW].w == SANE_TRUE; */
DBG (3, "attach: sending RESERVE UNIT\n");
status = reserve_unit(fd);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: RESERVE UNIT failed\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
/* DBG (3, "attach: sending GET SCAN MODE for transparency unit\n"); */
/* memset (ebuf, 0, sizeof (ebuf)); */
/* buf_size = sizeof (ebuf); */
/* buf_size = 12; */
/* status = get_scan_mode (fd, TRANSPARENCY_UNIT, ebuf, &buf_size); */
/* if (status != SANE_STATUS_GOOD) */
/* { */
/* DBG (1, "attach: GET SCAN MODE for transparency unit failed\n"); */
/* sanei_scsi_close (fd); */
/* return (SANE_STATUS_INVAL); */
/* } */
/* for (i=0; i<buf_size; i++) */
/* { */
/* DBG(3, "scan mode trans byte[%d] = %d\n", i, ebuf[i]); */
/* } */
/* DBG (3, "attach: sending GET SCAN MODE for scan control conditions\n"); */
/* memset (ebuf, 0, sizeof (ebuf)); */
/* buf_size = sizeof (ebuf); */
/* buf_size = 20; */
/* status = get_scan_mode (fd, SCAN_CONTROL_CONDITIONS, ebuf, &buf_size); */
/* if (status != SANE_STATUS_GOOD) */
/* { */
/* DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); */
/* sanei_scsi_close (fd); */
/* return (SANE_STATUS_INVAL); */
/* } */
/* for (i=0; i<buf_size; i++) */
/* { */
/* DBG(3, "scan mode byte[%d] = %d\n", i, ebuf[i]); */
/* } */
DBG (3, "attach: sending (extended) INQUIRY\n");
memset (ebuf, 0, sizeof (ebuf));
buf_size = sizeof (ebuf);
status = inquiry (fd, 1, ebuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: (extended) INQUIRY failed\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
/* DBG (3, "attach: sending GET SCAN MODE for all scan mode pages\n"); */
/* memset (ebuf, 0, sizeof (ebuf)); */
/* buf_size = 32; */
/* status = get_scan_mode (fd, (u_char)ALL_SCAN_MODE_PAGES, */
/* ebuf, &buf_size); */
/* if (status != SANE_STATUS_GOOD) */
/* { */
/* DBG (1, "attach: GET SCAN MODE for scan control conditions failed\n"); */
/* sanei_scsi_close (fd); */
/* return (SANE_STATUS_INVAL); */
/* } */
/* for (i=0; i<buf_size; i++) */
/* { */
/* DBG(3, "scan mode control byte[%d] = %d\n", i, ebuf[i]); */
/* } */
DBG (3, "attach: sending MODE SENSE\n");
memset (mbuf, 0, sizeof (mbuf));
buf_size = sizeof (mbuf);
status = mode_sense (fd, mbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: MODE_SENSE failed\n");
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_INVAL);
}
dev = malloc (sizeof (*dev));
if (!dev)
{
sanei_scsi_close (fd);
fd = -1;
return (SANE_STATUS_NO_MEM);
}
memset (dev, 0, sizeof (*dev));
dev->sane.name = strdup (devnam);
dev->sane.vendor = "CANON";
str = malloc (16 + 1);
memset (str, 0, sizeof (str));
strncpy (str, ibuf + 16, 16);
dev->sane.model = str;
if (!strncmp(str, "IX-27015", 5))
{
dev->info.model = CS2700;
dev->sane.type = "film scanner";
}
else
{
dev->info.model = CS3_600;
dev->sane.type = "flatbed scanner";
}
DBG (5, "dev->sane.name = '%s'\n", dev->sane.name);
DBG (5, "dev->sane.vendor = '%s'\n", dev->sane.vendor);
DBG (5, "dev->sane.model = '%s'\n", dev->sane.model);
DBG (5, "dev->sane.type = '%s'\n", dev->sane.type);
get_tpu_stat(fd, dev); /* Query TPU */
dev->info.bmu = mbuf[6];
DBG (5, "bmu=%d\n", dev->info.bmu);
dev->info.mud = (mbuf[8] * 256) + mbuf[9];
DBG (5, "mud=%d\n", dev->info.mud);
dev->info.xres_default = (ebuf[5] * 256) + ebuf[6];
DBG (5, "xres_default=%d\n", dev->info.xres_default);
dev->info.xres_range.max = (ebuf[10] * 256) + ebuf[11];
DBG (5, "xres_range.max=%d\n", dev->info.xres_range.max);
dev->info.xres_range.min = (ebuf[14] * 256) + ebuf[15];
DBG (5, "xres_range.min=%d\n", dev->info.xres_range.min);
dev->info.xres_range.quant = ebuf[9] >> 4;
DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
dev->info.yres_default = (ebuf[7] * 256) + ebuf[8];
DBG (5, "yres_default=%d\n", dev->info.yres_default);
dev->info.yres_range.max = (ebuf[12] * 256) + ebuf[13];
DBG (5, "yres_range.max=%d\n", dev->info.yres_range.max);
dev->info.yres_range.min = (ebuf[16] * 256) + ebuf[17];
DBG (5, "yres_range.min=%d\n", dev->info.yres_range.min);
dev->info.yres_range.quant = ebuf[9] & 0x0f;
DBG (5, "xres_range.quant=%d\n", dev->info.xres_range.quant);
dev->info.x_range.min = SANE_FIX(0.0);
dev->info.x_range.max = (ebuf[20] * 256 * 256 * 256)
+ (ebuf[21] * 256 * 256) + (ebuf[22] * 256) + ebuf[23] - 1;
dev->info.x_range.max = SANE_FIX(dev->info.x_range.max * MM_PER_INCH / dev->info.mud);
DBG (5, "x_range.max=%d\n", dev->info.x_range.max);
dev->info.x_range.quant = 0;
dev->info.y_range.min = SANE_FIX(0.0);
dev->info.y_range.max = (ebuf[24] * 256 * 256 * 256)
+ (ebuf[25] * 256 * 256) + (ebuf[26] * 256) + ebuf[27] - 1;
dev->info.y_range.max = SANE_FIX(dev->info.y_range.max * MM_PER_INCH / dev->info.mud);
DBG (5, "y_range.max=%d\n", dev->info.y_range.max);
dev->info.y_range.quant = 0;
dev->info.x_adf_range.max = (ebuf[30] * 256 * 256 * 256)
+ (ebuf[31] * 256 * 256) + (ebuf[32] * 256) + ebuf[33] - 1;
DBG (5, "x_adf_range.max=%d\n", dev->info.x_adf_range.max);
dev->info.y_adf_range.max = (ebuf[34] * 256 * 256 * 256)
+ (ebuf[35] * 256 * 256) + (ebuf[36] * 256) + ebuf[37] - 1;
DBG (5, "y_adf_range.max=%d\n", dev->info.y_adf_range.max);
dev->info.brightness_range.min = 1;
dev->info.brightness_range.max = 255;
dev->info.brightness_range.quant = 0;
dev->info.contrast_range.min = 1;
dev->info.contrast_range.max = 255;
dev->info.contrast_range.quant = 0;
dev->info.threshold_range.min = 1;
dev->info.threshold_range.max = 255;
dev->info.threshold_range.quant = 0;
dev->info.HiliteR_range.min = 0;
dev->info.HiliteR_range.max = 255;
dev->info.HiliteR_range.quant = 0;
dev->info.ShadowR_range.min = 0;
dev->info.ShadowR_range.max = 254;
dev->info.ShadowR_range.quant = 0;
dev->info.HiliteG_range.min = 1;
dev->info.HiliteG_range.max = 255;
dev->info.HiliteG_range.quant = 0;
dev->info.ShadowG_range.min = 0;
dev->info.ShadowG_range.max = 254;
dev->info.ShadowG_range.quant = 0;
dev->info.HiliteB_range.min = 0;
dev->info.HiliteB_range.max = 255;
dev->info.HiliteB_range.quant = 0;
dev->info.ShadowB_range.min = 0;
dev->info.ShadowB_range.max = 254;
dev->info.ShadowB_range.quant = 0;
dev->info.focus_range.min = 0;
dev->info.focus_range.max = 255;
dev->info.focus_range.quant = 0;
dev->info.TPU_Transparency_range.min = 0;
dev->info.TPU_Transparency_range.max = 10000;
dev->info.TPU_Transparency_range.quant = 100;
sanei_scsi_close (fd);
fd = -1;
++num_devices;
dev->next = first_dev;
first_dev = dev;
if (devp)
*devp = dev;
DBG (1, "<< attach\n");
return (SANE_STATUS_GOOD);
}
/**************************************************************************/
static SANE_Status
do_cancel (CANON_Scanner * s)
{
SANE_Status status;
DBG (1, ">> do_cancel\n");
s->scanning = SANE_FALSE;
if (s->fd >= 0)
{
if (s->val[OPT_EJECT_AFTERSCAN].w == SANE_TRUE)
{
DBG (3, "attach: sending MEDIUM POSITION\n");
status = medium_position (s->fd);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "attach: MEDIUM POSTITION failed\n");
return (SANE_STATUS_INVAL);
}
s->AF_NOW = SANE_TRUE;
DBG(1, "do_cancel AF_NOW = '%d'\n", s->AF_NOW);
}
sanei_scsi_close (s->fd);
s->fd = -1;
}
DBG (1, "<< do_cancel\n");
return (SANE_STATUS_CANCELLED);
}
/**************************************************************************/
static SANE_Status
adjust_hilo_points(CANON_Scanner *s)
{
int mode;
char *mode_str;
SANE_Status status;
u_char wbuf[72], dbuf[28], gbuf[256];
size_t buf_size;
int i, j;
size_t nread, bread;
SANE_Byte *adjbuf;
double histo[3][256], sum[3], partsum[3], newsum;
double percentage, next_percentage;
float gamma[3];
int maxhi, minlo;
SANE_Word save_xres, new_xres;
DBG (1, ">> adjust_hilo_points\n");
/* First make sure we have a current parameter set. Some of the
parameters will be overwritten below, but that's OK. */
status = sane_get_parameters (s, 0);
if (status != SANE_STATUS_GOOD)
return status;
status = sanei_scsi_open (s->hw->sane.name, &s->fd, sense_handler, 0);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "open of %s failed: %s\n",
s->hw->sane.name, sane_strstatus (status));
return (status);
}
if (s->val[OPT_CUSTOM_GAMMA].w == 1)
{
buf_size = 256 * sizeof (u_char);
for (i=0; i<4; i++)
{
for (j=0; j<256; j++)
{
/* Use a straight intensity curve for all colors */
gbuf[j] = (u_char)j;
DBG (22, "set_density %d: gbuf[%d] = [%d]\n", i, j, gbuf[j]);
}
status = set_density_curve (s->fd, i, gbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (7, "SET_DENSITY_CURVE failed\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
}
}
mode_str = s->val[OPT_MODE].s;
save_xres = (SANE_Word)s->val[OPT_X_RESOLUTION].w;
s->scanning = SANE_FALSE;
new_xres = 170;
sane_control_option (s, OPT_X_RESOLUTION, SANE_ACTION_SET_VALUE,
&new_xres, 0);
status = sane_get_parameters (s, 0);
if (status != SANE_STATUS_GOOD)
return status;
s->xres = s->val[OPT_X_RESOLUTION].w;
s->yres = s->val[OPT_X_RESOLUTION].w;
s->ulx = SANE_UNFIX(s->val[OPT_TL_X].w) * s->hw->info.mud / MM_PER_INCH;
s->uly = SANE_UNFIX(s->val[OPT_TL_Y].w) * s->hw->info.mud / MM_PER_INCH;
s->width = SANE_UNFIX(s->val[OPT_BR_X].w - s->val[OPT_TL_X].w)
* s->hw->info.mud / MM_PER_INCH;
s->length = SANE_UNFIX(s->val[OPT_BR_Y].w - s->val[OPT_TL_Y].w)
* s->hw->info.mud / MM_PER_INCH;
DBG(11, "ahl: s->width='%d', s->length='%d'\n", s->width, s->length);
s->RIF = (s->hw->info.model == CS2700) ?
s->RIF :
s->val[OPT_HNEGATIVE].w;
s->brightness = (s->RIF == 1) ?
s->val[OPT_BRIGHTNESS].w : (255 - s->val[OPT_BRIGHTNESS].w);
s->contrast = s->val[OPT_CONTRAST].w;
s->threshold = s->val[OPT_THRESHOLD].w;
s->bpp = s->params.depth;
s->GRC = s->val[OPT_CUSTOM_GAMMA].w;
s->Mirror = s->val[OPT_MIRROR].w;
s->AE = 0;
s->HiliteR = 255;
s->ShadowR = 0;
s->HiliteG = 255;
s->ShadowG = 0;
s->HiliteB = 255;
s->ShadowB = 0;
if (strcmp (mode_str, "Lineart") == 0)
{
mode = 4;
s->image_composition = 0;
}
else if (strcmp (mode_str, "Halftone") == 0)
{
mode = 4;
s->image_composition = 1;
}
else if (strcmp (mode_str, "Gray") == 0)
{
mode = 5;
s->image_composition = 2;
}
else if (strcmp (mode_str, "Color") == 0)
{
mode = 6;
s->image_composition = 5;
}
DBG(11, "ahl: s->xres='%d', s->yres='%d'\n", s->xres, s->yres);
memset (wbuf, 0, sizeof (wbuf));
wbuf[7] = 64;
wbuf[10] = s->xres >> 8;
wbuf[11] = s->xres;
wbuf[12] = s->yres >> 8;
wbuf[13] = s->yres;
wbuf[14] = s->ulx >> 24;
wbuf[15] = s->ulx >> 16;
wbuf[16] = s->ulx >> 8;
wbuf[17] = s->ulx;
wbuf[18] = s->uly >> 24;
wbuf[19] = s->uly >> 16;
wbuf[20] = s->uly >> 8;
wbuf[21] = s->uly;
wbuf[22] = s->width >> 24;
wbuf[23] = s->width >> 16;
wbuf[24] = s->width >> 8;
wbuf[25] = s->width;
wbuf[26] = s->length >> 24;
wbuf[27] = s->length >> 16;
wbuf[28] = s->length >> 8;
wbuf[29] = s->length;
wbuf[30] = s->brightness;
wbuf[31] = s->threshold;
wbuf[32] = s->contrast;
wbuf[33] = s->image_composition;
wbuf[34] = s->bpp;
wbuf[36] = 1;
wbuf[37] = (s->RIF << 7) + 3;
/* wbuf[50] = (s->GRC << 3) | (s->Mirror << 2) | (s->AE); */
wbuf[50] = (s->GRC << 3) | (s->Mirror << 2);
wbuf[54] = 2;
wbuf[57] = 1;
wbuf[58] = 1;
wbuf[59] = s->HiliteR;
wbuf[60] = s->ShadowR;
wbuf[62] = s->HiliteG;
wbuf[64] = s->ShadowG;
wbuf[70] = s->HiliteB;
wbuf[71] = s->ShadowB;
DBG(7, "ahl: RIF=%d, GRC=%d, Mirror=%d, AE=%d\n",
s->RIF, s->GRC, s->Mirror, s->AE);
DBG(7, "ahl: HR=%d, SR=%d, HG=%d, SG=%d, HB=%d, SB=%d\n",
s->HiliteR, s->ShadowR,
s->HiliteG, s->ShadowG,
s->HiliteB, s->ShadowB);
buf_size = sizeof (wbuf);
status = set_window (s->fd, wbuf);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "SET WINDOW failed: %s\n", sane_strstatus (status));
return (status);
}
DBG(7, "adjust_hilo: set_window done\n");
buf_size = sizeof (wbuf);
memset (wbuf, 0, buf_size);
status = get_window (s->fd, wbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "GET WINDOW failed: %s\n", sane_strstatus (status));
return (status);
}
DBG (5, "ahl: xres=%d\n", (wbuf[10] * 256) + wbuf[11]);
DBG (5, "ahl: yres=%d\n", (wbuf[12] * 256) + wbuf[13]);
DBG (5, "ahl: ulx=%d\n", (wbuf[14] * 256 * 256 * 256)
+ (wbuf[15] * 256 * 256) + (wbuf[16] * 256) + wbuf[17]);
DBG (5, "ahl: uly=%d\n", (wbuf[18] * 256 * 256 * 256)
+ (wbuf[19] * 256 * 256) + (wbuf[20] * 256) + wbuf[21]);
DBG (5, "ahl: width=%d\n", (wbuf[22] * 256 * 256 * 256)
+ (wbuf[23] * 256 * 256) + (wbuf[24] * 256) + wbuf[25]);
DBG (5, "ahl: length=%d\n", (wbuf[26] * 256 * 256 * 256)
+ (wbuf[27] * 256 * 256) + (wbuf[28] * 256) + wbuf[29]);
status = scan (s->fd);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "start of scan failed: %s\n", sane_strstatus (status));
return (status);
}
buf_size = sizeof (dbuf);
memset (dbuf, 0, buf_size);
status = get_data_status (s->fd, dbuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "GET DATA STATUS failed: %s\n", sane_strstatus (status));
return (status);
}
DBG (5, "ahl: Magnified Width=%d\n", (dbuf[12] * 256 * 256 * 256)
+ (dbuf[13] * 256 * 256) + (dbuf[14] * 256) + dbuf[15]);
DBG (5, "ahl: Magnified Length=%d\n", (dbuf[16] * 256 * 256 * 256)
+ (dbuf[17] * 256 * 256) + (dbuf[18] * 256) + dbuf[19]);
DBG (5, "ahl: Data=%d bytes\n", (dbuf[20] * 256 * 256 * 256)
+ (dbuf[21] * 256 * 256) + (dbuf[22] * 256) + dbuf[23]);
if (s->xres > 0 && s->yres > 0 && s->width > 0 && s->length > 0)
{
s->params.pixels_per_line = s->width * s->xres / s->hw->info.mud;
s->params.lines = s->length * s->yres / s->hw->info.mud;
}
s->bytes_to_read = s->params.bytes_per_line * s->params.lines;
DBG (1, "ahl: %d pixels per line, %d bytes, %d lines high, total %lu bytes, "
"dpi=%d\n", s->params.pixels_per_line, s->params.bytes_per_line,
s->params.lines, (u_long) s->bytes_to_read, s->val[OPT_X_RESOLUTION].w);
if(s->bytes_to_read == 0)
{
return(SANE_STATUS_GOOD);
}
if( (adjbuf = (SANE_Byte *) malloc(s->bytes_to_read)) == NULL )
{
DBG (1, "adjust_hilo: malloc failed\n");
do_cancel (s);
return (SANE_STATUS_IO_ERROR);
}
bread = 0;
nread = 16384;
while(s->bytes_to_read > 0)
{
if (nread > s->bytes_to_read)
nread = s->bytes_to_read;
status = read_data (s->fd, &adjbuf[bread], &nread);
bread += nread;
s->bytes_to_read -= nread;
if (status != SANE_STATUS_GOOD)
{
DBG (1, "adjust_hilo: read_data failed: %s\n", sane_strstatus (status));
do_cancel (s);
return (SANE_STATUS_IO_ERROR);
}
}
DBG(7, "adjust_hilo: bread='%lu'\n", (unsigned long) bread);
sane_control_option (s, OPT_X_RESOLUTION, SANE_ACTION_SET_VALUE, &save_xres, 0);
status = sane_get_parameters (s, 0);
if (status != SANE_STATUS_GOOD)
return status;
DBG(7, "adjust_hilo: building histograms\n");
/* Build the histograms */
DBG(7, "sizeof(histo)='%lu'\n", (unsigned long) sizeof(histo));
memset (histo, 0, sizeof(histo));
for(i=0; i<bread; i+=3)
{
++histo[RED][(int)adjbuf[i]];
++histo[GREEN][(int)adjbuf[i+1]];
++histo[BLUE][(int)adjbuf[i+2]];
}
for(i=0; i<256; i++)
{
DBG(7, "adjust_hilo: histo[%d]=(%03.3f, %03.3f, %03.3f)\n",
i,
histo[RED][i],
histo[GREEN][i],
histo[BLUE][i]);
}
/* For each color */
minlo=255;
maxhi=0;
for(i=RED; i<=BLUE; i++)
{
/* Count the "total weight" of the histogram */
for(j=0; j<256; j++)
{
sum[i] += histo[i][j];
}
/* Find the "middle weight point" in the histogram */
j=0;
while( (partsum[i] <= sum[i]/2) && (j<256) )
{
partsum[i] += histo[i][j];
j++;
}
/* We set the gamma value to the middle weight point */
/* s->gamma_table[][] = */
gamma[i] = (float)--j;
DBG(1, "adjust_hilo: gamma[%d] = '%f'\n", i, gamma[i]);
/* Find the shadow point */
/* j = 0; */
/* while( (histo[i][j] == 0) && (j<256) ) */
/* j++; */
/* if (j < minlo) */
/* minlo = j; */
newsum = 0.0;
for (j = 0; j < 255; j++)
{
newsum += histo[i][j];
percentage = newsum / sum[i];
next_percentage = (newsum + histo[i][j+1]) / sum[i];
if ( fabs(percentage - 0.006) < fabs(next_percentage - 0.006) )
{
minlo = j+1;
break;
}
}
s->val[OPT_SHADOW_R+i*2].w = *((SANE_Word *)&minlo);
DBG(1, "adjust_hilo: lo[%d]='%d'\n", i, minlo);
DBG(1, "adjust_hilo: s->val[OPT_SHADOW_R+%d*2]='%d'\n",
i, s->val[OPT_SHADOW_R+i*2].w);
/* Find the hilight point */
/* j = 255; */
/* while( (histo[i][j] == 0) && (j>0) ) */
/* j--; */
/* if (j > maxhi) */
/* maxhi = j; */
newsum = 0.0;
for (j = 255; j > 0; j--)
{
newsum += histo[i][j];
percentage = newsum / sum[i];
next_percentage = (newsum + histo[i][j-1]) / sum[i];
if ( fabs(percentage - 0.006) < fabs(next_percentage - 0.006) )
{
maxhi = j-1;
break;
}
}
s->val[OPT_HILITE_R+i*2].w = *((SANE_Word *)&maxhi);
DBG(1, "adjust_hilo: hi[%d]='%d'\n", i, maxhi);
DBG(1, "adjust_hilo: s->val[OPT_HILITE_R+%d*2]='%d'\n",
i, s->val[OPT_HILITE_R+i*2].w);
}
DBG (1, "<< adjust_hilo_points\n");
free(adjbuf);
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_GOOD);
}
/**************************************************************************/
static SANE_Status
init_options (CANON_Scanner * s)
{
int i;
DBG (1, ">> init_options\n");
memset (s->opt, 0, sizeof (s->opt));
memset (s->val, 0, sizeof (s->val));
s->AF_NOW = SANE_TRUE;
for (i = 0; i < NUM_OPTIONS; ++i)
{
s->opt[i].size = sizeof (SANE_Word);
s->opt[i].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
}
s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
s->opt[OPT_PAGE].name = "options page";
s->opt[OPT_PAGE].title = "";
s->opt[OPT_PAGE].desc = "Selects the options page to show";
s->opt[OPT_PAGE].type = SANE_TYPE_STRING;
s->opt[OPT_PAGE].size = max_string_size (page_list);
s->opt[OPT_PAGE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_PAGE].constraint.string_list = page_list;
s->val[OPT_PAGE].s = strdup (page_list[0]);
/* "Mode" group: */
s->opt[OPT_MODE_GROUP].title = "Scan Mode";
s->opt[OPT_MODE_GROUP].desc = "";
s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_MODE_GROUP].cap = 0;
s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* scan mode */
s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
s->opt[OPT_MODE].type = SANE_TYPE_STRING;
s->opt[OPT_MODE].size = max_string_size (mode_list);
s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_MODE].constraint.string_list = mode_list;
s->val[OPT_MODE].s = strdup (mode_list[3]);
/* Slides or negatives */
s->opt[OPT_NEGATIVE].name = SANE_NAME_NEGATIVE;
s->opt[OPT_NEGATIVE].title = "Film type";
s->opt[OPT_NEGATIVE].desc = "Selects the film type, i.e. negatives or slides";
s->opt[OPT_NEGATIVE].type = SANE_TYPE_STRING;
s->opt[OPT_NEGATIVE].size = max_string_size (filmtype_list);
s->opt[OPT_NEGATIVE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_NEGATIVE].constraint.string_list = filmtype_list;
s->opt[OPT_NEGATIVE].cap |=
(s->hw->info.model == CS2700) ? 0: SANE_CAP_INACTIVE;
s->val[OPT_NEGATIVE].s = strdup(filmtype_list[0]);
/* bind resolution */
s->opt[OPT_RESOLUTION_BIND].name = SANE_NAME_RESOLUTION_BIND;
s->opt[OPT_RESOLUTION_BIND].title = SANE_TITLE_RESOLUTION_BIND;
s->opt[OPT_RESOLUTION_BIND].desc = SANE_DESC_RESOLUTION_BIND;
s->opt[OPT_RESOLUTION_BIND].type = SANE_TYPE_BOOL;
s->val[OPT_RESOLUTION_BIND].w = SANE_TRUE;
/* x-resolution */
s->opt[OPT_X_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
s->opt[OPT_X_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
s->opt[OPT_X_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
s->opt[OPT_X_RESOLUTION].type = SANE_TYPE_INT;
s->opt[OPT_X_RESOLUTION].unit = SANE_UNIT_DPI;
s->opt[OPT_X_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_X_RESOLUTION].constraint.range = &s->hw->info.xres_range;
s->val[OPT_X_RESOLUTION].w = (s->hw->info.model == CS2700) ? 340 : 300;
/* y-resolution */
s->opt[OPT_Y_RESOLUTION].name = SANE_NAME_SCAN_Y_RESOLUTION;
s->opt[OPT_Y_RESOLUTION].title = SANE_TITLE_SCAN_Y_RESOLUTION;
s->opt[OPT_Y_RESOLUTION].desc = SANE_DESC_SCAN_Y_RESOLUTION;
s->opt[OPT_Y_RESOLUTION].type = SANE_TYPE_INT;
s->opt[OPT_Y_RESOLUTION].unit = SANE_UNIT_DPI;
s->opt[OPT_Y_RESOLUTION].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_Y_RESOLUTION].constraint.range = &s->hw->info.yres_range;
s->val[OPT_Y_RESOLUTION].w = (s->hw->info.model == CS2700) ? 340 : 300;
s->opt[OPT_Y_RESOLUTION].cap |= SANE_CAP_INACTIVE;
/* Focus group: */
s->opt[OPT_FOCUS_GROUP].title = "Focus";
s->opt[OPT_FOCUS_GROUP].desc = "";
s->opt[OPT_FOCUS_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_FOCUS_GROUP].cap = SANE_CAP_ADVANCED;
s->opt[OPT_FOCUS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* Auto-Focus switch */
s->opt[OPT_AF].name = "af";
s->opt[OPT_AF].title = "Auto Focus";
s->opt[OPT_AF].desc = "Enable/disable Auto Focus";
s->opt[OPT_AF].type = SANE_TYPE_BOOL;
s->val[OPT_AF].w = SANE_TRUE;
/* Auto-Focus once switch */
s->opt[OPT_AF_ONCE].name = "afonce";
s->opt[OPT_AF_ONCE].title = "Auto Focus only once";
s->opt[OPT_AF_ONCE].desc = "Do Auto Focus only once between ejects";
s->opt[OPT_AF_ONCE].type = SANE_TYPE_BOOL;
s->opt[OPT_AF_ONCE].cap |=
(s->hw->info.model == CS2700) ? 0: SANE_CAP_INACTIVE;
s->val[OPT_AF_ONCE].w =
(s->hw->info.model == CS2700) ? SANE_TRUE: SANE_FALSE;
/* Manual focus */
s->opt[OPT_FOCUS].name = "focus";
s->opt[OPT_FOCUS].title = "Manual focus position";
s->opt[OPT_FOCUS].desc = "Set the optical system's focus position by hand. The default position is 128.";
s->opt[OPT_FOCUS].type = SANE_TYPE_INT;
s->opt[OPT_FOCUS].unit = SANE_UNIT_NONE;
s->opt[OPT_FOCUS].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_FOCUS].constraint.range = &s->hw->info.focus_range;
s->opt[OPT_FOCUS].cap |= SANE_CAP_INACTIVE;
s->val[OPT_FOCUS].w = 128;
/* Margins group: */
s->opt[OPT_MARGINS_GROUP].title = "Scan margins";
s->opt[OPT_MARGINS_GROUP].desc = "";
s->opt[OPT_MARGINS_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_MARGINS_GROUP].cap = SANE_CAP_ADVANCED;
s->opt[OPT_MARGINS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* top-left x */
s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_X].constraint.range = &s->hw->info.x_range;
s->val[OPT_TL_X].w = 0;
/* top-left y */
s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TL_Y].constraint.range = &s->hw->info.y_range;
s->val[OPT_TL_Y].w = 0;
/* bottom-right x */
s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_X].constraint.range = &s->hw->info.x_range;
s->val[OPT_BR_X].w = s->hw->info.x_range.max;
/* bottom-right y */
s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BR_Y].constraint.range = &s->hw->info.y_range;
s->val[OPT_BR_Y].w = s->hw->info.y_range.max;
/* Colors group: */
s->opt[OPT_COLORS_GROUP].title = "Extra color adjustments";
s->opt[OPT_COLORS_GROUP].desc = "";
s->opt[OPT_COLORS_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_COLORS_GROUP].cap = SANE_CAP_ADVANCED;
s->opt[OPT_COLORS_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* Positive/Negative switch for the CanoScan 300/600 models */
s->opt[OPT_HNEGATIVE].name = SANE_NAME_NEGATIVE;
s->opt[OPT_HNEGATIVE].title = SANE_TITLE_NEGATIVE;
s->opt[OPT_HNEGATIVE].desc = SANE_DESC_NEGATIVE;
s->opt[OPT_HNEGATIVE].type = SANE_TYPE_BOOL;
s->opt[OPT_HNEGATIVE].cap |=
(s->hw->info.model == CS2700) ? SANE_CAP_INACTIVE : 0;
s->val[OPT_HNEGATIVE].w = SANE_TRUE;
/* Same values vor highlight and shadow points for red, green, blue */
s->opt[OPT_BIND_HILO].name = SANE_NAME_RGB_BIND;
s->opt[OPT_BIND_HILO].title = SANE_TITLE_RGB_BIND;
s->opt[OPT_BIND_HILO].desc = SANE_DESC_RGB_BIND;
s->opt[OPT_BIND_HILO].type = SANE_TYPE_BOOL;
s->val[OPT_BIND_HILO].w = SANE_TRUE;
/* highlight point for red */
s->opt[OPT_HILITE_R].name = SANE_NAME_HIGHLIGHT_R;
s->opt[OPT_HILITE_R].title = SANE_TITLE_HIGHLIGHT_R;
s->opt[OPT_HILITE_R].desc = SANE_DESC_HIGHLIGHT_R;
s->opt[OPT_HILITE_R].type = SANE_TYPE_INT;
s->opt[OPT_HILITE_R].unit = SANE_UNIT_NONE;
s->opt[OPT_HILITE_R].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_HILITE_R].constraint.range = &s->hw->info.HiliteR_range;
s->opt[OPT_HILITE_R].cap |= SANE_CAP_INACTIVE;
s->val[OPT_HILITE_R].w = 0;
/* shadow point for red */
s->opt[OPT_SHADOW_R].name = SANE_NAME_SHADOW_R;
s->opt[OPT_SHADOW_R].title = SANE_TITLE_SHADOW_R;
s->opt[OPT_SHADOW_R].desc = SANE_DESC_SHADOW_R;
s->opt[OPT_SHADOW_R].type = SANE_TYPE_INT;
s->opt[OPT_SHADOW_R].unit = SANE_UNIT_NONE;
s->opt[OPT_SHADOW_R].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_SHADOW_R].constraint.range = &s->hw->info.ShadowR_range;
s->opt[OPT_SHADOW_R].cap |= SANE_CAP_INACTIVE;
s->val[OPT_SHADOW_R].w = 0;
/* highlight point for green */
s->opt[OPT_HILITE_G].name = SANE_NAME_HIGHLIGHT;
s->opt[OPT_HILITE_G].title = SANE_TITLE_HIGHLIGHT;
s->opt[OPT_HILITE_G].desc = SANE_DESC_HIGHLIGHT;
s->opt[OPT_HILITE_G].type = SANE_TYPE_INT;
s->opt[OPT_HILITE_G].unit = SANE_UNIT_NONE;
s->opt[OPT_HILITE_G].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_HILITE_G].constraint.range = &s->hw->info.HiliteG_range;
s->val[OPT_HILITE_G].w = 255;
/* shadow point for green */
s->opt[OPT_SHADOW_G].name = SANE_NAME_SHADOW;
s->opt[OPT_SHADOW_G].title = SANE_TITLE_SHADOW;
s->opt[OPT_SHADOW_G].desc = SANE_DESC_SHADOW;
s->opt[OPT_SHADOW_G].type = SANE_TYPE_INT;
s->opt[OPT_SHADOW_G].unit = SANE_UNIT_NONE;
s->opt[OPT_SHADOW_G].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_SHADOW_G].constraint.range = &s->hw->info.ShadowG_range;
s->val[OPT_SHADOW_G].w = 0;
/* highlight point for blue */
s->opt[OPT_HILITE_B].name = SANE_NAME_HIGHLIGHT_B;
s->opt[OPT_HILITE_B].title = SANE_TITLE_HIGHLIGHT_B;
s->opt[OPT_HILITE_B].desc = SANE_DESC_HIGHLIGHT_B;
s->opt[OPT_HILITE_B].type = SANE_TYPE_INT;
s->opt[OPT_HILITE_B].unit = SANE_UNIT_NONE;
s->opt[OPT_HILITE_B].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_HILITE_B].constraint.range = &s->hw->info.HiliteB_range;
s->opt[OPT_HILITE_B].cap |= SANE_CAP_INACTIVE;
s->val[OPT_HILITE_B].w = 0;
/* shadow point for blue */
s->opt[OPT_SHADOW_B].name = SANE_NAME_SHADOW_B;
s->opt[OPT_SHADOW_B].title = SANE_TITLE_SHADOW_B;
s->opt[OPT_SHADOW_B].desc = SANE_DESC_SHADOW_B;
s->opt[OPT_SHADOW_B].type = SANE_TYPE_INT;
s->opt[OPT_SHADOW_B].unit = SANE_UNIT_NONE;
s->opt[OPT_SHADOW_B].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_SHADOW_B].constraint.range = &s->hw->info.ShadowB_range;
s->opt[OPT_SHADOW_B].cap |= SANE_CAP_INACTIVE;
s->val[OPT_SHADOW_B].w = 0;
/* "Enhancement" group: */
s->opt[OPT_ENHANCEMENT_GROUP].title = "Enhancement";
s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* brightness */
s->opt[OPT_BRIGHTNESS].name = SANE_NAME_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].title = SANE_TITLE_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].desc = SANE_DESC_BRIGHTNESS;
s->opt[OPT_BRIGHTNESS].type = SANE_TYPE_INT;
s->opt[OPT_BRIGHTNESS].unit = SANE_UNIT_NONE;
s->opt[OPT_BRIGHTNESS].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_BRIGHTNESS].constraint.range = &s->hw->info.brightness_range;
s->val[OPT_BRIGHTNESS].w = 128;
/* contrast */
s->opt[OPT_CONTRAST].name = SANE_NAME_CONTRAST;
s->opt[OPT_CONTRAST].title = SANE_TITLE_CONTRAST;
s->opt[OPT_CONTRAST].desc = SANE_DESC_CONTRAST;
s->opt[OPT_CONTRAST].type = SANE_TYPE_INT;
s->opt[OPT_CONTRAST].unit = SANE_UNIT_NONE;
s->opt[OPT_CONTRAST].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_CONTRAST].constraint.range = &s->hw->info.contrast_range;
s->val[OPT_CONTRAST].w = 128;
/* threshold */
s->opt[OPT_THRESHOLD].name = SANE_NAME_THRESHOLD;
s->opt[OPT_THRESHOLD].title = SANE_TITLE_THRESHOLD;
s->opt[OPT_THRESHOLD].desc = SANE_DESC_THRESHOLD;
s->opt[OPT_THRESHOLD].type = SANE_TYPE_INT;
s->opt[OPT_THRESHOLD].unit = SANE_UNIT_NONE;
s->opt[OPT_THRESHOLD].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_THRESHOLD].constraint.range = &s->hw->info.threshold_range;
s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
s->val[OPT_THRESHOLD].w = 128;
s->opt[OPT_MIRROR].name = "mirror";
s->opt[OPT_MIRROR].title = "Mirror image";
s->opt[OPT_MIRROR].desc = "Mirror the image horizontally";
s->opt[OPT_MIRROR].type = SANE_TYPE_BOOL;
s->val[OPT_MIRROR].w = SANE_FALSE;
/* analog-gamma curve */
s->opt[OPT_CUSTOM_GAMMA].name = SANE_NAME_CUSTOM_GAMMA;
s->opt[OPT_CUSTOM_GAMMA].title = SANE_TITLE_CUSTOM_GAMMA;
s->opt[OPT_CUSTOM_GAMMA].desc = SANE_DESC_CUSTOM_GAMMA;
s->opt[OPT_CUSTOM_GAMMA].type = SANE_TYPE_BOOL;
s->val[OPT_CUSTOM_GAMMA].w = SANE_FALSE;
/* bind analog-gamma */
s->opt[OPT_CUSTOM_GAMMA_BIND].name = SANE_NAME_ANALOG_GAMMA_BIND;
s->opt[OPT_CUSTOM_GAMMA_BIND].title = SANE_TITLE_ANALOG_GAMMA_BIND;
s->opt[OPT_CUSTOM_GAMMA_BIND].desc = SANE_DESC_ANALOG_GAMMA_BIND;
s->opt[OPT_CUSTOM_GAMMA_BIND].type = SANE_TYPE_BOOL;
s->opt[OPT_CUSTOM_GAMMA_BIND].cap |= SANE_CAP_INACTIVE;
s->val[OPT_CUSTOM_GAMMA_BIND].w = SANE_TRUE;
/* grayscale gamma vector */
s->opt[OPT_GAMMA_VECTOR].name = SANE_NAME_ANALOG_GAMMA;
s->opt[OPT_GAMMA_VECTOR].title = SANE_TITLE_ANALOG_GAMMA;
s->opt[OPT_GAMMA_VECTOR].desc = SANE_DESC_ANALOG_GAMMA;
s->opt[OPT_GAMMA_VECTOR].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR].size = 256 * sizeof (SANE_Word);
s->opt[OPT_GAMMA_VECTOR].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR].wa = &s->gamma_table[0][0];
/* red gamma vector */
s->opt[OPT_GAMMA_VECTOR_R].name = SANE_NAME_ANALOG_GAMMA_R;
s->opt[OPT_GAMMA_VECTOR_R].title = SANE_TITLE_ANALOG_GAMMA_R;
s->opt[OPT_GAMMA_VECTOR_R].desc = SANE_DESC_ANALOG_GAMMA_R;
s->opt[OPT_GAMMA_VECTOR_R].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_R].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_R].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_R].size = 256 * sizeof (SANE_Word);
s->opt[OPT_GAMMA_VECTOR_R].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_R].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_R].wa = &s->gamma_table[1][0];
/* green gamma vector */
s->opt[OPT_GAMMA_VECTOR_G].name = SANE_NAME_ANALOG_GAMMA_G;
s->opt[OPT_GAMMA_VECTOR_G].title = SANE_TITLE_ANALOG_GAMMA_G;
s->opt[OPT_GAMMA_VECTOR_G].desc = SANE_DESC_ANALOG_GAMMA_G;
s->opt[OPT_GAMMA_VECTOR_G].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_G].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_G].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_G].size = 256 * sizeof (SANE_Word);
s->opt[OPT_GAMMA_VECTOR_G].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_G].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_G].wa = &s->gamma_table[2][0];
/* blue gamma vector */
s->opt[OPT_GAMMA_VECTOR_B].name = SANE_NAME_ANALOG_GAMMA_B;
s->opt[OPT_GAMMA_VECTOR_B].title = SANE_TITLE_ANALOG_GAMMA_B;
s->opt[OPT_GAMMA_VECTOR_B].desc = SANE_DESC_ANALOG_GAMMA_B;
s->opt[OPT_GAMMA_VECTOR_B].type = SANE_TYPE_INT;
s->opt[OPT_GAMMA_VECTOR_B].cap |= SANE_CAP_INACTIVE;
s->opt[OPT_GAMMA_VECTOR_B].unit = SANE_UNIT_NONE;
s->opt[OPT_GAMMA_VECTOR_B].size = 256 * sizeof (SANE_Word);
s->opt[OPT_GAMMA_VECTOR_B].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_GAMMA_VECTOR_B].constraint.range = &u8_range;
s->val[OPT_GAMMA_VECTOR_B].wa = &s->gamma_table[3][0];
s->opt[OPT_AE].name = "ae";
s->opt[OPT_AE].title = "Auto Exposure";
s->opt[OPT_AE].desc = "Enable/disable the Auto Exposure feature";
s->opt[OPT_AE].type = SANE_TYPE_BOOL;
s->val[OPT_AE].w = SANE_FALSE;
/* "Eject" group (active only for model 2700F) */
s->opt[OPT_EJECT_GROUP].title = "Medium handling";
s->opt[OPT_EJECT_GROUP].desc = "";
s->opt[OPT_EJECT_GROUP].type = SANE_TYPE_GROUP;
/* s->opt[OPT_EJECT_GROUP].cap |= */
/* (s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE; */
s->opt[OPT_EJECT_GROUP].cap = 0;
s->opt[OPT_EJECT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
/* eject after scan */
s->opt[OPT_EJECT_AFTERSCAN].name = "eject-after-scan";
s->opt[OPT_EJECT_AFTERSCAN].title = "Eject film after each scan";
s->opt[OPT_EJECT_AFTERSCAN].desc =
"Automatically eject the film from the device after each scan";
s->opt[OPT_EJECT_AFTERSCAN].cap |=
(s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE;
s->opt[OPT_EJECT_AFTERSCAN].type = SANE_TYPE_BOOL;
s->val[OPT_EJECT_AFTERSCAN].w = SANE_FALSE;
/* eject before exit */
s->opt[OPT_EJECT_BEFOREEXIT].name = "eject-before-exit";
s->opt[OPT_EJECT_BEFOREEXIT].title = "Eject film before exit";
s->opt[OPT_EJECT_BEFOREEXIT].desc =
"Automatically eject the film from the device before exiting the program";
s->opt[OPT_EJECT_BEFOREEXIT].cap |=
(s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE;
s->opt[OPT_EJECT_BEFOREEXIT].type = SANE_TYPE_BOOL;
s->val[OPT_EJECT_BEFOREEXIT].w = SANE_FALSE;
/* eject now */
s->opt[OPT_EJECT_NOW].name = "eject-now";
s->opt[OPT_EJECT_NOW].title = "Eject film now";
s->opt[OPT_EJECT_NOW].desc = "Eject the film from the device *now*";
s->opt[OPT_EJECT_NOW].type = SANE_TYPE_BUTTON;
s->opt[OPT_EJECT_NOW].unit = SANE_UNIT_NONE;
s->opt[OPT_EJECT_NOW].cap |=
(s->hw->info.model == CS2700) ? 0 : SANE_CAP_INACTIVE;
s->opt[OPT_EJECT_NOW].constraint_type = SANE_CONSTRAINT_NONE;
s->opt[OPT_EJECT_NOW].constraint.range = NULL;
/* "TPU" group: */
s->opt[OPT_TPU_GROUP].title = "Transparency Unit";
s->opt[OPT_TPU_GROUP].desc = "";
s->opt[OPT_TPU_GROUP].type = SANE_TYPE_GROUP;
s->opt[OPT_TPU_GROUP].cap = 0;
s->opt[OPT_TPU_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
s->opt[OPT_TPU_GROUP].cap |=
(s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE;
/* Transparency Unit (FAU, Film Adapter Unit) */
s->opt[OPT_TPU_ON].name = "transparency-unit-on-off";
s->opt[OPT_TPU_ON].title =
(s->hw->tpu.Status == TPU_STAT_ACTIVE) ?
"Turn Off the Transparency Unit" : "Turn On the Transparency Unit";
s->opt[OPT_TPU_ON].desc =
"Switch on/off the Transparency Unit (FAU, Film Adapter Unit)";
s->opt[OPT_TPU_ON].type = SANE_TYPE_BOOL;
s->opt[OPT_TPU_ON].unit = SANE_UNIT_NONE;
s->val[OPT_TPU_ON].w = s->hw->tpu.Status;
s->opt[OPT_TPU_ON].cap |=
(s->hw->tpu.Status != TPU_STAT_NONE) ? 0 : SANE_CAP_INACTIVE;
s->opt[OPT_TPU_PN].name = "transparency-unit-negative-film";
s->opt[OPT_TPU_PN].title = "Negative Film";
s->opt[OPT_TPU_PN].desc = "Positive or Negative Film";
s->opt[OPT_TPU_PN].type = SANE_TYPE_BOOL;
s->opt[OPT_TPU_PN].unit = SANE_UNIT_NONE;
s->val[OPT_TPU_PN].w = s->hw->tpu.PosNeg;
s->opt[OPT_TPU_PN].cap |=
(s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE;
/* density control mode */
s->opt[OPT_TPU_DCM].name = "TPMDC";
s->opt[OPT_TPU_DCM].title = "Density Control";
s->opt[OPT_TPU_DCM].desc = "Set Density Control Mode";
s->opt[OPT_TPU_DCM].type = SANE_TYPE_STRING;
s->opt[OPT_TPU_DCM].size = max_string_size (tpu_dc_mode_list);
s->opt[OPT_TPU_DCM].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_TPU_DCM].constraint.string_list = tpu_dc_mode_list;
s->val[OPT_TPU_DCM].s = strdup (tpu_dc_mode_list[s->hw->tpu.ControlMode]);
s->opt[OPT_TPU_DCM].cap |=
(s->hw->tpu.Status == TPU_STAT_ACTIVE) ? 0 : SANE_CAP_INACTIVE;
/* Transparency Ratio */
s->opt[OPT_TPU_TRANSPARENCY].name = "Transparency-Ratio";
s->opt[OPT_TPU_TRANSPARENCY].title = "Transparency Ratio";
s->opt[OPT_TPU_TRANSPARENCY].desc = "";
s->opt[OPT_TPU_TRANSPARENCY].type = SANE_TYPE_INT;
s->opt[OPT_TPU_TRANSPARENCY].unit = SANE_UNIT_NONE;
s->opt[OPT_TPU_TRANSPARENCY].constraint_type = SANE_CONSTRAINT_RANGE;
s->opt[OPT_TPU_TRANSPARENCY].constraint.range =
&s->hw->info.TPU_Transparency_range;
s->val[OPT_TPU_TRANSPARENCY].w = s->hw->tpu.Transparency;
s->opt[OPT_TPU_TRANSPARENCY].cap |=
(s->hw->tpu.Status == TPU_STAT_ACTIVE &&
s->hw->tpu.ControlMode == 3) ?
0 : SANE_CAP_INACTIVE;
/* Select Film type */
s->opt[OPT_TPU_FILMTYPE].name = "Filmtype";
s->opt[OPT_TPU_FILMTYPE].title = "Select Film type";
s->opt[OPT_TPU_FILMTYPE].desc = "Select the film type";
s->opt[OPT_TPU_FILMTYPE].type = SANE_TYPE_STRING;
s->opt[OPT_TPU_FILMTYPE].size = max_string_size (tpu_filmtype_list);
s->opt[OPT_TPU_FILMTYPE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
s->opt[OPT_TPU_FILMTYPE].constraint.string_list = tpu_filmtype_list;
s->val[OPT_TPU_FILMTYPE].s = strdup (tpu_filmtype_list[s->hw->tpu.FilmType]);
s->opt[OPT_TPU_FILMTYPE].cap |=
(s->hw->tpu.Status == TPU_STAT_ACTIVE &&
s->hw->tpu.ControlMode == 1) ?
0 : SANE_CAP_INACTIVE;
/* preview */
s->opt[OPT_PREVIEW].name = SANE_NAME_PREVIEW;
s->opt[OPT_PREVIEW].title = SANE_TITLE_PREVIEW;
s->opt[OPT_PREVIEW].desc = SANE_DESC_PREVIEW;
s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
s->opt[OPT_PREVIEW].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
s->val[OPT_PREVIEW].w = SANE_FALSE;
DBG (1, "<< init_options\n");
return SANE_STATUS_GOOD;
}
/**************************************************************************/
static SANE_Status
attach_one (const char *dev)
{
DBG (1, ">> attach_one\n");
attach (dev, 0);
DBG (1, "<< attach_one\n");
return SANE_STATUS_GOOD;
}
/**************************************************************************/
static SANE_Status
do_focus(CANON_Scanner *s)
{
SANE_Status status;
u_char ebuf[74];
size_t buf_size;
DBG (3, "do_focus: sending GET FILM STATUS\n");
memset (ebuf, 0, sizeof (ebuf));
buf_size = 4;
status = get_film_status (s->fd, ebuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "do_focus: GET FILM STATUS failed\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
DBG(3, "focus point before autofocus : %d\n", ebuf[3]);
if (s->val[OPT_AF].w == SANE_TRUE)
{
/* Auto-Focus */
status = execute_auto_focus (s->fd, AUTO_FOCUS, NO_AUTO_SCAN_SPEED,
NO_AUTO_EXPOSURE, 0, NULL, 0);
}
else
{
/* Manual Focus */
status = execute_auto_focus (s->fd, MANUAL_FOCUS, NO_AUTO_SCAN_SPEED,
NO_AUTO_EXPOSURE, s->val[OPT_FOCUS].w, NULL, 0);
}
if (status != SANE_STATUS_GOOD)
{
DBG (7, "EXECUTE_AUTO_FOCUS failed\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
DBG (3, "do_focus: sending GET FILM STATUS\n");
memset (ebuf, 0, sizeof (ebuf));
buf_size = 4;
status = get_film_status (s->fd, ebuf, &buf_size);
if (status != SANE_STATUS_GOOD)
{
DBG (1, "do_focus: GET FILM STATUS failed\n");
sanei_scsi_close (s->fd);
s->fd = -1;
return (SANE_STATUS_INVAL);
}
DBG(3, "focus point after autofocus : %d\n", ebuf[3]);
return(SANE_STATUS_GOOD);
}
/**************************************************************************/
#include "canon-sane.c"