summaryrefslogtreecommitdiff
path: root/backend/hp3900_sane.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/hp3900_sane.c')
-rw-r--r--backend/hp3900_sane.c2734
1 files changed, 2734 insertions, 0 deletions
diff --git a/backend/hp3900_sane.c b/backend/hp3900_sane.c
new file mode 100644
index 0000000..a1c381c
--- /dev/null
+++ b/backend/hp3900_sane.c
@@ -0,0 +1,2734 @@
+/* HP Scanjet 3900 series - SANE Backend controller
+ Copyright (C) 2005-2009 Jonathan Bravo Lopez <jkdsoft@gmail.com>
+
+ 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.
+*/
+
+/* Backend Code for SANE*/
+#define HP3900_CONFIG_FILE "hp3900.conf"
+#define GAMMA_DEFAULT 1.0
+
+#include "../include/sane/config.h"
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/sanei_backend.h"
+#include "../include/sane/sanei_config.h"
+#include "../include/sane/saneopts.h"
+#include "../include/sane/sanei_usb.h"
+#include "../include/sane/sanei_debug.h"
+
+#include "hp3900_rts8822.c"
+
+struct st_convert
+{
+ SANE_Int colormode;
+ SANE_Int depth;
+ SANE_Int threshold;
+ SANE_Int negative;
+ SANE_Int real_depth;
+};
+
+/* options enumerator */
+typedef enum
+{
+ opt_begin = 0,
+
+ grp_geometry,
+ opt_tlx, opt_tly, opt_brx, opt_bry,
+ opt_resolution,
+
+ /* gamma tables */
+ opt_gamma_red,
+ opt_gamma_green,
+ opt_gamma_blue,
+
+ opt_scantype,
+ opt_colormode,
+ opt_depth,
+ opt_threshold,
+
+ /* debugging options */
+ grp_debug,
+ opt_model,
+ opt_negative,
+ opt_nogamma,
+ opt_nowshading,
+ opt_realdepth,
+ opt_emulategray,
+ opt_nowarmup,
+ opt_dbgimages,
+ opt_reset,
+
+ /* device information */
+ grp_info,
+ opt_chipname,
+ opt_chipid,
+ opt_scancount,
+ opt_infoupdate,
+
+ /* supported buttons. RTS8822 supports up to 6 buttons */
+ grp_sensors,
+ opt_button_0,
+ opt_button_1,
+ opt_button_2,
+ opt_button_3,
+ opt_button_4,
+ opt_button_5,
+
+ opt_count
+} EOptionIndex;
+
+/* linked list of SANE_Device structures */
+typedef struct TDevListEntry
+{
+ struct TDevListEntry *pNext;
+ SANE_Device dev;
+ char *devname;
+} TDevListEntry;
+
+typedef struct
+{
+ char *pszVendor;
+ char *pszName;
+} TScannerModel;
+
+typedef union
+{
+ SANE_Word w;
+ SANE_Word *wa; /* word array */
+ SANE_String s;
+} TOptionValue;
+
+typedef struct
+{
+ SANE_Int model;
+ SANE_Option_Descriptor aOptions[opt_count];
+ TOptionValue aValues[opt_count];
+ struct params ScanParams;
+
+ /* lists */
+ SANE_String_Const *list_colormodes;
+ SANE_Int *list_depths;
+ SANE_String_Const *list_models;
+ SANE_Int *list_resolutions;
+ SANE_String_Const *list_sources;
+
+ SANE_Word *aGammaTable[3]; /* a 16-to-16 bit color lookup table */
+ SANE_Range rng_gamma;
+
+ /* reading image */
+ SANE_Byte *image;
+ SANE_Byte *rest;
+ SANE_Int rest_amount;
+ SANE_Int mylin;
+
+ /* convertion settings */
+ struct st_convert cnv;
+
+ /* ranges */
+ SANE_Range rng_threshold;
+ SANE_Range rng_horizontal;
+ SANE_Range rng_vertical;
+
+ SANE_Int scan_count;
+ SANE_Int fScanning; /* TRUE if actively scanning */
+} TScanner;
+
+/* functions to manage backend's options */
+static void options_init (TScanner * scanner);
+static void options_free (TScanner * scanner);
+
+/* devices listing */
+static SANE_Int _ReportDevice (TScannerModel * pModel,
+ const char *pszDeviceName);
+static SANE_Status attach_one_device (SANE_String_Const devname);
+
+/* capabilities */
+static SANE_Status bknd_colormodes (TScanner * scanner, SANE_Int model);
+static void bknd_constrains (TScanner * scanner, SANE_Int source,
+ SANE_Int type);
+static SANE_Status bknd_depths (TScanner * scanner, SANE_Int model);
+static SANE_Status bknd_info (TScanner * scanner);
+static SANE_Status bknd_models (TScanner * scanner);
+static SANE_Status bknd_resolutions (TScanner * scanner, SANE_Int model);
+static SANE_Status bknd_sources (TScanner * scanner, SANE_Int model);
+
+/* convertions */
+static void Color_Negative (SANE_Byte * buffer, SANE_Int size,
+ SANE_Int depth);
+static void Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth);
+static void Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size,
+ SANE_Int threshold);
+static void Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size,
+ SANE_Byte * to_buffer);
+
+/* gamma functions */
+static void gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size,
+ SANE_Int depth);
+static SANE_Int gamma_create (TScanner * s, double gamma);
+static void gamma_free (TScanner * s);
+
+static SANE_Int Get_Colormode (SANE_String colormode);
+static SANE_Int Get_Model (SANE_String model);
+static SANE_Int Get_Source (SANE_String source);
+static SANE_Int GetUSB_device_model (SANE_String_Const name);
+static size_t max_string_size (const SANE_String_Const strings[]);
+
+static SANE_Status get_button_status (TScanner * s);
+
+/* reading buffers */
+static SANE_Status img_buffers_alloc (TScanner * scanner, SANE_Int size);
+static SANE_Status img_buffers_free (TScanner * scanner);
+
+static SANE_Status option_get (TScanner * scanner, SANE_Int optid,
+ void *result);
+static SANE_Status option_set (TScanner * scanner, SANE_Int optid,
+ void *value, SANE_Int * pInfo);
+
+static void Set_Coordinates (SANE_Int scantype, SANE_Int resolution,
+ struct st_coords *coords);
+static SANE_Int set_ScannerModel (SANE_Int proposed, SANE_Int product,
+ SANE_Int vendor);
+static void Silent_Compile (void);
+static SANE_Status Translate_coords (struct st_coords *coords);
+
+/* SANE functions */
+void sane_cancel (SANE_Handle h);
+void sane_close (SANE_Handle h);
+SANE_Status sane_control_option (SANE_Handle h, SANE_Int n,
+ SANE_Action Action, void *pVal,
+ SANE_Int * pInfo);
+void sane_exit (void);
+SANE_Status sane_get_devices (const SANE_Device *** device_list,
+ SANE_Bool local_only);
+const SANE_Option_Descriptor *sane_get_option_descriptor (SANE_Handle h,
+ SANE_Int n);
+SANE_Status sane_get_parameters (SANE_Handle h, SANE_Parameters * p);
+SANE_Status sane_get_select_fd (SANE_Handle handle, SANE_Int * fd);
+SANE_Status sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize);
+SANE_Status sane_open (SANE_String_Const name, SANE_Handle * h);
+SANE_Status sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen,
+ SANE_Int * len);
+SANE_Status sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking);
+SANE_Status sane_start (SANE_Handle h);
+
+/* variables */
+static struct st_device *device = NULL;
+static TDevListEntry *_pFirstSaneDev = 0;
+static SANE_Int iNumSaneDev = 0;
+static const SANE_Device **_pSaneDevList = 0;
+
+/* Own functions */
+
+static SANE_Status
+bknd_resolutions (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_resolutions(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_Int *res = NULL;
+
+ switch (model)
+ {
+ case BQ5550:
+ case UA4900:
+ {
+ SANE_Int myres[] = { 8, 50, 75, 100, 150, 200, 300, 600, 1200 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ case HPG2710:
+ case HP3800:
+ {
+ /* 1200 and 2400 dpi are disabled until problems are solved */
+ SANE_Int myres[] = { 7, 50, 75, 100, 150, 200, 300, 600 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ case HP4370:
+ case HPG3010:
+ case HPG3110:
+ {
+ SANE_Int myres[] =
+ { 10, 50, 75, 100, 150, 200, 300, 600, 1200, 2400, 4800 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+
+ default: /* HP3970 & HP4070 & UA4900 */
+ {
+ SANE_Int myres[] =
+ { 9, 50, 75, 100, 150, 200, 300, 600, 1200, 2400 };
+
+ res = (SANE_Int *) malloc (sizeof (myres));
+ if (res != NULL)
+ memcpy (res, &myres, sizeof (myres));
+ }
+ break;
+ }
+
+ if (res != NULL)
+ {
+ if (scanner->list_resolutions != NULL)
+ free (scanner->list_resolutions);
+
+ scanner->list_resolutions = res;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_models (TScanner * scanner)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_models:\n");
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *model = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_String_Const mymodel[] =
+ { "HP3800", "HP3970", "HP4070", "HP4370", "UA4900", "HPG3010",
+"BQ5550", "HPG2710", "HPG3110", 0 };
+
+ /* allocate space to save list */
+ model = (SANE_String_Const *) malloc (sizeof (mymodel));
+ if (model != NULL)
+ memcpy (model, &mymodel, sizeof (mymodel));
+
+ if (model != NULL)
+ {
+ /* free previous list */
+ if (scanner->list_models != NULL)
+ free (scanner->list_models);
+
+ /* set new list */
+ scanner->list_models = model;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_colormodes (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_colormodes(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *colormode = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_String_Const mycolormode[] =
+ { SANE_VALUE_SCAN_MODE_COLOR, SANE_VALUE_SCAN_MODE_GRAY, SANE_VALUE_SCAN_MODE_LINEART, 0 };
+
+ /* silence gcc */
+ model = model;
+
+ colormode = (SANE_String_Const *) malloc (sizeof (mycolormode));
+ if (colormode != NULL)
+ memcpy (colormode, &mycolormode, sizeof (mycolormode));
+
+ if (colormode != NULL)
+ {
+ if (scanner->list_colormodes != NULL)
+ free (scanner->list_colormodes);
+
+ scanner->list_colormodes = colormode;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_sources (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_sources(*scanner, model=%i)\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_String_Const *source = NULL;
+
+ switch (model)
+ {
+ case UA4900:
+ {
+ SANE_String_Const mysource[] = { SANE_I18N ("Flatbed"), 0 };
+ source = (SANE_String_Const *) malloc (sizeof (mysource));
+ if (source != NULL)
+ memcpy (source, &mysource, sizeof (mysource));
+ }
+ break;
+ default: /* hp3970, hp4070, hp4370 and others */
+ {
+ SANE_String_Const mysource[] =
+ { SANE_I18N ("Flatbed"), SANE_I18N ("Slide"),
+SANE_I18N ("Negative"), 0 };
+ source = (SANE_String_Const *) malloc (sizeof (mysource));
+ if (source != NULL)
+ memcpy (source, &mysource, sizeof (mysource));
+ }
+ break;
+ }
+
+ if (source != NULL)
+ {
+ if (scanner->list_sources != NULL)
+ free (scanner->list_sources);
+
+ scanner->list_sources = source;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_depths (TScanner * scanner, SANE_Int model)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_depths(*scanner, model=%i\n", model);
+
+ if (scanner != NULL)
+ {
+ SANE_Int *depth = NULL;
+
+ /* at this moment all devices use the same list */
+ SANE_Int mydepth[] = { 2, 8, 16 }; /*{3, 8, 12, 16}; */
+
+ /* silence gcc */
+ model = model;
+
+ depth = (SANE_Int *) malloc (sizeof (mydepth));
+ if (depth != NULL)
+ memcpy (depth, &mydepth, sizeof (mydepth));
+
+ if (depth != NULL)
+ {
+ if (scanner->list_depths != NULL)
+ free (scanner->list_depths);
+
+ scanner->list_depths = depth;
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ return rst;
+}
+
+static SANE_Status
+bknd_info (TScanner * scanner)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+
+ DBG (DBG_FNC, "> bknd_info(*scanner)");
+
+ if (scanner != NULL)
+ {
+ char data[256];
+
+ /* update chipset name */
+ Chipset_Name (device, data, 255);
+ if (scanner->aValues[opt_chipname].s != NULL)
+ {
+ free (scanner->aValues[opt_chipname].s);
+ scanner->aValues[opt_chipname].s = NULL;
+ }
+
+ scanner->aValues[opt_chipname].s = strdup (data);
+ scanner->aOptions[opt_chipname].size = strlen (data) + 1;
+
+ /* update chipset id */
+ scanner->aValues[opt_chipid].w = Chipset_ID (device);
+
+ /* update scans counter */
+ scanner->aValues[opt_scancount].w = RTS_ScanCounter_Get (device);
+
+ rst = SANE_STATUS_GOOD;
+ }
+
+ return rst;
+}
+
+static SANE_Int
+GetUSB_device_model (SANE_String_Const name)
+{
+ SANE_Int usbid, model;
+
+ /* default model is unknown */
+ model = -1;
+
+ /* open usb device */
+ if (sanei_usb_open (name, &usbid) == SANE_STATUS_GOOD)
+ {
+ SANE_Int vendor, product;
+
+ if (sanei_usb_get_vendor_product (usbid, &vendor, &product) ==
+ SANE_STATUS_GOOD)
+ model = Device_get (product, vendor);
+
+ sanei_usb_close (usbid);
+ }
+
+ return model;
+}
+
+static void
+Silent_Compile (void)
+{
+ /*
+ There are some functions in hp3900_rts8822.c that aren't used yet.
+ To avoid compilation warnings we will use them here
+ */
+
+ SANE_Byte a = 1;
+
+ if (a == 0)
+ {
+ Buttons_Status (device);
+ Calib_WriteTable (device, NULL, 0, 0);
+ Gamma_GetTables (device, NULL);
+ }
+}
+
+static void
+bknd_constrains (TScanner * scanner, SANE_Int source, SANE_Int type)
+{
+ struct st_coords *coords = Constrains_Get (device, source);
+
+ if ((coords != NULL) && (scanner != NULL))
+ {
+ switch (type)
+ {
+ case 1: /* Y */
+ scanner->rng_vertical.max = coords->height;
+ break;
+ default: /* X */
+ scanner->rng_horizontal.max = coords->width;
+ break;
+ }
+ }
+}
+
+static SANE_Status
+img_buffers_free (TScanner * scanner)
+{
+ if (scanner != NULL)
+ {
+ if (scanner->image != NULL)
+ {
+ free (scanner->image);
+ scanner->image = NULL;
+ }
+
+ if (scanner->rest != NULL)
+ {
+ free (scanner->rest);
+ scanner->rest = NULL;
+ }
+
+ scanner->rest_amount = 0;
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+img_buffers_alloc (TScanner * scanner, SANE_Int size)
+{
+ SANE_Status rst;
+
+ /* default result at this point */
+ rst = SANE_STATUS_INVAL;
+
+ if (scanner != NULL)
+ {
+ /* default result at this point */
+ rst = SANE_STATUS_NO_MEM;
+
+ /* free previous allocs */
+ img_buffers_free (scanner);
+
+ scanner->image = (SANE_Byte *) malloc (size * sizeof (SANE_Byte));
+ if (scanner->image != NULL)
+ {
+ scanner->rest = (SANE_Byte *) malloc (size * sizeof (SANE_Byte));
+ if (scanner->rest != NULL)
+ rst = SANE_STATUS_GOOD; /* ok !! */
+ }
+
+ if (rst != SANE_STATUS_GOOD)
+ img_buffers_free (scanner);
+ }
+
+ return rst;
+}
+
+static SANE_Int
+set_ScannerModel (SANE_Int proposed, SANE_Int product, SANE_Int vendor)
+{
+ /* This function will set the device behaviour */
+
+ SANE_Int current = Device_get (product, vendor);
+ char *sdevname[10] =
+ { "Unknown", "HP3970", "HP4070", "HP4370", "UA4900", "HP3800", "HPG3010",
+"BQ5550", "HPG2710", "HPG3110" };
+
+ DBG (DBG_FNC,
+ "> set_ScannerModel(proposed=%i, product=%04x, vendor=%04x)\n",
+ proposed, product, vendor);
+
+ if (proposed < 0)
+ {
+ if ((current < 0) || (current >= DEVSCOUNT))
+ {
+ DBG (DBG_VRB, " -> Unknown device. Defaulting to HP3970...\n");
+ RTS_Debug->dev_model = HP3970;
+ }
+ else
+ {
+ RTS_Debug->dev_model = current;
+ DBG (DBG_VRB, " -> Device model is %s\n", sdevname[current + 1]);
+ }
+ }
+ else
+ {
+ if (proposed < DEVSCOUNT)
+ {
+ RTS_Debug->dev_model = proposed;
+ DBG (DBG_VRB, " -> Device %s , treating as %s ...\n",
+ sdevname[current + 1], sdevname[proposed + 1]);
+ }
+ else
+ {
+ if ((current >= 0) && (current < DEVSCOUNT))
+ {
+ RTS_Debug->dev_model = current;
+ DBG (DBG_VRB,
+ " -> Device not supported. Defaulting to %s ...\n",
+ sdevname[current + 1]);
+ }
+ else
+ {
+ RTS_Debug->dev_model = HP3970;
+ DBG (DBG_VRB,
+ "-> Device not supported. Defaulting to HP3970...\n");
+ }
+ }
+ }
+
+ return OK;
+}
+
+static void
+Set_Coordinates (SANE_Int scantype, SANE_Int resolution,
+ struct st_coords *coords)
+{
+ struct st_coords *limits = Constrains_Get (device, scantype);
+
+ DBG (DBG_FNC, "> Set_Coordinates(res=%i, *coords):\n", resolution);
+
+ if (coords->left == -1)
+ coords->left = 0;
+
+ if (coords->width == -1)
+ coords->width = limits->width;
+
+ if (coords->top == -1)
+ coords->top = 0;
+
+ if (coords->height == -1)
+ coords->height = limits->height;
+
+ DBG (DBG_FNC, " -> Coords [MM] : xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+
+ coords->left = MM_TO_PIXEL (coords->left, resolution);
+ coords->width = MM_TO_PIXEL (coords->width, resolution);
+ coords->top = MM_TO_PIXEL (coords->top, resolution);
+ coords->height = MM_TO_PIXEL (coords->height, resolution);
+
+ DBG (DBG_FNC, " -> Coords [px] : xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+
+ Constrains_Check (device, resolution, scantype, coords);
+
+ DBG (DBG_FNC, " -> Coords [check]: xy(%i, %i) wh(%i, %i)\n", coords->left,
+ coords->top, coords->width, coords->height);
+}
+
+static void
+Color_Negative (SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ if (buffer != NULL)
+ {
+ SANE_Int a;
+ SANE_Int max_value = (1 << depth) - 1;
+
+ if (depth > 8)
+ {
+ USHORT *sColor = (void *) buffer;
+ for (a = 0; a < size / 2; a++)
+ {
+ *sColor = max_value - *sColor;
+ sColor++;
+ }
+ }
+ else
+ {
+ for (a = 0; a < size; a++)
+ *(buffer + a) = max_value - *(buffer + a);
+ }
+ }
+}
+
+static SANE_Status
+get_button_status (TScanner * s)
+{
+ if (s != NULL)
+ {
+ SANE_Int a, b, status, btn;
+
+ b = 1;
+ status = Buttons_Released (device) & 63;
+ for (a = 0; a < 6; a++)
+ {
+ if ((status & b) != 0)
+ {
+ btn = Buttons_Order (device, b);
+ if (btn != -1)
+ s->aValues[opt_button_0 + btn].w = SANE_TRUE;
+ }
+
+ b <<= 1;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static void
+Depth_16_to_8 (SANE_Byte * from_buffer, SANE_Int size, SANE_Byte * to_buffer)
+{
+ if ((from_buffer != NULL) && (to_buffer != NULL))
+ {
+ SANE_Int a, b;
+
+ a = 1;
+ b = 0;
+
+ while (a < size)
+ {
+ *(to_buffer + b) = *(from_buffer + a);
+ a += 2;
+ b++;
+ }
+ }
+}
+
+static void
+Gray_to_Lineart (SANE_Byte * buffer, SANE_Int size, SANE_Int threshold)
+{
+ /* code provided by tobias leutwein */
+
+ if (buffer != NULL)
+ {
+ SANE_Byte toBufferByte;
+ SANE_Int fromBufferPos_i = 0;
+ SANE_Int toBufferPos_i = 0;
+ SANE_Int bitPos_i;
+
+ while (fromBufferPos_i < size)
+ {
+ toBufferByte = 0;
+
+ for (bitPos_i = 7; bitPos_i != (-1); bitPos_i--)
+ {
+ if ((fromBufferPos_i < size)
+ && (buffer[fromBufferPos_i] < threshold))
+ toBufferByte |= (1u << bitPos_i);
+
+ fromBufferPos_i++;
+ }
+
+ buffer[toBufferPos_i] = toBufferByte;
+ toBufferPos_i++;
+ }
+ }
+}
+
+static void
+Color_to_Gray (SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ /* converts 3 color channel into 1 gray channel of specified bit depth */
+
+ if (buffer != NULL)
+ {
+ SANE_Int c, chn, chn_size;
+ SANE_Byte *ptr_src = NULL;
+ SANE_Byte *ptr_dst = NULL;
+ float data, chn_data;
+ float coef[3] = { 0.299, 0.587, 0.114 }; /* coefficients per channel */
+
+ chn_size = (depth > 8) ? 2 : 1;
+ ptr_src = (void *) buffer;
+ ptr_dst = (void *) buffer;
+
+ for (c = 0; c < size / (3 * chn_size); c++)
+ {
+ data = 0.;
+
+ /* get, apply coeffs and sum channels */
+ for (chn = 0; chn < 3; chn++)
+ {
+ chn_data = data_lsb_get (ptr_src + (chn * chn_size), chn_size);
+ data += (chn_data * coef[chn]);
+ }
+
+ /* save result */
+ data_lsb_set (ptr_dst, (SANE_Int) data, chn_size);
+
+ ptr_src += 3 * chn_size; /* next triplet */
+ ptr_dst += chn_size;
+ }
+ }
+}
+
+static void
+gamma_free (TScanner * s)
+{
+ DBG (DBG_FNC, "> gamma_free()\n");
+
+ if (s != NULL)
+ {
+ /* Destroy gamma tables */
+ SANE_Int a;
+
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ if (s->aGammaTable[a] != NULL)
+ {
+ free (s->aGammaTable[a]);
+ s->aGammaTable[a] = NULL;
+ }
+ }
+ }
+}
+
+static SANE_Int
+gamma_create (TScanner * s, double gamma)
+{
+ SANE_Int rst = ERROR; /* by default */
+
+ DBG (DBG_FNC, "> gamma_create(*s)\n");
+
+ if (s != NULL)
+ {
+ SANE_Int a;
+ double value, c;
+
+ /* default result */
+ rst = OK;
+
+ /* destroy previus gamma tables */
+ gamma_free (s);
+
+ /* check gamma value */
+ if (gamma < 0)
+ gamma = GAMMA_DEFAULT;
+
+ /* allocate space for 16 bit gamma tables */
+ for (a = CL_RED; a <= CL_BLUE; a++)
+ {
+ s->aGammaTable[a] = malloc (65536 * sizeof (SANE_Word));
+ if (s->aGammaTable[a] == NULL)
+ {
+ rst = ERROR;
+ break;
+ }
+ }
+
+ if (rst == OK)
+ {
+ /* fill tables */
+ for (a = 0; a < 65536; a++)
+ {
+ value = (a / (65536. - 1));
+ value = pow (value, (1. / gamma));
+ value = value * (65536. - 1);
+
+ c = (SANE_Int) value;
+ if (c > (65536. - 1))
+ c = (65536. - 1);
+ else if (c < 0)
+ c = 0;
+
+ s->aGammaTable[CL_RED][a] = c;
+ s->aGammaTable[CL_GREEN][a] = c;
+ s->aGammaTable[CL_BLUE][a] = c;
+ }
+ }
+ else
+ gamma_free (s);
+ }
+
+ return rst;
+}
+
+static void
+gamma_apply (TScanner * s, SANE_Byte * buffer, SANE_Int size, SANE_Int depth)
+{
+ if ((s != NULL) && (buffer != NULL))
+ {
+ SANE_Int c;
+ SANE_Int dot_size = 3 * ((depth > 8) ? 2 : 1);
+ SANE_Byte *pColor = buffer;
+ USHORT *sColor = (void *) buffer;
+
+ if ((s->aGammaTable[CL_RED] != NULL)
+ && (s->aGammaTable[CL_GREEN] != NULL)
+ && (s->aGammaTable[CL_BLUE] != NULL))
+ {
+ for (c = 0; c < size / dot_size; c++)
+ {
+ if (depth > 8)
+ {
+ *sColor = s->aGammaTable[CL_RED][*sColor];
+ *(sColor + 1) = s->aGammaTable[CL_GREEN][*(sColor + 1)];
+ *(sColor + 2) = s->aGammaTable[CL_BLUE][*(sColor + 2)];
+ sColor += 3;
+ }
+ else
+ {
+ /* 8 bits gamma */
+ *pColor =
+ (s->aGammaTable[CL_RED][*pColor * 256] >> 8) & 0xff;
+ *(pColor + 1) =
+ (s->
+ aGammaTable[CL_GREEN][*(pColor + 1) * 256] >> 8) & 0xff;
+ *(pColor + 2) =
+ (s->
+ aGammaTable[CL_BLUE][*(pColor + 2) * 256] >> 8) & 0xff;
+ pColor += 3;
+ }
+ }
+ }
+ }
+}
+
+static SANE_Int
+Get_Model (SANE_String model)
+{
+ SANE_Int rst;
+
+ if (strcmp (model, "HP3800") == 0)
+ rst = HP3800;
+ else if (strcmp (model, "HPG2710") == 0)
+ rst = HPG2710;
+ else if (strcmp (model, "HP3970") == 0)
+ rst = HP3970;
+ else if (strcmp (model, "HP4070") == 0)
+ rst = HP4070;
+ else if (strcmp (model, "HP4370") == 0)
+ rst = HP4370;
+ else if (strcmp (model, "HPG3010") == 0)
+ rst = HPG3010;
+ else if (strcmp (model, "HPG3110") == 0)
+ rst = HPG3110;
+ else if (strcmp (model, "UA4900") == 0)
+ rst = UA4900;
+ else if (strcmp (model, "BQ5550") == 0)
+ rst = BQ5550;
+ else
+ rst = HP3970; /* default */
+
+ return rst;
+}
+
+static SANE_Int
+Get_Source (SANE_String source)
+{
+ SANE_Int rst;
+
+ if (strcmp (source, SANE_I18N ("Flatbed")) == 0)
+ rst = ST_NORMAL;
+ else if (strcmp (source, SANE_I18N ("Slide")) == 0)
+ rst = ST_TA;
+ else if (strcmp (source, SANE_I18N ("Negative")) == 0)
+ rst = ST_NEG;
+ else
+ rst = ST_NORMAL; /* default */
+
+ return rst;
+}
+
+static SANE_Int
+Get_Colormode (SANE_String colormode)
+{
+ SANE_Int rst;
+
+ if (strcmp (colormode, SANE_VALUE_SCAN_MODE_COLOR) == 0)
+ rst = CM_COLOR;
+ else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_GRAY) == 0)
+ rst = CM_GRAY;
+ else if (strcmp (colormode, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ rst = CM_LINEART;
+ else
+ rst = CM_COLOR; /* default */
+
+ return rst;
+}
+
+static SANE_Status
+Translate_coords (struct st_coords *coords)
+{
+ SANE_Int data;
+
+ DBG (DBG_FNC, "> Translate_coords(*coords)\n");
+
+ if ((coords->left < 0) || (coords->top < 0) ||
+ (coords->width < 0) || (coords->height < 0))
+ return SANE_STATUS_INVAL;
+
+ if (coords->width < coords->left)
+ {
+ data = coords->left;
+ coords->left = coords->width;
+ coords->width = data;
+ }
+
+ if (coords->height < coords->top)
+ {
+ data = coords->top;
+ coords->top = coords->height;
+ coords->height = data;
+ }
+
+ coords->width -= coords->left;
+ coords->height -= coords->top;
+
+ if (coords->width == 0)
+ coords->width++;
+
+ if (coords->height == 0)
+ coords->height++;
+
+ return SANE_STATUS_GOOD;
+}
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ DBG (DBG_FNC, "> max_string_size:\n");
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+
+ return max_size;
+}
+
+static void
+options_free (TScanner * scanner)
+{
+ /* frees all information contained in controls */
+
+ DBG (DBG_FNC, "> options_free\n");
+
+ if (scanner != NULL)
+ {
+ SANE_Int i;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+
+ /* free gamma tables */
+ gamma_free (scanner);
+
+ /* free lists */
+ if (scanner->list_resolutions != NULL)
+ free (scanner->list_resolutions);
+
+ if (scanner->list_depths != NULL)
+ free (scanner->list_depths);
+
+ if (scanner->list_sources != NULL)
+ free (scanner->list_sources);
+
+ if (scanner->list_colormodes != NULL)
+ free (scanner->list_colormodes);
+
+ if (scanner->list_models != NULL)
+ free (scanner->list_models);
+
+ /* free values in certain controls */
+ for (i = opt_begin; i < opt_count; i++)
+ {
+ pDesc = &scanner->aOptions[i];
+ pVal = &scanner->aValues[i];
+
+ if (pDesc->type == SANE_TYPE_STRING)
+ {
+ if (pVal->s != NULL)
+ free (pVal->s);
+ }
+ }
+ }
+}
+
+static void
+options_init (TScanner * scanner)
+{
+ /* initializes all controls */
+
+ DBG (DBG_FNC, "> options_init\n");
+
+ if (scanner != NULL)
+ {
+ SANE_Int i;
+ SANE_Option_Descriptor *pDesc;
+ TOptionValue *pVal;
+
+ /* set gamma */
+ gamma_create (scanner, 2.2);
+
+ /* color convertion */
+ scanner->cnv.colormode = -1;
+ scanner->cnv.negative = FALSE;
+ scanner->cnv.threshold = 40;
+ scanner->cnv.real_depth = FALSE;
+ scanner->cnv.depth = -1;
+
+ /* setting threshold */
+ scanner->rng_threshold.min = 0;
+ scanner->rng_threshold.max = 255;
+ scanner->rng_threshold.quant = 0;
+
+ /* setting gamma range (16 bits depth) */
+ scanner->rng_gamma.min = 0;
+ scanner->rng_gamma.max = 65535;
+ scanner->rng_gamma.quant = 0;
+
+ /* setting default horizontal constrain in milimeters */
+ scanner->rng_horizontal.min = 0;
+ scanner->rng_horizontal.max = 220;
+ scanner->rng_horizontal.quant = 1;
+
+ /* setting default vertical constrain in milimeters */
+ scanner->rng_vertical.min = 0;
+ scanner->rng_vertical.max = 300;
+ scanner->rng_vertical.quant = 1;
+
+ /* allocate option lists */
+ bknd_info (scanner);
+ bknd_colormodes (scanner, RTS_Debug->dev_model);
+ bknd_depths (scanner, RTS_Debug->dev_model);
+ bknd_models (scanner);
+ bknd_resolutions (scanner, RTS_Debug->dev_model);
+ bknd_sources (scanner, RTS_Debug->dev_model);
+
+ /* By default preview scan */
+ scanner->ScanParams.scantype = ST_NORMAL;
+ scanner->ScanParams.colormode = CM_COLOR;
+ scanner->ScanParams.resolution_x = 75;
+ scanner->ScanParams.resolution_y = 75;
+ scanner->ScanParams.coords.left = 0;
+ scanner->ScanParams.coords.top = 0;
+ scanner->ScanParams.coords.width = 220;
+ scanner->ScanParams.coords.height = 300;
+ scanner->ScanParams.depth = 8;
+ scanner->ScanParams.channel = 0;
+
+ for (i = opt_begin; i < opt_count; i++)
+ {
+ pDesc = &scanner->aOptions[i];
+ pVal = &scanner->aValues[i];
+
+ /* defaults */
+ pDesc->name = "";
+ pDesc->title = "";
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = 0;
+
+ switch (i)
+ {
+ case opt_begin:
+ pDesc->title = SANE_TITLE_NUM_OPTIONS;
+ pDesc->desc = SANE_DESC_NUM_OPTIONS;
+ pDesc->cap = SANE_CAP_SOFT_DETECT;
+ pVal->w = (SANE_Word) opt_count;
+ break;
+
+ case grp_geometry:
+ pDesc->name = SANE_NAME_GEOMETRY;
+ pDesc->title = SANE_TITLE_GEOMETRY;
+ pDesc->desc = SANE_DESC_GEOMETRY;
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_tlx:
+ pDesc->name = SANE_NAME_SCAN_TL_X;
+ pDesc->title = SANE_TITLE_SCAN_TL_X;
+ pDesc->desc = SANE_DESC_SCAN_TL_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_horizontal;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 0;
+ break;
+
+ case opt_tly:
+ pDesc->name = SANE_NAME_SCAN_TL_Y;
+ pDesc->title = SANE_TITLE_SCAN_TL_Y;
+ pDesc->desc = SANE_DESC_SCAN_TL_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_vertical;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = 0;
+ break;
+
+ case opt_brx:
+ pDesc->name = SANE_NAME_SCAN_BR_X;
+ pDesc->title = SANE_TITLE_SCAN_BR_X;
+ pDesc->desc = SANE_DESC_SCAN_BR_X;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_horizontal;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->rng_horizontal.max;
+ break;
+
+ case opt_bry:
+ pDesc->name = SANE_NAME_SCAN_BR_Y;
+ pDesc->title = SANE_TITLE_SCAN_BR_Y;
+ pDesc->desc = SANE_DESC_SCAN_BR_Y;
+ pDesc->unit = SANE_UNIT_MM;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_vertical;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->rng_vertical.max;
+ break;
+
+ case opt_resolution:
+ pDesc->name = SANE_NAME_SCAN_RESOLUTION;
+ pDesc->title = SANE_TITLE_SCAN_RESOLUTION;
+ pDesc->desc = SANE_DESC_SCAN_RESOLUTION;
+ pDesc->unit = SANE_UNIT_DPI;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = scanner->list_resolutions;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->list_resolutions[1];
+ break;
+
+ case opt_gamma_red:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_R;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_R;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_R;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_RED];
+ break;
+
+ case opt_gamma_green:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_G;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_G;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_G;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_GREEN];
+ break;
+
+ case opt_gamma_blue:
+ pDesc->name = SANE_NAME_GAMMA_VECTOR_B;
+ pDesc->title = SANE_TITLE_GAMMA_VECTOR_B;
+ pDesc->desc = SANE_DESC_GAMMA_VECTOR_B;
+ pDesc->size = scanner->rng_gamma.max * sizeof (SANE_Word);
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_gamma;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->wa = scanner->aGammaTable[CL_BLUE];
+ break;
+
+ case opt_scantype:
+ pDesc->name = SANE_NAME_SCAN_SOURCE;
+ pDesc->title = SANE_TITLE_SCAN_SOURCE;
+ pDesc->desc = SANE_DESC_SCAN_SOURCE;
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_sources);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_sources;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_sources[0]);
+ break;
+
+ case opt_colormode:
+ pDesc->name = SANE_NAME_SCAN_MODE;
+ pDesc->title = SANE_TITLE_SCAN_MODE;
+ pDesc->desc = SANE_DESC_SCAN_MODE;
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_colormodes);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_colormodes;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_colormodes[0]);
+ break;
+
+ case opt_depth:
+ pDesc->name = SANE_NAME_BIT_DEPTH;
+ pDesc->title = SANE_TITLE_BIT_DEPTH;
+ pDesc->desc = SANE_DESC_BIT_DEPTH;
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_BIT;
+ pDesc->constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ pDesc->constraint.word_list = scanner->list_depths;
+ pDesc->cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ pVal->w = scanner->list_depths[1];
+ break;
+
+ case opt_threshold:
+ pDesc->name = SANE_NAME_THRESHOLD;
+ pDesc->title = SANE_TITLE_THRESHOLD;
+ pDesc->desc = SANE_DESC_THRESHOLD;
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_RANGE;
+ pDesc->constraint.range = &scanner->rng_threshold;
+ pDesc->cap |=
+ SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_INACTIVE;
+ pVal->w = 0x80;
+ break;
+
+ /* debugging options */
+ case grp_debug:
+ pDesc->name = "grp_debug";
+ pDesc->title = SANE_I18N ("Debugging Options");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = SANE_CAP_ADVANCED;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_model:
+ pDesc->name = "opt_model";
+ pDesc->title = SANE_I18N ("Scanner model");
+ pDesc->desc =
+ SANE_I18N
+ ("Allows to test device behaviour with other supported models");
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->size = max_string_size (scanner->list_models);
+ pDesc->constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ pDesc->constraint.string_list = scanner->list_models;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT |
+ SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (scanner->list_models[0]);
+ break;
+
+ case opt_negative:
+ pDesc->name = "opt_negative";
+ pDesc->title = SANE_I18N ("Negative");
+ pDesc->desc = SANE_I18N ("Image colours will be inverted");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nogamma:
+ pDesc->name = "opt_nogamma";
+ pDesc->title = SANE_I18N ("Disable gamma correction");
+ pDesc->desc = SANE_I18N ("Gamma correction will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nowshading:
+ pDesc->name = "opt_nowshading";
+ pDesc->title = SANE_I18N ("Disable white shading correction");
+ pDesc->desc =
+ SANE_I18N ("White shading correction will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_nowarmup:
+ pDesc->name = "opt_nowarmup";
+ pDesc->title = SANE_I18N ("Skip warmup process");
+ pDesc->desc = SANE_I18N ("Warmup process will be disabled");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_realdepth:
+ pDesc->name = "opt_realdepth";
+ pDesc->title = SANE_I18N ("Force real depth");
+ pDesc->desc =
+ SANE_I18N
+ ("If gamma is enabled, scans are always made in 16 bits depth to improve image quality and then converted to the selected depth. This option avoids depth emulation.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_emulategray:
+ pDesc->name = "opt_emulategray";
+ pDesc->title = SANE_I18N ("Emulate Grayscale");
+ pDesc->desc =
+ SANE_I18N
+ ("If enabled, image will be scanned in color mode and then converted to grayscale by software. This may improve image quality in some circumstances.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_dbgimages:
+ pDesc->name = "opt_dbgimages";
+ pDesc->title = SANE_I18N ("Save debugging images");
+ pDesc->desc =
+ SANE_I18N
+ ("If enabled, some images involved in scanner processing are saved to analyze them.");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pDesc->cap =
+ SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT |
+ SANE_CAP_SOFT_SELECT;
+ pVal->w = SANE_FALSE;
+ break;
+
+ case opt_reset:
+ pDesc->name = "opt_reset";
+ pDesc->title = SANE_I18N ("Reset chipset");
+ pDesc->desc = SANE_I18N ("Resets chipset data");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.string_list = 0;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT;
+ pVal->w = 0;
+ break;
+
+ /* device information */
+ case grp_info:
+ pDesc->name = "grp_info";
+ pDesc->title = SANE_I18N ("Information");
+ pDesc->desc = "";
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_chipname:
+ pDesc->name = "opt_chipname";
+ pDesc->title = SANE_I18N ("Chipset name");
+ pDesc->desc = SANE_I18N ("Shows chipset name used in device.");
+ pDesc->type = SANE_TYPE_STRING;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->s = strdup (SANE_I18N ("Unknown"));
+ pDesc->size = strlen(pVal->s) + 1;
+ break;
+
+ case opt_chipid:
+ pDesc->name = "opt_chipid";
+ pDesc->title = SANE_I18N ("Chipset ID");
+ pDesc->desc = SANE_I18N ("Shows the chipset ID");
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->w = -1;
+ break;
+
+ case opt_scancount:
+ pDesc->name = "opt_scancount";
+ pDesc->title = SANE_I18N ("Scan counter");
+ pDesc->desc =
+ SANE_I18N ("Shows the number of scans made by scanner");
+ pDesc->type = SANE_TYPE_INT;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_DETECT;
+ pVal->w = -1;
+ break;
+
+ case opt_infoupdate:
+ pDesc->name = "opt_infoupdate";
+ pDesc->title = SANE_I18N ("Update information");
+ pDesc->desc = SANE_I18N ("Updates information about device");
+ pDesc->type = SANE_TYPE_BUTTON;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.string_list = 0;
+ pDesc->cap = SANE_CAP_ADVANCED | SANE_CAP_SOFT_SELECT;
+ pVal->w = 0;
+ break;
+
+ /* buttons support */
+ case grp_sensors:
+ pDesc->name = SANE_NAME_SENSORS;
+ pDesc->title = SANE_TITLE_SENSORS;
+ pDesc->desc = SANE_DESC_SENSORS;
+ pDesc->type = SANE_TYPE_GROUP;
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = 0;
+ pDesc->cap = 0;
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pDesc->constraint.range = 0;
+ pVal->w = 0;
+ break;
+
+ case opt_button_0:
+ case opt_button_1:
+ case opt_button_2:
+ case opt_button_3:
+ case opt_button_4:
+ case opt_button_5:
+ {
+ char name[12];
+ char title[128];
+
+ sprintf (name, "button %d", i - opt_button_0);
+ sprintf (title, "Scanner button %d", i - opt_button_0);
+ pDesc->name = strdup (name);
+ pDesc->title = strdup (title);
+ pDesc->desc =
+ SANE_I18N
+ ("This option reflects a front panel scanner button");
+ pDesc->type = SANE_TYPE_BOOL;
+ pDesc->cap = SANE_CAP_SOFT_DETECT | SANE_CAP_ADVANCED;
+
+ if (i - opt_button_0 >= Buttons_Count (device))
+ pDesc->cap |= SANE_CAP_INACTIVE;
+
+ pDesc->unit = SANE_UNIT_NONE;
+ pDesc->size = sizeof (SANE_Word);
+ pDesc->constraint_type = SANE_CONSTRAINT_NONE;
+ pVal->w = SANE_FALSE;
+ }
+ break;
+ }
+ }
+ }
+}
+
+static SANE_Int
+_ReportDevice (TScannerModel * pModel, const char *pszDeviceName)
+{
+ SANE_Int rst = ERROR;
+ TDevListEntry *pNew, *pDev;
+
+ DBG (DBG_FNC, "> _ReportDevice:\n");
+
+ pNew = malloc (sizeof (TDevListEntry));
+ if (pNew != NULL)
+ {
+ rst = OK;
+
+ /* add new element to the end of the list */
+ if (_pFirstSaneDev != NULL)
+ {
+ /* Add at the end of existing list */
+ for (pDev = _pFirstSaneDev; pDev->pNext; pDev = pDev->pNext);
+
+ pDev->pNext = pNew;
+ }
+ else
+ _pFirstSaneDev = pNew;
+
+ /* fill in new element */
+ pNew->pNext = NULL;
+ pNew->devname = (char *) strdup (pszDeviceName);
+ pNew->dev.name = pNew->devname;
+ pNew->dev.vendor = pModel->pszVendor;
+ pNew->dev.model = pModel->pszName;
+ pNew->dev.type = SANE_I18N ("flatbed scanner");
+
+ iNumSaneDev++;
+ }
+
+ return rst;
+}
+
+static SANE_Status
+attach_one_device (SANE_String_Const devname)
+{
+ static TScannerModel sModel;
+
+ DBG (DBG_FNC, "> attach_one_device(devname=%s)\n", devname);
+
+ switch (GetUSB_device_model (devname))
+ {
+ case HP3800:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 3800");
+ break;
+ case HPG2710:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G2710");
+ break;
+ case HP3970:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 3970");
+ break;
+ case HP4070:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 4070 Photosmart");
+ break;
+ case HP4370:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet 4370");
+ break;
+ case HPG3010:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G3010");
+ break;
+ case HPG3110:
+ sModel.pszVendor = (char *) strdup ("Hewlett-Packard");
+ sModel.pszName = (char *) strdup ("Scanjet G3110");
+ break;
+ case UA4900:
+ sModel.pszVendor = (char *) strdup ("UMAX");
+ sModel.pszName = (char *) strdup ("Astra 4900");
+ break;
+ case BQ5550:
+ sModel.pszVendor = (char *) strdup ("BenQ");
+ sModel.pszName = (char *) strdup ("5550");
+ break;
+ default:
+ sModel.pszVendor = (char *) strdup ("Unknown");
+ sModel.pszName = (char *) strdup ("RTS8822 chipset based");
+ break;
+ }
+
+ _ReportDevice (&sModel, devname);
+
+ return SANE_STATUS_GOOD;
+}
+
+/* Sane default functions */
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ FILE *conf_fp; /* Config file stream */
+ SANE_Char line[PATH_MAX];
+ SANE_Char *str = NULL;
+ SANE_String_Const proper_str;
+ SANE_Int nline = 0;
+
+ /* Initialize debug */
+ DBG_INIT ();
+
+ DBG (DBG_FNC, "> sane_init\n");
+
+ /* silence gcc */
+ authorize = authorize;
+
+ /* Initialize usb */
+ sanei_usb_init ();
+
+ /* Parse config file */
+ conf_fp = sanei_config_open (HP3900_CONFIG_FILE);
+ if (conf_fp)
+ {
+ while (sanei_config_read (line, sizeof (line), conf_fp))
+ {
+ nline++;
+ if (str)
+ free (str);
+
+ proper_str = sanei_config_get_string (line, &str);
+
+ /* Discards white lines and comments */
+ if ((str != NULL) && (proper_str != line) && (str[0] != '#'))
+ {
+ /* If line's not blank or a comment, then it's the device
+ * filename or a usb directive. */
+ sanei_usb_attach_matching_devices (line, attach_one_device);
+ }
+ }
+ fclose (conf_fp);
+ }
+ else
+ {
+ /* default */
+ DBG (DBG_VRB, "- %s not found. Looking for hardcoded usb ids ...\n",
+ HP3900_CONFIG_FILE);
+
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2605", attach_one_device); /* HP3800 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2805", attach_one_device); /* HPG2710 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2305", attach_one_device); /* HP3970 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x2405", attach_one_device); /* HP4070 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4105", attach_one_device); /* HP4370 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4205", attach_one_device); /* HPG3010 */
+ sanei_usb_attach_matching_devices ("usb 0x03f0 0x4305", attach_one_device); /* HPG3110 */
+ sanei_usb_attach_matching_devices ("usb 0x06dc 0x0020", attach_one_device); /* UA4900 */
+ sanei_usb_attach_matching_devices ("usb 0x04a5 0x2211", attach_one_device); /* BQ5550 */
+ }
+
+ /* Return backend version */
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, 0);
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Status rst = SANE_STATUS_GOOD;
+
+ local_only = local_only;
+
+ if (_pSaneDevList)
+ free (_pSaneDevList);
+
+ _pSaneDevList = malloc (sizeof (*_pSaneDevList) * (iNumSaneDev + 1));
+ if (_pSaneDevList != NULL)
+ {
+ TDevListEntry *pDev;
+ SANE_Int i = 0;
+
+ for (pDev = _pFirstSaneDev; pDev; pDev = pDev->pNext)
+ _pSaneDevList[i++] = &pDev->dev;
+
+ _pSaneDevList[i++] = 0; /* last entry is 0 */
+ *device_list = _pSaneDevList;
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+
+ DBG (DBG_FNC, "> sane_get_devices: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_open (SANE_String_Const name, SANE_Handle * h)
+{
+ TScanner *s;
+ SANE_Status rst;
+
+ /* check the name */
+ if (strlen (name) == 0)
+ /* default to first available device */
+ name = _pFirstSaneDev->dev.name;
+
+ /* allocate space for RTS environment */
+ device = RTS_Alloc ();
+ if (device != NULL)
+ {
+ /* Open device */
+ rst = sanei_usb_open (name, &device->usb_handle);
+ if (rst == SANE_STATUS_GOOD)
+ {
+ /* Allocating memory for device */
+ s = malloc (sizeof (TScanner));
+ if (s != NULL)
+ {
+ memset (s, 0, sizeof (TScanner));
+
+ /* Initializing RTS */
+ if (Init_Vars () == OK)
+ {
+ SANE_Int vendor, product;
+
+ /* Setting device model */
+ if (sanei_usb_get_vendor_product
+ (device->usb_handle, &vendor,
+ &product) == SANE_STATUS_GOOD)
+ s->model = Device_get (product, vendor);
+ else
+ s->model = HP3970;
+
+ set_ScannerModel (s->model, product, vendor);
+
+ /* Initialize device */
+ if (RTS_Scanner_Init (device) == OK)
+ {
+ /* silencing unused functions */
+ Silent_Compile ();
+
+ /* initialize backend options */
+ options_init (s);
+ *h = s;
+
+ /* everything went ok */
+ rst = SANE_STATUS_GOOD;
+ }
+ else
+ {
+ free ((void *) s);
+ rst = SANE_STATUS_INVAL;
+ }
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+ }
+ }
+ else
+ rst = SANE_STATUS_NO_MEM;
+
+ DBG (DBG_FNC, "> sane_open(name=%s): %i\n", name, rst);
+
+ return rst;
+}
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle h, SANE_Int n)
+{
+ SANE_Option_Descriptor *rst = NULL;
+
+ if ((n >= opt_begin) && (n < opt_count))
+ {
+ TScanner *s = (TScanner *) h;
+ rst = &s->aOptions[n];
+ }
+
+ DBG (DBG_FNC, "> SANE_Option_Descriptor(handle, n=%i): %i\n", n,
+ (rst == NULL) ? -1 : 0);
+
+ return rst;
+}
+
+static SANE_Status
+option_get (TScanner * scanner, SANE_Int optid, void *result)
+{
+ /* This function returns value contained in selected option */
+
+ DBG (DBG_FNC, "> option_get(optid=%i)\n", optid);
+
+ if ((scanner != NULL) && (result != NULL))
+ {
+ switch (optid)
+ {
+ /* SANE_Word */
+ case opt_begin: /* null */
+ case opt_reset: /* null */
+ case opt_negative:
+ case opt_nogamma:
+ case opt_nowshading:
+ case opt_emulategray:
+ case opt_dbgimages:
+ case opt_nowarmup:
+ case opt_realdepth:
+ case opt_depth:
+ case opt_resolution:
+ case opt_threshold:
+ case opt_brx:
+ case opt_tlx:
+ case opt_bry:
+ case opt_tly:
+ *(SANE_Word *) result = scanner->aValues[optid].w;
+ break;
+
+ /* SANE_Int */
+ case opt_chipid:
+ case opt_scancount:
+ *(SANE_Int *) result = scanner->aValues[optid].w;
+ break;
+
+ /* SANE_Word array */
+ case opt_gamma_red:
+ case opt_gamma_green:
+ case opt_gamma_blue:
+ memcpy (result, scanner->aValues[optid].wa,
+ scanner->aOptions[optid].size);
+ break;
+
+ /* String */
+ case opt_colormode:
+ case opt_scantype:
+ case opt_model:
+ case opt_chipname:
+ strncpy (result, scanner->aValues[optid].s, scanner->aOptions[optid].size);
+ ((char*)result)[scanner->aOptions[optid].size-1] = '\0';
+
+ break;
+
+ /* scanner buttons */
+ case opt_button_0:
+ get_button_status (scanner);
+ case opt_button_1:
+ case opt_button_2:
+ case opt_button_3:
+ case opt_button_4:
+ case opt_button_5:
+ /* copy the button state */
+ *(SANE_Word *) result = scanner->aValues[optid].w;
+ /* clear the button state */
+ scanner->aValues[optid].w = SANE_FALSE;
+ break;
+ }
+ }
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+option_set (TScanner * scanner, SANE_Int optid, void *value, SANE_Int * pInfo)
+{
+ SANE_Status rst;
+
+ DBG (DBG_FNC, "> option_set(optid=%i)\n", optid);
+
+ rst = SANE_STATUS_INVAL;
+
+ if (scanner != NULL)
+ {
+ if (scanner->fScanning == FALSE)
+ {
+ SANE_Int info = 0;
+
+ rst = SANE_STATUS_GOOD;
+
+ switch (optid)
+ {
+ case opt_brx:
+ case opt_tlx:
+ case opt_bry:
+ case opt_tly:
+ case opt_depth:
+ case opt_nogamma:
+ case opt_nowshading:
+ case opt_nowarmup:
+ case opt_negative:
+ case opt_emulategray:
+ case opt_dbgimages:
+ case opt_threshold:
+ case opt_resolution:
+ info |= SANE_INFO_RELOAD_PARAMS;
+ scanner->aValues[optid].w = *(SANE_Word *) value;
+ break;
+
+ case opt_gamma_red:
+ case opt_gamma_green:
+ case opt_gamma_blue:
+ memcpy (scanner->aValues[optid].wa, value,
+ scanner->aOptions[optid].size);
+ break;
+
+ case opt_scantype:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ struct st_coords *coords;
+ SANE_Int source;
+
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+
+ scanner->aValues[optid].s = strdup (value);
+
+ source = Get_Source (scanner->aValues[opt_scantype].s);
+ coords = Constrains_Get (device, source);
+ if (coords != NULL)
+ {
+ bknd_constrains (scanner, source, 0);
+ bknd_constrains (scanner, source, 1);
+ scanner->aValues[opt_tlx].w = 0;
+ scanner->aValues[opt_tly].w = 0;
+ scanner->aValues[opt_brx].w = coords->width;
+ scanner->aValues[opt_bry].w = coords->height;
+ }
+
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_colormode:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+ scanner->aValues[optid].s = strdup (value);
+ if (Get_Colormode (scanner->aValues[optid].s) == CM_LINEART)
+ scanner->aOptions[opt_threshold].cap &=
+ ~SANE_CAP_INACTIVE;
+ else
+ scanner->aOptions[opt_threshold].cap |= SANE_CAP_INACTIVE;
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_model:
+ if (strcmp (scanner->aValues[optid].s, value) != 0)
+ {
+ SANE_Int model;
+
+ if (scanner->aValues[optid].s)
+ free (scanner->aValues[optid].s);
+ scanner->aValues[optid].s = strdup (value);
+
+ model = Get_Model (scanner->aValues[optid].s);
+ if (model != RTS_Debug->dev_model)
+ {
+ SANE_Int source;
+ struct st_coords *coords;
+
+ /* free configuration of last model */
+ Free_Config (device);
+
+ /* set new model */
+ RTS_Debug->dev_model = model;
+
+ /* and load configuration of current model */
+ Load_Config (device);
+
+ /* update options according to selected device */
+ bknd_info (scanner);
+ bknd_colormodes (scanner, model);
+ bknd_depths (scanner, model);
+ bknd_resolutions (scanner, model);
+ bknd_sources (scanner, model);
+
+ /* updating lists */
+ scanner->aOptions[opt_colormode].size =
+ max_string_size (scanner->list_colormodes);
+ scanner->aOptions[opt_colormode].constraint.
+ string_list = scanner->list_colormodes;
+ scanner->aOptions[opt_depth].constraint.word_list =
+ scanner->list_depths;
+ scanner->aOptions[opt_resolution].constraint.word_list =
+ scanner->list_resolutions;
+ scanner->aOptions[opt_scantype].size =
+ max_string_size (scanner->list_sources);
+ scanner->aOptions[opt_scantype].constraint.string_list =
+ scanner->list_sources;
+
+ /* default values */
+ if (scanner->aValues[opt_colormode].s != NULL)
+ free (scanner->aValues[opt_colormode].s);
+
+ if (scanner->aValues[opt_scantype].s != NULL)
+ free (scanner->aValues[opt_scantype].s);
+
+ scanner->aValues[opt_colormode].s =
+ strdup (scanner->list_colormodes[0]);
+ scanner->aValues[opt_scantype].s =
+ strdup (scanner->list_sources[0]);
+ scanner->aValues[opt_resolution].w =
+ scanner->list_resolutions[1];
+ scanner->aValues[opt_depth].w = scanner->list_depths[1];
+
+ source = Get_Source (scanner->aValues[opt_scantype].s);
+ coords = Constrains_Get (device, source);
+ if (coords != NULL)
+ {
+ bknd_constrains (scanner, source, 0);
+ bknd_constrains (scanner, source, 1);
+ scanner->aValues[opt_tlx].w = 0;
+ scanner->aValues[opt_tly].w = 0;
+ scanner->aValues[opt_brx].w = coords->width;
+ scanner->aValues[opt_bry].w = coords->height;
+ }
+ }
+
+ info |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ }
+ break;
+
+ case opt_reset:
+ Chipset_Reset (device);
+ break;
+
+ case opt_realdepth:
+ scanner->aValues[optid].w =
+ (scanner->cnv.real_depth == TRUE) ? SANE_TRUE : SANE_FALSE;
+ break;
+
+ case opt_infoupdate:
+ if (bknd_info (scanner) == SANE_STATUS_GOOD)
+ info |= SANE_INFO_RELOAD_OPTIONS;
+ break;
+
+ default:
+ rst = SANE_STATUS_INVAL;
+ break;
+ }
+
+ if (pInfo != NULL)
+ *pInfo = info;
+ }
+ }
+
+ return rst;
+}
+
+SANE_Status
+sane_control_option (SANE_Handle h, SANE_Int n, SANE_Action Action,
+ void *pVal, SANE_Int * pInfo)
+{
+ TScanner *scanner;
+ SANE_Status rst;
+
+ DBG (DBG_FNC, "> sane_control_option\n");
+
+ scanner = (TScanner *) h;
+
+ switch (Action)
+ {
+ case SANE_ACTION_GET_VALUE:
+ rst = option_get (scanner, n, pVal);
+ break;
+
+ case SANE_ACTION_SET_VALUE:
+ rst = option_set (scanner, n, pVal, pInfo);
+ break;
+
+ case SANE_ACTION_SET_AUTO:
+ rst = SANE_STATUS_UNSUPPORTED;
+ break;
+
+ default:
+ rst = SANE_STATUS_INVAL;
+ break;
+ }
+
+ return rst;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle h, SANE_Parameters * p)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+ TScanner *s = (TScanner *) h;
+
+ DBG (DBG_FNC, "+ sane_get_parameters:");
+
+ if (s != NULL)
+ {
+ struct st_coords coords;
+ SANE_Int res, source, depth, colormode, frameformat, bpl;
+
+ /* first do some checks */
+
+ /* colormode */
+ colormode = Get_Colormode (s->aValues[opt_colormode].s);
+
+ /* frameformat */
+ frameformat =
+ (colormode == CM_COLOR) ? SANE_FRAME_RGB : SANE_FRAME_GRAY;
+
+ /* depth */
+ depth = (colormode == CM_LINEART) ? 1 : s->aValues[opt_depth].w;
+
+ /* scan type */
+ source = Get_Source (s->aValues[opt_scantype].s);
+
+ /* resolution */
+ res = s->aValues[opt_resolution].w;
+
+ /* image coordinates in milimeters */
+ coords.left = s->aValues[opt_tlx].w;
+ coords.top = s->aValues[opt_tly].w;
+ coords.width = s->aValues[opt_brx].w;
+ coords.height = s->aValues[opt_bry].w;
+
+ /* validate coords */
+ if (Translate_coords (&coords) == SANE_STATUS_GOOD)
+ {
+ Set_Coordinates (source, res, &coords);
+
+ if (colormode != CM_LINEART)
+ {
+ bpl = coords.width * ((depth > 8) ? 2 : 1);
+ if (colormode == CM_COLOR)
+ bpl *= 3; /* three channels */
+ }
+ else
+ bpl = (coords.width + 7) / 8;
+
+ /* return the data */
+ p->format = frameformat;
+ p->last_frame = SANE_TRUE;
+ p->depth = depth;
+ p->lines = coords.height;
+ p->pixels_per_line = coords.width;
+ p->bytes_per_line = bpl;
+
+ DBG (DBG_FNC, " -> Depth : %i\n", depth);
+ DBG (DBG_FNC, " -> Height: %i\n", coords.height);
+ DBG (DBG_FNC, " -> Width : %i\n", coords.width);
+ DBG (DBG_FNC, " -> BPL : %i\n", bpl);
+
+ rst = SANE_STATUS_GOOD;
+ }
+ }
+
+ DBG (DBG_FNC, "- sane_get_parameters: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_start (SANE_Handle h)
+{
+ SANE_Status rst = SANE_STATUS_INVAL;
+ TScanner *s;
+
+ DBG (DBG_FNC, "+ sane_start\n");
+
+ s = (TScanner *) h;
+ if (s != NULL)
+ {
+ struct st_coords coords;
+ SANE_Int res, source, colormode, depth, channel;
+
+ /* first do some checks */
+ /* Get Scan type */
+ source = Get_Source (s->aValues[opt_scantype].s);
+
+ /* Check if scanner supports slides and negatives in case selected source is tma */
+ if (!((source != ST_NORMAL) && (RTS_isTmaAttached (device) == FALSE)))
+ {
+ /* Get depth */
+ depth = s->aValues[opt_depth].w;
+
+ /* Get color mode */
+ colormode = Get_Colormode (s->aValues[opt_colormode].s);
+
+ /* Emulating certain color modes */
+ if (colormode == CM_LINEART)
+ {
+ /* emulate lineart */
+ s->cnv.colormode = CM_LINEART;
+ colormode = CM_GRAY;
+ depth = 8;
+ }
+ else if ((colormode == CM_GRAY)
+ && (s->aValues[opt_emulategray].w == SANE_TRUE))
+ {
+ /* emulate grayscale */
+ s->cnv.colormode = CM_GRAY;
+ colormode = CM_COLOR;
+ }
+ else
+ s->cnv.colormode = -1;
+
+ /* setting channel for colormodes different than CM_COLOR */
+ channel = (colormode != CM_COLOR) ? 1 : 0;
+
+ /* negative colors */
+ s->cnv.negative =
+ (s->aValues[opt_negative].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* Get threshold */
+ s->cnv.threshold = s->aValues[opt_threshold].w;
+
+ /* Get resolution */
+ res = s->aValues[opt_resolution].w;
+
+ /* set depth emulation */
+ if (s->cnv.colormode == CM_LINEART)
+ s->cnv.real_depth = TRUE;
+ else
+ s->cnv.real_depth =
+ (s->aValues[opt_realdepth].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* use gamma? */
+ RTS_Debug->EnableGamma =
+ (s->aValues[opt_nogamma].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* disable white shading correction? */
+ RTS_Debug->wshading =
+ (s->aValues[opt_nowshading].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* skip warmup process? */
+ RTS_Debug->warmup =
+ (s->aValues[opt_nowarmup].w == SANE_TRUE) ? FALSE : TRUE;
+
+ /* save debugging images? */
+ RTS_Debug->SaveCalibFile =
+ (s->aValues[opt_dbgimages].w == SANE_TRUE) ? TRUE : FALSE;
+
+ /* Get image coordinates in milimeters */
+ coords.left = s->aValues[opt_tlx].w;
+ coords.top = s->aValues[opt_tly].w;
+ coords.width = s->aValues[opt_brx].w;
+ coords.height = s->aValues[opt_bry].w;
+
+ /* Validate coords */
+ if (Translate_coords (&coords) == SANE_STATUS_GOOD)
+ {
+
+ /* Stop previusly started scan */
+ RTS_Scanner_StopScan (device, TRUE);
+
+ s->ScanParams.scantype = source;
+ s->ScanParams.colormode = colormode;
+ s->ScanParams.resolution_x = res;
+ s->ScanParams.resolution_y = res;
+ s->ScanParams.channel = channel;
+
+ memcpy (&s->ScanParams.coords, &coords,
+ sizeof (struct st_coords));
+ Set_Coordinates (source, res, &s->ScanParams.coords);
+
+ /* emulating depth? */
+ if ((s->cnv.real_depth == FALSE) && (depth < 16)
+ && (RTS_Debug->EnableGamma == TRUE))
+ {
+ /* In order to improve image quality, we will scan at 16bits if
+ we are using gamma correction */
+ s->cnv.depth = depth;
+ s->ScanParams.depth = 16;
+ }
+ else
+ {
+ s->ScanParams.depth = depth;
+ s->cnv.depth = -1;
+ }
+
+ /* set scanning parameters */
+ if (RTS_Scanner_SetParams (device, &s->ScanParams) == OK)
+ {
+ /* Start scanning process */
+ if (RTS_Scanner_StartScan (device) == OK)
+ {
+ /* Allocate buffer to read one line */
+ s->mylin = 0;
+ rst = img_buffers_alloc (s, bytesperline);
+ }
+ }
+ }
+ }
+ else
+ rst = SANE_STATUS_COVER_OPEN;
+ }
+
+ DBG (DBG_FNC, "- sane_start: %i\n", rst);
+
+ return rst;
+}
+
+SANE_Status
+sane_read (SANE_Handle h, SANE_Byte * buf, SANE_Int maxlen, SANE_Int * len)
+{
+ SANE_Status rst = SANE_STATUS_GOOD;
+ TScanner *s = (TScanner *) h;
+
+ DBG (DBG_FNC, "+ sane_read\n");
+
+ if ((s != NULL) && (buf != NULL) && (len != NULL))
+ {
+ /* nothing has been read at the moment */
+ *len = 0;
+
+ /* if we read all the lines return EOF */
+ if ((s->mylin == s->ScanParams.coords.height)
+ || (device->status->cancel == TRUE))
+ {
+ rst =
+ (device->status->cancel ==
+ TRUE) ? SANE_STATUS_CANCELLED : SANE_STATUS_EOF;
+
+ RTS_Scanner_StopScan (device, FALSE);
+ img_buffers_free (s);
+ }
+ else
+ {
+ SANE_Int emul_len, emul_maxlen;
+ SANE_Int thwidth, transferred, bufflength;
+ SANE_Byte *buffer, *pbuffer;
+
+ emul_len = 0;
+ if (s->cnv.depth != -1)
+ emul_maxlen = maxlen * (s->ScanParams.depth / s->cnv.depth);
+ else
+ emul_maxlen = maxlen;
+
+ /* if grayscale emulation is enabled check that retrieved data is multiple of three */
+ if (s->cnv.colormode == CM_GRAY)
+ {
+ SANE_Int chn_size, rest;
+
+ chn_size = (s->ScanParams.depth > 8) ? 2 : 1;
+ rest = emul_maxlen % (3 * chn_size);
+
+ if (rest != 0)
+ emul_maxlen -= rest;
+ }
+
+ /* this is important to keep lines alignment in lineart mode */
+ if (s->cnv.colormode == CM_LINEART)
+ emul_maxlen = s->ScanParams.coords.width;
+
+ /* if we are emulating depth, we scan at 16bit when frontend waits
+ for 8bit data. Next buffer will be used to retrieve data from
+ scanner prior to convert to 8 bits depth */
+ buffer = (SANE_Byte *) malloc (emul_maxlen * sizeof (SANE_Byte));
+
+ if (buffer != NULL)
+ {
+ pbuffer = buffer;
+
+ /* get bytes per line */
+ if (s->ScanParams.colormode != CM_LINEART)
+ {
+ thwidth =
+ s->ScanParams.coords.width *
+ ((s->ScanParams.depth > 8) ? 2 : 1);
+
+ if (s->ScanParams.colormode == CM_COLOR)
+ thwidth *= 3; /* three channels */
+ }
+ else
+ thwidth = (s->ScanParams.coords.width + 7) / 8;
+
+ /* read as many lines the buffer may contain and while there are lines to be read */
+ while ((emul_len < emul_maxlen)
+ && (s->mylin < s->ScanParams.coords.height))
+ {
+ /* Is there any data waiting for being passed ? */
+ if (s->rest_amount != 0)
+ {
+ /* copy to buffer as many bytes as we can */
+ bufflength =
+ min (emul_maxlen - emul_len, s->rest_amount);
+ memcpy (pbuffer, s->rest, bufflength);
+ emul_len += bufflength;
+ pbuffer += bufflength;
+ s->rest_amount -= bufflength;
+ if (s->rest_amount == 0)
+ s->mylin++;
+ }
+ else
+ {
+ /* read from scanner up to one line */
+ if (Read_Image
+ (device, bytesperline, s->image,
+ &transferred) != OK)
+ {
+ /* error, exit function */
+ rst = SANE_STATUS_EOF;
+ break;
+ }
+
+ /* is there any data? */
+ if (transferred != 0)
+ {
+ /* copy to buffer as many bytes as we can */
+ bufflength = min (emul_maxlen - emul_len, thwidth);
+
+ memcpy (pbuffer, s->image, bufflength);
+ emul_len += bufflength;
+ pbuffer += bufflength;
+
+ /* the rest will be copied to s->rest buffer */
+ if (bufflength < thwidth)
+ {
+ s->rest_amount = thwidth - bufflength;
+ memcpy (s->rest, s->image + bufflength,
+ s->rest_amount);
+ }
+ else
+ s->mylin++;
+ }
+ else
+ break;
+ }
+ } /* while */
+
+ /* process buffer before sending to frontend */
+ if ((emul_len > 0) && (rst != SANE_STATUS_EOF))
+ {
+ /* at this point ...
+ buffer : contains retrieved image
+ emul_len: contains size in bytes of retrieved image
+
+ after this code ...
+ buf : will contain postprocessed image
+ len : will contain size in bytes of postprocessed image */
+
+ /* apply gamma if neccesary */
+ if (RTS_Debug->EnableGamma == TRUE)
+ gamma_apply (s, buffer, emul_len, s->ScanParams.depth);
+
+ /* if we are scanning negatives, let's invert colors */
+ if (s->ScanParams.scantype == ST_NEG)
+ {
+ if (s->cnv.negative == FALSE)
+ Color_Negative (buffer, emul_len,
+ s->ScanParams.depth);
+ }
+ else if (s->cnv.negative != FALSE)
+ Color_Negative (buffer, emul_len, s->ScanParams.depth);
+
+ /* emulating grayscale ? */
+ if (s->cnv.colormode == CM_GRAY)
+ {
+ Color_to_Gray (buffer, emul_len, s->ScanParams.depth);
+ emul_len /= 3;
+ }
+
+ /* emulating depth */
+ if (s->cnv.depth != -1)
+ {
+ switch (s->cnv.depth)
+ {
+ /* case 1: treated separately as lineart */
+ /*case 12: in the future */
+ case 8:
+ Depth_16_to_8 (buffer, emul_len, buffer);
+ emul_len /= 2;
+ break;
+ }
+ }
+
+ /* lineart mode ? */
+ if (s->cnv.colormode == CM_LINEART)
+ {
+ /* I didn't see any scanner supporting lineart mode.
+ Windows drivers scan in grayscale and then convert image to lineart
+ so let's perform convertion */
+ SANE_Int rest = emul_len % 8;
+
+ Gray_to_Lineart (buffer, emul_len, s->cnv.threshold);
+ emul_len /= 8;
+ if (rest > 0)
+ emul_len++;
+ }
+
+ /* copy postprocessed image */
+ *len = emul_len;
+ memcpy (buf, buffer, *len);
+ }
+
+ free (buffer);
+ }
+ }
+ }
+ else
+ rst = SANE_STATUS_EOF;
+
+ DBG (DBG_FNC, "- sane_read: %s\n", sane_strstatus (rst));
+
+ return rst;
+}
+
+void
+sane_cancel (SANE_Handle h)
+{
+ DBG (DBG_FNC, "> sane_cancel\n");
+
+ /* silence gcc */
+ h = h;
+
+ device->status->cancel = TRUE;
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ DBG (DBG_FNC, "> sane_set_io_mode\n");
+
+ /* silence gcc */
+ handle = handle;
+ non_blocking = non_blocking;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ DBG (DBG_FNC, "> sane_get_select_fd\n");
+
+ /* silence gcc */
+ handle = handle;
+ fd = fd;
+
+ return SANE_STATUS_UNSUPPORTED;
+}
+
+void
+sane_close (SANE_Handle h)
+{
+ TScanner *scanner = (TScanner *) h;
+
+ DBG (DBG_FNC, "- sane_close...\n");
+
+ /* stop previus scans */
+ RTS_Scanner_StopScan (device, TRUE);
+
+ /* close usb */
+ sanei_usb_close (device->usb_handle);
+
+ /* free scanner internal variables */
+ RTS_Scanner_End (device);
+
+ /* free RTS enviroment */
+ RTS_Free (device);
+
+ /* free backend variables */
+ if (scanner != NULL)
+ {
+ options_free (scanner);
+
+ img_buffers_free (scanner);
+ }
+}
+
+void
+sane_exit (void)
+{
+ /* free device list memory */
+ if (_pSaneDevList)
+ {
+ TDevListEntry *pDev, *pNext;
+
+ for (pDev = _pFirstSaneDev; pDev; pDev = pNext)
+ {
+ pNext = pDev->pNext;
+ /* pDev->dev.name is the same pointer that pDev->devname */
+ free (pDev->devname);
+ free (pDev);
+ }
+
+ _pFirstSaneDev = NULL;
+ free (_pSaneDevList);
+ _pSaneDevList = NULL;
+ }
+}