summaryrefslogtreecommitdiff
path: root/backend/mustek_usb2.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/mustek_usb2.c')
-rw-r--r--backend/mustek_usb2.c2696
1 files changed, 2696 insertions, 0 deletions
diff --git a/backend/mustek_usb2.c b/backend/mustek_usb2.c
new file mode 100644
index 0000000..25b8464
--- /dev/null
+++ b/backend/mustek_usb2.c
@@ -0,0 +1,2696 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 2000-2005 Mustek.
+ Originally maintained by Mustek
+
+ Copyright (C) 2001-2005 by Henning Meier-Geinitz.
+
+ 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 a SANE backend for the Mustek BearPaw 2448 TA Pro
+ and similar USB2 scanners. */
+
+#define BUILD 10
+
+#include "../include/sane/config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "../include/sane/sane.h"
+#include "../include/sane/sanei.h"
+#include "../include/sane/saneopts.h"
+
+#define BACKEND_NAME mustek_usb2
+
+#include "../include/sane/sanei_backend.h"
+#include "mustek_usb2_high.c"
+
+#include "mustek_usb2.h"
+
+static SANE_Int num_devices;
+static const SANE_Device **devlist = 0;
+
+static const SANE_Range u8_range = {
+ 0, /* minimum */
+ 255, /* maximum */
+ 0 /* quantization */
+};
+static SANE_Range x_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (8.3 * MM_PER_INCH), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range y_range = {
+ SANE_FIX (0.0), /* minimum */
+ SANE_FIX (11.6 * MM_PER_INCH), /* maximum */
+ SANE_FIX (0.0) /* quantization */
+};
+
+static SANE_Range gamma_range = {
+ SANE_FIX (0.01), /* minimum */
+ SANE_FIX (5.0), /* maximum */
+ SANE_FIX (0.01) /* quantization */
+};
+static SANE_String_Const mode_list[] = {
+ SANE_I18N ("Color48"),
+ SANE_I18N ("Color24"),
+ SANE_I18N ("Gray16"),
+ SANE_I18N ("Gray8"),
+ SANE_VALUE_SCAN_MODE_LINEART,
+ 0
+};
+
+static SANE_String_Const negative_mode_list[] = {
+ SANE_I18N ("Color24"),
+ 0
+};
+
+static SANE_String_Const source_list[] = {
+ SANE_I18N ("Reflective"),
+ SANE_I18N ("Positive"),
+ SANE_I18N ("Negative"),
+ 0
+};
+static Scanner_Model mustek_A2nu2_model = {
+ "mustek-A2nu2", /* Name */
+ "Mustek", /* Device vendor string */
+
+ "BearPaw 2448TA Pro", /* Device model name */
+ "", /* Name of the firmware file */
+
+ {1200, 600, 300, 150, 75, 0}, /* possible resolutions */
+
+ SANE_FIX (0.0), /* Start of scan area in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in mm (y) */
+ SANE_FIX (8.3 * MM_PER_INCH), /* Size of scan area in mm (x) */
+ SANE_FIX (11.6 * MM_PER_INCH), /* Size of scan area in mm (y) */
+
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (x) */
+ SANE_FIX (0.0), /* Start of scan area in TA mode in mm (y) */
+ SANE_FIX (1.46 * MM_PER_INCH), /* Size of scan area in TA mode in mm (x) */
+ SANE_FIX (6.45 * MM_PER_INCH), /* Size of scan area in TA mode in mm (y) */
+
+ 0, /* Order of the CCD/CIS colors 0:RO_RGB 1:RO_BGR */
+ SANE_FIX (2.0), /* Default gamma value */
+
+ SANE_FALSE, /* Is this a CIS scanner? */
+ 0 /* Which flags are needed for this scanner? */
+ /* Setup and tested */
+};
+
+
+/* Forward declarations */
+
+static SANE_Bool GetDeviceStatus (void);
+static SANE_Bool PowerControl (SANE_Bool isLampOn, SANE_Bool isTaLampOn);
+static SANE_Bool CarriageHome (void);
+static SANE_Bool SetParameters (LPSETPARAMETERS pSetParameters);
+static SANE_Bool GetParameters (LPGETPARAMETERS pGetParameters);
+static SANE_Bool StartScan (void);
+static SANE_Bool ReadScannedData (LPIMAGEROWS pImageRows);
+static SANE_Bool StopScan (void);
+static SANE_Bool IsTAConnected (void);
+static void AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine);
+static size_t max_string_size (const SANE_String_Const strings[]);
+static SANE_Status calc_parameters (Mustek_Scanner * s);
+#ifdef SANE_UNUSED
+static SANE_Bool GetGammaInfo (LPGAMMAINFO pGamaInfo);
+static SANE_Bool GetKeyStatus (SANE_Byte * pKey);
+static void QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine);
+static void QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight);
+#endif
+
+
+
+static size_t
+max_string_size (const SANE_String_Const strings[])
+{
+ size_t size, max_size = 0;
+ SANE_Int i;
+
+ for (i = 0; strings[i]; ++i)
+ {
+ size = strlen (strings[i]) + 1;
+ if (size > max_size)
+ max_size = size;
+ }
+ return max_size;
+}
+
+static SANE_Status
+calc_parameters (Mustek_Scanner * s)
+{
+ SANE_String val, val_source;
+ val = s->val[OPT_MODE].s;
+ val_source = s->val[OPT_SOURCE].s;
+
+ s->params.last_frame = SANE_TRUE;
+
+ if (strcmp (val, "Color48") == 0) /* Color48 */
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 16;
+ s->setpara.smScanMode = SM_RGB48;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_RGB24\n");
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_RGB24;
+ }
+ }
+ else if (strcmp (val, "Color24") == 0) /* Color24 */
+ {
+ s->params.format = SANE_FRAME_RGB;
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_RGB24;
+ }
+ else if (strcmp (val, "Gray16") == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 16;
+ s->setpara.smScanMode = SM_GRAY16;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->params.depth = 8;
+ DBG (DBG_DET, "calc_parameters : preview set ScanMode SM_GRAY\n");
+ s->setpara.smScanMode = SM_GRAY;
+ }
+ }
+ else if (strcmp (val, "Gray8") == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 8;
+ s->setpara.smScanMode = SM_GRAY;
+ }
+ else if (strcmp (val, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ s->params.format = SANE_FRAME_GRAY;
+ s->params.depth = 1;
+ s->setpara.smScanMode = SM_TEXT;
+ }
+
+ /*set Scan Source */
+ DBG (DBG_DET, "calc_parameters :scan Source = %s\n", val_source);
+ if (strcmp (val_source, "Reflective") == 0)
+ {
+ s->setpara.ssScanSource = SS_Reflective;
+ }
+ else if (strcmp (val_source, "Positive") == 0)
+ {
+ s->setpara.ssScanSource = SS_Positive;
+ }
+ else if (strcmp (val_source, "Negative") == 0)
+ {
+ s->setpara.ssScanSource = SS_Negative;
+ }
+
+
+ s->setpara.fmArea.x1 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_X].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.x2 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_X].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.y1 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_TL_Y].w) * 300.0) / MM_PER_INCH + 0.5);
+ s->setpara.fmArea.y2 =
+ (unsigned short) ((SANE_UNFIX (s->val[OPT_BR_Y].w) * 300.0) / MM_PER_INCH + 0.5);
+
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->setpara.fmArea.y1 = s->setpara.fmArea.y1 + PER_ADD_START_LINES;
+ s->setpara.fmArea.x1 += PRE_ADD_START_X;
+ } /*just for range bug. */
+
+ s->setpara.pfPixelFlavor = PF_BlackIs0;
+ s->setpara.wLinearThreshold = s->val[OPT_THRESHOLD].w;
+
+ s->setpara.wTargetDPI = s->val[OPT_RESOLUTION].w;
+ if (s->val[OPT_PREVIEW].w)
+ {
+ s->setpara.wTargetDPI = 75;
+ }
+
+ s->setpara.pGammaTable = NULL;
+
+ s->params.pixels_per_line =
+ (SANE_Int) ((s->setpara.fmArea.x2 -
+ s->setpara.fmArea.x1) * s->setpara.wTargetDPI / 300.0 + 0.5);
+
+ switch (s->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (s->params.depth == 8)
+ s->params.bytes_per_line = s->params.pixels_per_line * 3;
+ if (s->params.depth == 16)
+ s->params.bytes_per_line = s->params.pixels_per_line * 6;
+ break;
+ case SANE_FRAME_GRAY:
+ if (s->params.depth == 1)
+ s->params.bytes_per_line = s->params.pixels_per_line / 8;
+ if (s->params.depth == 8)
+ s->params.bytes_per_line = s->params.pixels_per_line;
+ if (s->params.depth == 16)
+ s->params.bytes_per_line = s->params.pixels_per_line * 2;
+ break;
+ default:
+ DBG (DBG_DET, "sane_star:sane params .format = %d\n", s->params.format);
+ }
+
+ s->params.lines =
+ (SANE_Int) ((s->setpara.fmArea.y2 -
+ s->setpara.fmArea.y1) * s->setpara.wTargetDPI / 300 + 0.5);
+
+ DBG (DBG_FUNC, "calc_parameters: end\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status
+init_options (Mustek_Scanner * s)
+{
+ SANE_Int option, count;
+ SANE_Word *dpi_list; /*Resolution Support */
+
+ DBG (DBG_FUNC, "init_options: start\n");
+
+ memset (s->opt, 0, sizeof (s->opt));
+ memset (s->val, 0, sizeof (s->val));
+
+ for (option = 0; option < NUM_OPTIONS; ++option)
+ {
+ s->opt[option].size = sizeof (SANE_Word);
+ s->opt[option].cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT;
+ }
+ /* Option num */
+ s->opt[OPT_NUM_OPTS].name = SANE_NAME_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].title = SANE_TITLE_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].desc = SANE_DESC_NUM_OPTIONS;
+ s->opt[OPT_NUM_OPTS].type = SANE_TYPE_INT;
+ s->opt[OPT_NUM_OPTS].cap = SANE_CAP_SOFT_DETECT;
+ s->val[OPT_NUM_OPTS].w = NUM_OPTIONS;
+
+ /* "Mode" group: */
+ s->opt[OPT_MODE_GROUP].title = SANE_I18N ("Scan Mode");
+ s->opt[OPT_MODE_GROUP].desc = "";
+ s->opt[OPT_MODE_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_MODE_GROUP].size = 0;
+ s->opt[OPT_MODE_GROUP].cap = 0;
+ s->opt[OPT_MODE_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* scan mode */
+ s->opt[OPT_MODE].name = SANE_NAME_SCAN_MODE;
+ s->opt[OPT_MODE].title = SANE_TITLE_SCAN_MODE;
+ s->opt[OPT_MODE].desc = SANE_DESC_SCAN_MODE;
+ s->opt[OPT_MODE].type = SANE_TYPE_STRING;
+ s->opt[OPT_MODE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+
+ /* Scan Source */
+ s->opt[OPT_SOURCE].name = SANE_NAME_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].title = SANE_TITLE_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].desc = SANE_DESC_SCAN_SOURCE;
+ s->opt[OPT_SOURCE].type = SANE_TYPE_STRING;
+ s->opt[OPT_SOURCE].constraint_type = SANE_CONSTRAINT_STRING_LIST;
+ s->opt[OPT_SOURCE].size = max_string_size (source_list);
+ s->opt[OPT_SOURCE].constraint.string_list = source_list;
+ s->val[OPT_SOURCE].s = strdup ("Reflective");
+
+ if (!IsTAConnected ())
+ {
+ DISABLE (OPT_SOURCE);
+ }
+
+
+ /* resolution */
+
+ for (count = 0; s->model.dpi_values[count] != 0; count++)
+ {
+ }
+ dpi_list = malloc ((count + 1) * sizeof (SANE_Word));
+ if (!dpi_list)
+ return SANE_STATUS_NO_MEM;
+ dpi_list[0] = count;
+
+ for (count = 0; s->model.dpi_values[count] != 0; count++)
+ dpi_list[count + 1] = s->model.dpi_values[count];
+
+ s->opt[OPT_RESOLUTION].name = SANE_NAME_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].title = SANE_TITLE_SCAN_RESOLUTION;
+
+
+ s->opt[OPT_RESOLUTION].desc = SANE_DESC_SCAN_RESOLUTION;
+ s->opt[OPT_RESOLUTION].type = SANE_TYPE_INT;
+ s->opt[OPT_RESOLUTION].unit = SANE_UNIT_DPI;
+ s->opt[OPT_RESOLUTION].constraint_type = SANE_CONSTRAINT_WORD_LIST;
+ s->opt[OPT_RESOLUTION].constraint.word_list = dpi_list;
+ s->val[OPT_RESOLUTION].w = 300;
+
+ /* 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].cap = SANE_CAP_SOFT_DETECT | SANE_CAP_SOFT_SELECT;
+ s->opt[OPT_PREVIEW].type = SANE_TYPE_BOOL;
+ s->val[OPT_PREVIEW].w = SANE_FALSE;
+
+ /* "Debug" group: */
+ s->opt[OPT_DEBUG_GROUP].title = SANE_I18N ("Debugging Options");
+ s->opt[OPT_DEBUG_GROUP].desc = "";
+ s->opt[OPT_DEBUG_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_DEBUG_GROUP].size = 0;
+ s->opt[OPT_DEBUG_GROUP].cap = 0;
+ s->opt[OPT_DEBUG_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* auto warmup */
+ s->opt[OPT_AUTO_WARMUP].name = "auto-warmup";
+ s->opt[OPT_AUTO_WARMUP].title = SANE_I18N ("Automatic warmup");
+ s->opt[OPT_AUTO_WARMUP].desc =
+ SANE_I18N ("Warm-up until the lamp's brightness is constant "
+ "instead of insisting on 40 seconds warm-up time.");
+ s->opt[OPT_AUTO_WARMUP].type = SANE_TYPE_BOOL;
+ s->opt[OPT_AUTO_WARMUP].unit = SANE_UNIT_NONE;
+ s->opt[OPT_AUTO_WARMUP].constraint_type = SANE_CONSTRAINT_NONE;
+ s->val[OPT_AUTO_WARMUP].w = SANE_FALSE;
+ if (s->model.is_cis)
+ DISABLE (OPT_AUTO_WARMUP);
+
+ /* "Enhancement" group: */
+ s->opt[OPT_ENHANCEMENT_GROUP].title = SANE_I18N ("Enhancement");
+ s->opt[OPT_ENHANCEMENT_GROUP].desc = "";
+ s->opt[OPT_ENHANCEMENT_GROUP].type = SANE_TYPE_GROUP;
+
+ s->opt[OPT_ENHANCEMENT_GROUP].size = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].cap = 0;
+ s->opt[OPT_ENHANCEMENT_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ /* 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 = &u8_range;
+ s->opt[OPT_THRESHOLD].cap |= SANE_CAP_INACTIVE;
+ s->val[OPT_THRESHOLD].w = DEF_LINEARTTHRESHOLD;
+
+ /* internal gamma value */
+ s->opt[OPT_GAMMA_VALUE].name = "gamma-value";
+ s->opt[OPT_GAMMA_VALUE].title = SANE_I18N ("Gamma value");
+ s->opt[OPT_GAMMA_VALUE].desc =
+ SANE_I18N ("Sets the gamma value of all channels.");
+ s->opt[OPT_GAMMA_VALUE].type = SANE_TYPE_FIXED;
+ s->opt[OPT_GAMMA_VALUE].unit = SANE_UNIT_NONE;
+ s->opt[OPT_GAMMA_VALUE].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_GAMMA_VALUE].constraint.range = &gamma_range;
+ s->opt[OPT_GAMMA_VALUE].cap |= SANE_CAP_EMULATED;
+ s->val[OPT_GAMMA_VALUE].w = s->model.default_gamma_value;
+
+ DISABLE (OPT_GAMMA_VALUE);
+
+ /* "Geometry" group: */
+ s->opt[OPT_GEOMETRY_GROUP].title = SANE_I18N ("Geometry");
+ s->opt[OPT_GEOMETRY_GROUP].desc = "";
+ s->opt[OPT_GEOMETRY_GROUP].type = SANE_TYPE_GROUP;
+ s->opt[OPT_GEOMETRY_GROUP].cap = SANE_CAP_ADVANCED;
+ s->opt[OPT_GEOMETRY_GROUP].size = 0;
+ s->opt[OPT_GEOMETRY_GROUP].constraint_type = SANE_CONSTRAINT_NONE;
+
+ x_range.max = s->model.x_size;
+ y_range.max = s->model.y_size;
+
+ /* top-left x */
+ s->opt[OPT_TL_X].name = SANE_NAME_SCAN_TL_X;
+ s->opt[OPT_TL_X].title = SANE_TITLE_SCAN_TL_X;
+ s->opt[OPT_TL_X].desc = SANE_DESC_SCAN_TL_X;
+ s->opt[OPT_TL_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_X].constraint.range = &x_range;
+
+ s->val[OPT_TL_X].w = 0;
+
+ /* top-left y */
+ s->opt[OPT_TL_Y].name = SANE_NAME_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].title = SANE_TITLE_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].desc = SANE_DESC_SCAN_TL_Y;
+ s->opt[OPT_TL_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_TL_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_TL_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_TL_Y].constraint.range = &y_range;
+ s->val[OPT_TL_Y].w = 0;
+
+ /* bottom-right x */
+ s->opt[OPT_BR_X].name = SANE_NAME_SCAN_BR_X;
+ s->opt[OPT_BR_X].title = SANE_TITLE_SCAN_BR_X;
+ s->opt[OPT_BR_X].desc = SANE_DESC_SCAN_BR_X;
+ s->opt[OPT_BR_X].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_X].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_X].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_X].constraint.range = &x_range;
+ s->val[OPT_BR_X].w = x_range.max;
+
+ /* bottom-right y */
+ s->opt[OPT_BR_Y].name = SANE_NAME_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].title = SANE_TITLE_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].desc = SANE_DESC_SCAN_BR_Y;
+ s->opt[OPT_BR_Y].type = SANE_TYPE_FIXED;
+ s->opt[OPT_BR_Y].unit = SANE_UNIT_MM;
+ s->opt[OPT_BR_Y].constraint_type = SANE_CONSTRAINT_RANGE;
+ s->opt[OPT_BR_Y].constraint.range = &y_range;
+ s->val[OPT_BR_Y].w = y_range.max;
+
+ calc_parameters (s);
+
+ DBG (DBG_FUNC, "init_options: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+/***************************** Code from spicall.c *****************************/
+
+static SANE_Byte * g_lpNegImageData = NULL;
+static SANE_Bool g_bIsFirstGetNegData = TRUE;
+static SANE_Bool g_bIsMallocNegData = FALSE;
+static unsigned int g_dwAlreadyGetNegLines = 0;
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Check the device connect status
+Parameters:
+ none
+Return value:
+ if the device is connected
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetDeviceStatus ()
+{
+ DBG (DBG_FUNC, "GetDeviceStatus: start\n");
+ return MustScanner_GetScannerState ();
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the lamp on or off
+Parameters:
+ isLampOn: turn the lamp on or off
+ isTALampOn: turn the TA lamp on or off
+Return value:
+ if operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn)
+{
+ DBG (DBG_FUNC, "PowerControl: start\n");
+ return MustScanner_PowerControl (isLampOn, isTALampOn);
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Turn the carriage home
+Parameters:
+ none
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+CarriageHome ()
+{
+ DBG (DBG_FUNC, "CarriageHome: start\n");
+ return MustScanner_BackHome ();
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ Get gamma input/output bit count
+Parameters:
+ pGammaInfo: the gamma information
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetGammaInfo (LPGAMMAINFO pGammaInfo)
+{
+ DBG (DBG_FUNC, "GetGammaInfo: start\n");
+
+ switch (pGammaInfo->smScanMode)
+ {
+ case SM_GRAY:
+ pGammaInfo->wInputGammaBits = 12;
+ pGammaInfo->wOutputGammaBits = 8;
+ break;
+ case SM_RGB24:
+ pGammaInfo->wInputGammaBits = 12;
+ pGammaInfo->wOutputGammaBits = 8;
+ break;
+ case SM_GRAY16:
+ pGammaInfo->wInputGammaBits = 16;
+ pGammaInfo->wOutputGammaBits = 16;
+ break;
+ case SM_RGB48:
+ pGammaInfo->wInputGammaBits = 16;
+ pGammaInfo->wOutputGammaBits = 16;
+ break;
+ default:
+ pGammaInfo->wInputGammaBits = 0;
+ pGammaInfo->wOutputGammaBits = 0;
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "GetGammaInfo: exit\n");
+ return TRUE;
+}
+#endif
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ set scan parameters
+Parameters:
+ pSetParameters: the information of scaning
+Return value:
+ if the operation success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+SetParameters (LPSETPARAMETERS pSetParameters)
+{
+ unsigned short X1inTargetDpi;
+ unsigned short Y1inTargetDpi;
+ unsigned short X2inTargetDpi;
+ unsigned short Y2inTargetDpi;
+
+ DBG (DBG_FUNC, "SetParameters: start\n");
+
+ /*0. Reset */
+ if (ST_Reflective == g_ScanType)
+ {
+ Reflective_Reset ();
+ }
+ else
+ {
+ Transparent_Reset ();
+ }
+
+ /*1. Scan mode */
+ switch (pSetParameters->smScanMode)
+ {
+ case SM_TEXT:
+ g_tiTarget.cmColorMode = CM_TEXT;
+ break;
+ case SM_GRAY:
+ g_tiTarget.cmColorMode = CM_GRAY8;
+ break;
+ case SM_GRAY16:
+ g_tiTarget.cmColorMode = CM_GRAY16;
+ break;
+ case SM_RGB24:
+ g_tiTarget.cmColorMode = CM_RGB24;
+ break;
+ case SM_RGB48:
+ g_tiTarget.cmColorMode = CM_RGB48;
+ break;
+ default:
+ return FALSE;
+ }
+
+ /*2. Scan source */
+ g_ssScanSource = pSetParameters->ssScanSource;
+ g_tiTarget.bScanSource = pSetParameters->ssScanSource;
+
+
+ if (SS_Reflective == pSetParameters->ssScanSource)
+ {
+ g_ScanType = ST_Reflective;
+ }
+ else if (SS_Positive == pSetParameters->ssScanSource
+ || SS_Negative == pSetParameters->ssScanSource
+ || SS_ADF == pSetParameters->ssScanSource)
+ {
+ g_ScanType = ST_Transparent;
+ }
+ else
+ {
+ DBG (DBG_ERR, "SetParameters: ScanSource error\n");
+ return FALSE;
+ }
+
+ /*3. pixel flavor */
+ if (PF_BlackIs0 == pSetParameters->pfPixelFlavor
+ || PF_WhiteIs0 == pSetParameters->pfPixelFlavor)
+ {
+ g_PixelFlavor = pSetParameters->pfPixelFlavor;
+ }
+ else
+ {
+ DBG (DBG_ERR, "SetParameters: PixelFlavor error\n");
+ return FALSE;
+ }
+
+ /*4. Scan area */
+ if (pSetParameters->fmArea.x1 >= pSetParameters->fmArea.x2)
+ {
+ DBG (DBG_ERR, "SetParameters: x1 > x2, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.y1 >= pSetParameters->fmArea.y2)
+ {
+ DBG (DBG_ERR, "SetParameters: y1 >= y2, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.x2 > MAX_SCANNING_WIDTH) /* Just for A4 size */
+ {
+ DBG (DBG_ERR, "SetParameters: x2 > MAX_SCANNING_WIDTH, error\n");
+ return FALSE;
+ }
+ if (pSetParameters->fmArea.y2 > MAX_SCANNING_HEIGHT) /* Just for A4 size */
+ {
+ DBG (DBG_ERR, "SetParameters: y2 > MAX_SCANNING_HEIGHT, error\n");
+ return FALSE;
+ }
+
+ X1inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.x1) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ Y1inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.y1) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ X2inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.x2) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+ Y2inTargetDpi =
+ (unsigned short) ((unsigned int) (pSetParameters->fmArea.y2) *
+ (unsigned int) (pSetParameters->wTargetDPI) / 300L);
+
+ g_tiTarget.isOptimalSpeed = TRUE;
+ g_tiTarget.wDpi = pSetParameters->wTargetDPI;
+ g_tiTarget.wX = X1inTargetDpi;
+ g_tiTarget.wY = Y1inTargetDpi;
+ g_tiTarget.wWidth = X2inTargetDpi - X1inTargetDpi;
+ g_tiTarget.wHeight = Y2inTargetDpi - Y1inTargetDpi;
+
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wDpi=%d\n", g_tiTarget.wDpi);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wX=%d\n", g_tiTarget.wX);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wY=%d\n", g_tiTarget.wY);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wWidth=%d\n", g_tiTarget.wWidth);
+ DBG (DBG_INFO, "SetParameters: g_tiTarget.wHeight=%d\n",
+ g_tiTarget.wHeight);
+
+ /*5.Prepare */
+ if (FALSE == MustScanner_Prepare (g_tiTarget.bScanSource))
+ {
+ DBG (DBG_ERR, "SetParameters: MustScanner_Prepare fail\n");
+ return FALSE;
+ }
+
+ /*6. Linear threshold */
+ if (pSetParameters->wLinearThreshold > 256
+ && pSetParameters->smScanMode == SM_TEXT)
+ {
+ DBG (DBG_ERR, "SetParameters: LinearThreshold error\n");
+ return FALSE;
+ }
+ else
+ {
+ g_wLineartThreshold = pSetParameters->wLinearThreshold;
+ }
+
+ /*7. Gamma table */
+ if (NULL != pSetParameters->pGammaTable)
+ {
+ DBG (DBG_INFO, "SetParameters: IN gamma table not NULL\n");
+ g_pGammaTable = pSetParameters->pGammaTable;
+ g_isSelfGamma = FALSE;
+ }
+ else if (pSetParameters->smScanMode == SM_GRAY
+ || pSetParameters->smScanMode == SM_RGB24)
+ {
+ unsigned short i;
+ SANE_Byte byGammaData;
+ double pow_d;
+ double pow_z = (double) 10 / 16.0;
+
+ g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 4096 * 3);
+
+ DBG (DBG_INFO, "SetParameters: gamma table malloc %ld Bytes\n",
+ (long int) sizeof (unsigned short) * 4096 * 3);
+ DBG (DBG_INFO, "SetParameters: address of g_pGammaTable=%p\n",
+ (void *) g_pGammaTable);
+
+ if (NULL == g_pGammaTable)
+ {
+ DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
+ return FALSE;
+ }
+ g_isSelfGamma = TRUE;
+
+ for (i = 0; i < 4096; i++)
+ {
+ pow_d = (double) i / (double) 4096;
+
+ byGammaData = (SANE_Byte) (pow (pow_d, pow_z) * 255);
+
+ *(g_pGammaTable + i) = byGammaData;
+ *(g_pGammaTable + i + 4096) = byGammaData;
+ *(g_pGammaTable + i + 8192) = byGammaData;
+ }
+ }
+ else if (pSetParameters->smScanMode == SM_GRAY16
+ || pSetParameters->smScanMode == SM_RGB48)
+ {
+ unsigned int i, wGammaData;
+ g_pGammaTable = (unsigned short *) malloc (sizeof (unsigned short) * 65536 * 3);
+
+ if (g_pGammaTable == NULL)
+ {
+ DBG (DBG_ERR, "SetParameters: gamma table malloc fail\n");
+ return FALSE;
+ }
+ g_isSelfGamma = TRUE;
+
+ for (i = 0; i < 65536; i++)
+ {
+ wGammaData =
+ (unsigned short) (pow ((((float) i) / 65536.0), (((float) 10) / 16.0)) *
+ 65535);
+
+ *(g_pGammaTable + i) = wGammaData;
+ *(g_pGammaTable + i + 65536) = wGammaData;
+ *(g_pGammaTable + i + 65536 * 2) = wGammaData;
+ }
+ }
+ else
+ {
+ DBG (DBG_INFO, "SetParameters: set g_pGammaTable to NULL\n");
+ g_pGammaTable = NULL;
+ }
+
+ DBG (DBG_FUNC, "SetParameters: exit\n");
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+Routine Description:
+ get the optical dpi and scan area
+Parameters:
+ pGetParameters: the information of scan
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetParameters (LPGETPARAMETERS pGetParameters)
+{
+ DBG (DBG_FUNC, "GetParameters: start\n");
+ if (ST_Reflective == g_ScanType)
+ {
+ if (FALSE == Reflective_ScanSuggest (&g_tiTarget, &g_ssSuggest))
+ {
+ DBG (DBG_ERR, "GetParameters: Reflective_ScanSuggest error\n");
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (FALSE == Transparent_ScanSuggest (&g_tiTarget, &g_ssSuggest))
+ {
+ DBG (DBG_ERR, "GetParameters: Transparent_ScanSuggest error\n");
+ return FALSE;
+ }
+ }
+
+ pGetParameters->wSourceXDPI = g_ssSuggest.wXDpi;
+ pGetParameters->wSourceYDPI = g_ssSuggest.wYDpi;
+ pGetParameters->dwLength = (unsigned int) g_ssSuggest.wHeight;
+ pGetParameters->dwLineByteWidth = g_ssSuggest.dwBytesPerRow;
+
+ DBG (DBG_FUNC, "GetParameters: exit\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/13
+
+Routine Description:
+ start scan image
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+StartScan ()
+{
+ DBG (DBG_FUNC, "StartScan: start\n");
+ if (ST_Reflective == g_ScanType)
+ {
+ DBG (DBG_INFO, "StartScan: g_ScanType==ST_Reflective\n");
+
+ return Reflective_SetupScan (g_ssSuggest.cmScanMode,
+ g_ssSuggest.wXDpi,
+ g_ssSuggest.wYDpi,
+ PF_BlackIs0,
+ g_ssSuggest.wX,
+ g_ssSuggest.wY,
+ g_ssSuggest.wWidth, g_ssSuggest.wHeight);
+ }
+ else
+
+ {
+
+ DBG (DBG_INFO, "StartScan: g_ScanType==ST_Transparent\n");
+
+ return Transparent_SetupScan (g_ssSuggest.cmScanMode,
+ g_ssSuggest.wXDpi,
+ g_ssSuggest.wYDpi,
+ PF_BlackIs0,
+ g_ssSuggest.wX,
+ g_ssSuggest.wY,
+ g_ssSuggest.wWidth, g_ssSuggest.wHeight);
+ }
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Read the scanner data
+Parameters:
+
+ pImageRows: the information of the data
+Return value:
+ if the operation is seccuss
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+ReadScannedData (LPIMAGEROWS pImageRows)
+{
+ SANE_Bool isRGBInvert;
+ unsigned short Rows = 0;
+ SANE_Byte *lpBlock = (SANE_Byte *) pImageRows->pBuffer;
+ SANE_Byte *lpReturnData = (SANE_Byte *) pImageRows->pBuffer;
+ int i = 0;
+
+ DBG (DBG_FUNC, "ReadScannedData: start\n");
+
+ if (pImageRows->roRgbOrder == RO_RGB)
+ isRGBInvert = FALSE;
+ else
+ isRGBInvert = TRUE;
+
+ Rows = pImageRows->wWantedLineNum;
+
+ DBG (DBG_INFO, "ReadScannedData: wanted Rows = %d\n", Rows);
+
+ if (ST_Reflective == g_ScanType)
+ {
+ if (FALSE == Reflective_GetRows (lpBlock, &Rows, isRGBInvert))
+ return FALSE;
+ }
+ else if (SS_Positive == g_ssScanSource)
+ {
+ if (FALSE == Transparent_GetRows (lpBlock, &Rows, isRGBInvert))
+ return FALSE;
+ }
+
+ pImageRows->wXferedLineNum = Rows;
+
+ if (g_PixelFlavor == PF_WhiteIs0 || g_ScanMode == CM_TEXT)
+ {
+ int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
+ for (i = 0; i < TotalSize; i++)
+ {
+ *(lpBlock++) ^= 0xff;
+ }
+ }
+
+ if (SS_Negative == g_ssScanSource)
+ {
+ DBG (DBG_INFO, "ReadScannedData: deal with the Negative\n");
+
+ if (g_bIsFirstGetNegData)
+ {
+ unsigned int TotalImgeSize = g_SWHeight * g_ssSuggest.dwBytesPerRow;
+ g_lpNegImageData = (SANE_Byte *) malloc (TotalImgeSize);
+ if (NULL != g_lpNegImageData)
+ {
+ SANE_Byte * lpTempData = g_lpNegImageData;
+ DBG (DBG_INFO,
+ "ReadScannedData: malloc the negative data is success!\n");
+ g_bIsMallocNegData = TRUE;
+ if (!Transparent_GetRows
+ (g_lpNegImageData, &g_SWHeight, isRGBInvert))
+ {
+ return FALSE;
+ }
+
+ DBG (DBG_INFO, "ReadScannedData: get image data is over!\n");
+
+ for (i = 0; i < (int) TotalImgeSize; i++)
+ {
+ *(g_lpNegImageData++) ^= 0xff;
+ }
+ g_lpNegImageData = lpTempData;
+ AutoLevel (g_lpNegImageData, g_ScanMode, g_SWHeight,
+ g_ssSuggest.dwBytesPerRow);
+ DBG (DBG_INFO, "ReadScannedData: autolevel is ok\n");
+ }
+ g_bIsFirstGetNegData = FALSE;
+ }
+
+ if (g_bIsMallocNegData)
+ {
+ memcpy (pImageRows->pBuffer,
+ g_lpNegImageData +
+ g_ssSuggest.dwBytesPerRow * g_dwAlreadyGetNegLines,
+ g_ssSuggest.dwBytesPerRow * Rows);
+
+ DBG (DBG_INFO, "ReadScannedData: copy the data over!\n");
+
+ g_dwAlreadyGetNegLines += Rows;
+ if (g_dwAlreadyGetNegLines >= g_SWHeight)
+ {
+ DBG (DBG_INFO, "ReadScannedData: free the image data!\n");
+ free (g_lpNegImageData);
+ g_lpNegImageData = NULL;
+ g_bIsFirstGetNegData = TRUE;
+ g_dwAlreadyGetNegLines = 0;
+ g_bIsMallocNegData = FALSE;
+ }
+ }
+ else
+ {
+ int TotalSize = Rows * g_ssSuggest.dwBytesPerRow;
+ DBG (DBG_INFO,
+ "ReadScannedData: malloc the negative data is fail!\n");
+ if (!Transparent_GetRows (lpReturnData, &Rows, isRGBInvert))
+ {
+ return FALSE;
+ }
+
+ for (i = 0; i < TotalSize; i++)
+ {
+ *(lpReturnData++) ^= 0xff;
+ }
+ pImageRows->wXferedLineNum = Rows;
+
+ g_dwAlreadyGetNegLines += Rows;
+ if (g_dwAlreadyGetNegLines >= g_SWHeight)
+ {
+ g_bIsFirstGetNegData = TRUE;
+ g_dwAlreadyGetNegLines = 0;
+ g_bIsMallocNegData = FALSE;
+ }
+ }
+
+ }
+
+ DBG (DBG_FUNC, "ReadScannedData: leave ReadScannedData\n");
+
+ return TRUE;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Stop scan
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+StopScan ()
+{
+ SANE_Bool rt;
+ int i;
+
+ DBG (DBG_FUNC, "StopScan: start\n");
+
+ /*stop read data and kill thread */
+ if (ST_Reflective == g_ScanType)
+ {
+ rt = Reflective_StopScan ();
+ }
+ else
+ {
+ rt = Transparent_StopScan ();
+ }
+
+ /*free gamma table */
+ if (g_isSelfGamma && g_pGammaTable != NULL)
+ {
+ for (i = 0; i < 20; i++)
+ {
+ if (!g_isScanning)
+ {
+ free (g_pGammaTable);
+ g_pGammaTable = NULL;
+ break;
+ }
+ else
+ {
+ sleep (1); /*waiting ReadScannedData return. */
+ }
+ }
+ }
+
+ /*free image buffer */
+ if (g_lpReadImageHead != NULL)
+
+ {
+ free (g_lpReadImageHead);
+ g_lpReadImageHead = NULL;
+ }
+
+ DBG (DBG_FUNC, "StopScan: exit\n");
+ return rt;
+}
+
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Check the status of TA
+Parameters:
+ none
+Return value:
+ if operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+IsTAConnected ()
+{
+ SANE_Bool hasTA;
+
+ DBG (DBG_FUNC, "StopScan: start\n");
+
+ if (Asic_Open (&g_chip, g_pDeviceFile) != STATUS_GOOD)
+ {
+ return FALSE;
+ }
+
+ if (Asic_IsTAConnected (&g_chip, &hasTA) != STATUS_GOOD)
+ {
+ Asic_Close (&g_chip);
+ return FALSE;
+ }
+
+ Asic_Close (&g_chip);
+
+ DBG (DBG_FUNC, "StopScan: exit\n");
+ return hasTA;
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Get the status of the HK
+Parameters:
+ pKey: the status of key
+Return value:
+ if the operation is success
+ return TRUE
+ else
+ return FALSE
+***********************************************************************/
+static SANE_Bool
+GetKeyStatus (SANE_Byte * pKey)
+{
+ SANE_Byte pKeyTemp = 0x00;
+ STATUS status = Asic_CheckFunctionKey (&g_chip, &pKeyTemp);
+ DBG (DBG_FUNC, "GetKeyStatus: start\n");
+
+ if (STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_Open is fail\n");
+ return FALSE;
+ }
+
+ if (STATUS_GOOD != status)
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_CheckFunctionKey is fail\n");
+ return FALSE;
+ }
+
+ if (0x01 == pKeyTemp)
+ {
+ *pKey = 0x01; /*Scan key pressed */
+ }
+
+ if (0x02 == pKeyTemp)
+ {
+ *pKey = 0x02; /*Copy key pressed */
+ }
+ if (0x04 == pKeyTemp)
+ {
+ *pKey = 0x03; /*Fax key pressed */
+ }
+ if (0x08 == pKeyTemp)
+ {
+ *pKey = 0x04; /*Email key pressed */
+ }
+ if (0x10 == pKeyTemp)
+ {
+ *pKey = 0x05; /*Panel key pressed */
+ }
+
+ if (STATUS_GOOD != Asic_Close (&g_chip))
+ {
+ DBG (DBG_ERR, "GetKeyStatus: Asic_Close is fail\n");
+ return FALSE;
+ }
+
+ DBG (DBG_FUNC, "GetKeyStatus: exit\n");
+ return TRUE;
+}
+#endif
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Deal with the image with auto level
+Parameters:
+ lpSource: the data of image
+ scanMode: the scan mode
+ ScanLines: the rows of image
+ BytesPerLine: the bytes of per line
+Return value:
+ none
+***********************************************************************/
+static void
+AutoLevel (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine)
+{
+ int ii;
+ unsigned int i, j;
+ unsigned int tLines, CountPixels, TotalImgSize;
+ unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
+ float fmax_R, fmax_G, fmax_B;
+ unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
+ float mean_R, mean_G, mean_B;
+ unsigned int hisgram_R[256], hisgram_G[256], hisgram_B[256];
+
+ unsigned int iWidth = BytesPerLine / 3;
+ unsigned int iHeight = ScanLines;
+ SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;
+
+ unsigned int tmp = 0;
+ unsigned short imin_threshold[3];
+ unsigned short imax_threshold[3];
+
+ DBG (DBG_FUNC, "AutoLevel: start\n");
+
+ if (scanMode != CM_RGB24ext)
+ {
+ return;
+ }
+
+ i = j = 0;
+ tLines = CountPixels = TotalImgSize = 0;
+
+ TotalImgSize = iWidth * iHeight;
+
+
+
+ for (i = 0; i < 256; i++)
+ {
+
+ hisgram_R[i] = 0;
+ hisgram_G[i] = 0;
+ hisgram_B[i] = 0;
+ }
+
+
+ DBG (DBG_INFO, "AutoLevel: init data is over\n");
+
+ /* Find min , max, mean */
+ max_R = max_G = max_B = 0;
+ min_R = min_G = min_B = 255;
+ tLines = 0;
+ DBG (DBG_INFO, "AutoLevel: iHeight = %d, iWidth = %d\n", iHeight, iWidth);
+
+ for (j = 0; j < iHeight; j++)
+ {
+ tLines = j * iWidth * 3;
+
+
+ for (i = 0; i < iWidth; i++)
+ {
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ max_R = _MAX (R, max_R);
+ max_G = _MAX (G, max_G);
+ max_B = _MAX (B, max_B);
+
+ min_R = _MIN (R, min_R);
+ min_G = _MIN (G, min_G);
+ min_B = _MIN (B, min_B);
+
+ hisgram_R[(SANE_Byte) R]++;
+ hisgram_G[(SANE_Byte) G]++;
+ hisgram_B[(SANE_Byte) B]++;
+
+ sum_R += R;
+ sum_G += G;
+ sum_B += B;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ CountPixels++;
+ }
+
+ }
+
+ DBG (DBG_INFO, "AutoLevel: Find min , max is over!\n");
+
+ mean_R = (float) (sum_R / TotalImgSize);
+ mean_G = (float) (sum_G / TotalImgSize);
+ mean_B = (float) (sum_B / TotalImgSize);
+
+
+ imin_threshold[0] = 0;
+ imin_threshold[1] = 0;
+ imin_threshold[2] = 0;
+ imax_threshold[0] = 0;
+ imax_threshold[1] = 0;
+ imax_threshold[2] = 0;
+
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_R[ii] > 0)
+ if (hisgram_R[ii] >= imin_threshold[0])
+ {
+ min_R = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_R[ii] > 0)
+ if (hisgram_R[ii] >= imax_threshold[0])
+ {
+ max_R = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_G[ii] > 0)
+ if (hisgram_G[ii] >= imin_threshold[1])
+ {
+ min_G = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_G[ii] > 0)
+ if (hisgram_G[ii] >= imax_threshold[1])
+ {
+ max_G = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 0; ii < 256; ii++)
+ {
+ if (hisgram_B[ii] > 0)
+ if (hisgram_B[ii] >= imin_threshold[2])
+ {
+ min_B = ii;
+ break;
+ }
+ }
+
+ tmp = 0;
+ for (ii = 255; ii >= 0; ii--)
+ {
+ if (hisgram_B[ii] > 0)
+ if (hisgram_B[ii] >= imax_threshold[2])
+ {
+ max_B = ii;
+ break;
+ }
+ }
+
+ DBG (DBG_INFO, "AutoLevel: Set min , max is over!\n");
+
+ /*Autolevel: */
+ sum_R = max_R - min_R;
+ sum_G = max_G - min_G;
+ sum_B = max_B - min_B;
+
+ for (j = 0; j < iHeight; j++)
+ {
+ tLines = j * iWidth * 3;
+ for (i = 0; i < iWidth; i++)
+ {
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ /*R*/ if (sum_R == 0)
+ R = max_R;
+ else if (R < min_R)
+ R = 0;
+ else if (R <= 255)
+ {
+ fmax_R = ((float) ((R - min_R) * 255) / (float) sum_R);
+ R = (unsigned short) fmax_R;
+ fmax_R = (fmax_R - R) * 10;
+
+ if (fmax_R >= 5)
+ R++;
+ }
+ if (R > 255)
+ R = 255;
+
+ /*G*/ if (sum_G == 0)
+ G = max_G;
+ else if (G < min_G)
+ G = 0;
+ else if (G <= 255)
+ {
+ fmax_G = ((float) ((G - min_G) * 255) / (float) sum_G);
+ G = (unsigned short) fmax_G;
+ fmax_G = (fmax_G - G) * 10;
+ if (fmax_G >= 5)
+ G++;
+
+ }
+ if (G > 255)
+ G = 255;
+
+ /*B*/ if (sum_B == 0)
+ B = max_B;
+ else if (B < min_B)
+ B = 0;
+ else if (B <= 255)
+ {
+ fmax_B = ((float) (B - min_B) * 255 / (float) sum_B);
+ B = (unsigned short) fmax_B;
+ fmax_B = (fmax_B - B) * 10;
+ if (fmax_B >= 5)
+ B++;
+ }
+ if (B > 255)
+ B = 255;
+
+ hisgram_R[(SANE_Byte) R]++;
+ hisgram_G[(SANE_Byte) G]++;
+ hisgram_B[(SANE_Byte) B]++;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ }
+ }
+
+ DBG (DBG_FUNC, "AutoLevel: exit\n");
+ return;
+}
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Deal with image with auto level
+Parameters:
+ pDIB: the data of image
+ ImageWidth: the width of image
+ ImageHeight: the height of image
+Return value:
+ none
+***********************************************************************/
+static void
+QBETDetectAutoLevel (void *pDIB, unsigned int ImageWidth, unsigned int ImageHeight)
+{
+ unsigned short *pbmpdata;
+ float fRPercent = 0.0;
+ float fGPercent = 0.0;
+ float fBPercent = 0.0;
+ float fRSum, fGSum, fBSum;
+
+ int i, j;
+ unsigned int tLines, CountPixels, TotalImgSize;
+ unsigned short R, G, B, max_R, max_G, max_B, min_R, min_G, min_B;
+ unsigned short wIndexR, wIndexG, wIndexB;
+ float fmax_R, fmax_G, fmax_B;
+ unsigned int sum_R = 0, sum_G = 0, sum_B = 0;
+ unsigned int hisgram_R[1024], hisgram_G[1024], hisgram_B[1024];
+
+ if (!pDIB)
+ {
+ return;
+ }
+
+ pbmpdata = (unsigned short *) pDIB;
+
+ CountPixels = 0;
+ TotalImgSize = ImageWidth * ImageHeight;
+
+
+ for (i = 0; i < 1024; i++)
+ {
+
+ hisgram_R[i] = 0;
+ hisgram_G[i] = 0;
+ hisgram_B[i] = 0;
+ }
+
+
+ /*Find min , max, mean */
+ max_R = max_G = max_B = 0;
+ min_R = min_G = min_B = 1023;
+ tLines = 0;
+
+ for (j = 0; j < (int) ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < (int) ImageWidth; i++)
+ {
+ R = *(pbmpdata + (tLines + i * 3 + 2));
+ G = *(pbmpdata + (tLines + i * 3 + 1));
+ B = *(pbmpdata + (tLines + i * 3));
+
+ max_R = _MAX (R, max_R);
+ max_G = _MAX (G, max_G);
+ max_B = _MAX (B, max_B);
+
+ min_R = _MIN (R, min_R);
+ min_G = _MIN (G, min_G);
+ min_B = _MIN (B, min_B);
+
+ hisgram_R[R]++;
+ hisgram_G[G]++;
+ hisgram_B[B]++;
+
+ sum_R += R;
+ sum_G += G;
+ sum_B += B;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = G;
+ *(pbmpdata + (tLines + i * 3)) = B;
+
+ CountPixels++;
+ }
+
+ }
+
+
+ fRSum = 0.0;
+ fGSum = 0.0;
+ fBSum = 0.0;
+
+ wIndexR = 511;
+ wIndexG = 511;
+ wIndexB = 511;
+
+ for (i = 0; i < 1024; i++)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent > 50)
+ {
+ wIndexR = i;
+ break;
+ }
+
+ }
+
+ for (i = 0; i < 1024; i++)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent > 50)
+ {
+ wIndexG = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < 1024; i++)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent > 50)
+ {
+ wIndexB = i;
+ break;
+ }
+
+ }
+
+
+ fRSum = 0.0;
+
+ for (i = wIndexR; i >= 0; i--)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent >= 48)
+ {
+ min_R = i;
+ break;
+ }
+
+ }
+
+ fRSum = 0.0;
+ for (i = wIndexR; i < 1024; i++)
+ {
+ fRSum += (float) hisgram_R[i];
+ fRPercent = (fRSum / CountPixels) * 100;
+ if (fRPercent >= 47)
+ {
+ max_R = i;
+ break;
+ }
+
+ }
+
+
+ fGSum = 0.0;
+ for (i = wIndexG; i >= 0; i--)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent >= 48)
+ {
+ min_G = i;
+ break;
+ }
+
+ }
+
+ fGSum = 0.0;
+ for (i = wIndexG; i < 1024; i++)
+ {
+ fGSum += (float) hisgram_G[i];
+ fGPercent = (fGSum / CountPixels) * 100;
+ if (fGPercent >= 47)
+ {
+ max_G = i;
+ break;
+ }
+
+ }
+
+ fBSum = 0.0;
+ for (i = wIndexB; i >= 0; i--)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent >= 46)
+ {
+ min_B = i;
+ break;
+ }
+
+ }
+
+ fBSum = 0.0;
+ for (i = wIndexB; i < 1024; i++)
+ {
+ fBSum += (float) hisgram_B[i];
+ fBPercent = (fBSum / CountPixels) * 100;
+ if (fBPercent >= 47)
+ {
+ max_B = i;
+ break;
+ }
+
+ }
+
+
+ /*Autolevel: */
+ sum_R = max_R - min_R;
+ sum_G = max_G - min_G;
+ sum_B = max_B - min_B;
+
+ for (j = 0; j < (int) ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < (int) ImageWidth; i++)
+ {
+ R = *(pbmpdata + (tLines + i * 3 + 2));
+ G = *(pbmpdata + (tLines + i * 3 + 1));
+ B = *(pbmpdata + (tLines + i * 3));
+
+
+ /*R*/ if (sum_R == 0)
+ R = max_R;
+ else if (R < min_R)
+ {
+
+ R = 0;
+ }
+ else if ((R >= min_R) && (R <= 1023))
+ {
+ fmax_R = ((float) ((R - min_R) * 923) / (float) sum_R) + 100;
+ R = (unsigned short) fmax_R;
+ fmax_R = (fmax_R - R) * 10;
+ if (fmax_R >= 5)
+ R++;
+ }
+ if (R > 1023)
+ R = 1023;
+
+ /*G*/ if (sum_G == 0)
+ G = max_G;
+ else if (G < min_G)
+ {
+
+ G = 0;
+ }
+ else if ((G >= min_G) && (G <= 1023))
+ {
+ fmax_G = ((float) ((G - min_G) * 923) / (float) sum_G) + 100;
+ G = (unsigned short) fmax_G;
+ fmax_G = (fmax_G - G) * 10;
+ if (fmax_G >= 5)
+ G++;
+ }
+ if (G > 1023)
+ G = 1023;
+
+ /*B*/ if (sum_B == 0)
+ B = max_B;
+ else if (B < min_R)
+ {
+
+ B = 0;
+ }
+ else if ((B >= min_B) && (R <= 1023))
+ {
+ fmax_B = ((float) (B - min_B) * 923 / (float) sum_B) + 100;
+
+ B = (unsigned short) fmax_B;
+ fmax_B = (fmax_B - B) * 10;
+ if (fmax_B >= 5)
+ B++;
+ }
+ if (B > 1023)
+ B = 1023;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = G;
+ *(pbmpdata + (tLines + i * 3)) = B;
+
+ }
+ }
+
+ return;
+}
+#endif
+
+#ifdef SANE_UNUSED
+/**********************************************************************
+Author: Jack Date: 2005/05/14
+Routine Description:
+ Change the image data and deal with auto level
+Parameters:
+ lpSource: the data of image
+ scanMode: the scan mode
+ ScanLines: the rows of image
+ BytesPerLine: the bytes of per line
+Return value:
+ none
+***********************************************************************/
+static void
+QBetChange (SANE_Byte *lpSource, SCANMODE scanMode, unsigned short ScanLines,
+ unsigned int BytesPerLine)
+{
+ unsigned short i, j;
+ unsigned int tLines, TotalImgSize;
+ unsigned short R1, G1, B1, R, G, B, R2, G2, B2, QBET_RGB = 0, PointF, PointB;
+ unsigned short *pwRGB;
+
+ int k;
+
+ unsigned int ImageWidth = BytesPerLine / 3;
+ unsigned int ImageHeight = ScanLines;
+ SANE_Byte *pbmpdata = (SANE_Byte *) lpSource;
+
+ if (scanMode != CM_RGB24ext)
+ {
+ return;
+ }
+
+
+ TotalImgSize = ImageWidth * ImageHeight * 3 * 2;
+ if ((pwRGB = (unsigned short *) malloc (TotalImgSize)) == NULL)
+ {
+ return;
+ }
+
+
+ for (j = 0; j < ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+ for (i = 0; i < ImageWidth; i++)
+ {
+ if (i == 0)
+ {
+ R1 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G1 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B1 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+ R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
+ G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
+ B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
+ }
+ else if (i == (ImageWidth - 1))
+ {
+ R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
+ G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
+ B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));
+ R2 = R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G2 = G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B2 = B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+ }
+ else
+ {
+ R1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 2));
+ G1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3 + 1));
+ B1 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i - 1) * 3));
+
+ R = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 2));
+ G = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3 + 1));
+ B = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + i * 3));
+
+ R2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 2));
+ G2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3 + 1));
+ B2 = (unsigned short) (SANE_Byte) * (pbmpdata + (tLines + (i + 1) * 3));
+ }
+
+ R1 = R1 & 0x0003;
+ G1 = G1 & 0x0003;
+ B1 = B1 & 0x0003;
+
+ R2 = R2 & 0x0003;
+ G2 = G2 & 0x0003;
+ B2 = B2 & 0x0003;
+ for (k = 0; k < 3; k++)
+ {
+ if (k == 0)
+ {
+ PointF = R1;
+ PointB = R2;
+ }
+ else if (k == 1)
+ {
+ PointF = G1;
+ PointB = G2;
+ }
+ else if (k == 2)
+ {
+ PointF = B1;
+ PointB = B2;
+ }
+
+ switch (PointF)
+ {
+ case 0:
+ case 1:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFC;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFC;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFE;
+ break;
+ case 2:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFD;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFF;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFF;
+ break;
+ case 3:
+ if (PointB == 0)
+ QBET_RGB = 0xFFFE;
+ else if (PointB == 1)
+ QBET_RGB = 0xFFFE;
+ else if (PointB == 2)
+ QBET_RGB = 0xFFFF;
+ else if (PointB == 3)
+ QBET_RGB = 0xFFFF;
+ break;
+ default:
+ break;
+ }
+
+ if (k == 0)
+ {
+ R = R << 2;
+ R = R + 0x0003;
+ R = R & QBET_RGB;
+ }
+ else if (k == 1)
+ {
+ G = G << 2;
+ G = G + 0x0003;
+ G = G & QBET_RGB;
+ }
+ else if (k == 2)
+ {
+ B = B << 2;
+ B = B + 0x0003;
+ B = B & QBET_RGB;
+ }
+
+ }
+
+ *(pwRGB + (tLines + i * 3 + 2)) = R;
+ *(pwRGB + (tLines + i * 3 + 1)) = G;
+ *(pwRGB + (tLines + i * 3)) = B;
+
+ }
+
+ }
+
+
+ QBETDetectAutoLevel (pwRGB, ImageWidth, ImageHeight);
+
+
+ for (j = 0; j < ImageHeight; j++)
+ {
+ tLines = j * ImageWidth * 3;
+
+ for (i = 0; i < ImageWidth; i++)
+ {
+ R = *(pwRGB + (tLines + i * 3 + 2));
+ G = *(pwRGB + (tLines + i * 3 + 1));
+ B = *(pwRGB + (tLines + i * 3));
+
+ R = R >> 2;
+ G = G >> 2;
+
+ B = B >> 2;
+ if (R > 255)
+ R = 255;
+ if (G > 255)
+ G = 255;
+ if (B > 255)
+ B = 255;
+
+ *(pbmpdata + (tLines + i * 3 + 2)) = (SANE_Byte) R;
+ *(pbmpdata + (tLines + i * 3 + 1)) = (SANE_Byte) G;
+ *(pbmpdata + (tLines + i * 3)) = (SANE_Byte) B;
+
+ }
+
+ }
+
+
+ if (pwRGB != NULL)
+ {
+ free (pwRGB);
+ }
+
+ return;
+}
+#endif
+
+/****************************** SANE API functions *****************************/
+
+SANE_Status
+sane_init (SANE_Int * version_code, SANE_Auth_Callback authorize)
+{
+ DBG_INIT ();
+ DBG (DBG_FUNC, "sane_init: start\n");
+ DBG (DBG_ERR, "SANE Mustek USB2 backend version %d.%d build %d from %s\n",
+ SANE_CURRENT_MAJOR, V_MINOR, BUILD, PACKAGE_STRING);
+
+ num_devices = 1; /* HOLD: only one device in this backend */
+
+ if (version_code != NULL)
+ *version_code = SANE_VERSION_CODE (SANE_CURRENT_MAJOR, V_MINOR, BUILD);
+
+ DBG (DBG_INFO, "sane_init: authorize %s null\n", authorize ? "!=" : "==");
+
+ DBG (DBG_FUNC, "sane_init: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+void
+sane_exit (void)
+{
+ DBG (DBG_FUNC, "sane_exit: start\n");
+
+ if (devlist != NULL)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ devlist = NULL;
+ DBG (DBG_FUNC, "sane_exit: exit\n");
+}
+
+SANE_Status
+sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only)
+{
+ SANE_Int dev_num;
+ DBG (DBG_FUNC, "sane_get_devices: start: local_only = %s\n",
+ local_only == SANE_TRUE ? "true" : "false");
+
+ if (devlist != NULL)
+ {
+ free (devlist);
+ devlist = NULL;
+ }
+
+ devlist = malloc ((num_devices + 1) * sizeof (devlist[0]));
+ if (devlist == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ dev_num = 0;
+ /* HOLD: This is ugly (only one scanner!) and should go to sane_init */
+ if (GetDeviceStatus ())
+ {
+ SANE_Device *sane_device;
+
+ sane_device = malloc (sizeof (*sane_device));
+ if (sane_device == NULL)
+ return SANE_STATUS_NO_MEM;
+ sane_device->name = strdup (device_name);
+ sane_device->vendor = strdup ("Mustek");
+ sane_device->model = strdup ("BearPaw 2448 TA Pro");
+ sane_device->type = strdup ("flatbed scanner");
+ devlist[dev_num++] = sane_device;
+ }
+ devlist[dev_num] = 0;
+ *device_list = devlist;
+ DBG (DBG_FUNC, "sane_get_devices: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_open (SANE_String_Const devicename, SANE_Handle * handle)
+{
+ Mustek_Scanner *s;
+
+ DBG (DBG_FUNC, "sane_open: start :devicename = %s\n", devicename);
+
+ if (!MustScanner_Init ())
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (!PowerControl (SANE_FALSE, SANE_FALSE))
+ {
+ return SANE_STATUS_INVAL;
+ }
+ if (!CarriageHome ())
+ {
+ return SANE_STATUS_INVAL;
+ }
+
+ s = malloc (sizeof (*s));
+ if (s == NULL)
+ return SANE_STATUS_NO_MEM;
+ memset (s, 0, sizeof (*s));
+
+ s->gamma_table = NULL;
+ memcpy (&s->model, &mustek_A2nu2_model, sizeof (Scanner_Model));
+ s->next = NULL;
+ s->bIsScanning = SANE_FALSE;
+ s->bIsReading = SANE_FALSE;
+
+ init_options (s);
+ *handle = s;
+
+ s->read_rows = 0;
+ s->scan_buffer_len = 0;
+
+ DBG (DBG_FUNC, "sane_open: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+
+void
+sane_close (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_close: start\n");
+
+ PowerControl (SANE_FALSE, SANE_FALSE);
+
+ CarriageHome ();
+
+ if (NULL != g_pDeviceFile)
+ {
+ free (g_pDeviceFile);
+ g_pDeviceFile = NULL;
+ }
+
+ if (s->Scan_data_buf != NULL)
+ free (s->Scan_data_buf);
+
+ s->Scan_data_buf = NULL;
+
+ free (handle);
+
+ DBG (DBG_FUNC, "sane_close: exit\n");
+}
+
+
+const SANE_Option_Descriptor *
+sane_get_option_descriptor (SANE_Handle handle, SANE_Int option)
+{
+ Mustek_Scanner *s = handle;
+
+ if ((unsigned) option >= NUM_OPTIONS)
+ return 0;
+ DBG (DBG_FUNC, "sane_get_option_descriptor: option = %s (%d)\n",
+ s->opt[option].name, option);
+ return s->opt + option;
+}
+
+
+SANE_Status
+sane_control_option (SANE_Handle handle, SANE_Int option,
+ SANE_Action action, void *val, SANE_Int * info)
+{
+ Mustek_Scanner *s = handle;
+ SANE_Status status;
+ SANE_Word cap;
+ SANE_Int myinfo = 0;
+
+ DBG (DBG_FUNC,
+ "sane_control_option: start: action = %s, option = %s (%d)\n",
+ (action == SANE_ACTION_GET_VALUE) ? "get" : (action ==
+ SANE_ACTION_SET_VALUE) ?
+ "set" : (action == SANE_ACTION_SET_AUTO) ? "set_auto" : "unknown",
+ s->opt[option].name, option);
+
+
+ if (info)
+ *info = 0;
+
+ if (s->bIsScanning)
+ {
+ DBG (DBG_ERR, "sane_control_option: don't call this function while "
+ "scanning\n");
+ return SANE_STATUS_DEVICE_BUSY;
+ }
+ if (option >= NUM_OPTIONS || option < 0)
+ {
+ DBG (DBG_ERR,
+ "sane_control_option: option %d >= NUM_OPTIONS || option < 0\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ cap = s->opt[option].cap;
+ if (!SANE_OPTION_IS_ACTIVE (cap))
+ {
+ DBG (DBG_ERR, "sane_control_option: option %d is inactive\n", option);
+ return SANE_STATUS_INVAL;
+ }
+ if (action == SANE_ACTION_GET_VALUE)
+ {
+ switch (option)
+ {
+ /* word options: */
+ case OPT_NUM_OPTS:
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_AUTO_WARMUP:
+ case OPT_GAMMA_VALUE:
+ case OPT_THRESHOLD:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ *(SANE_Word *) val = s->val[option].w;
+ break;
+ /* string options: */
+ case OPT_MODE:
+ strcpy (val, s->val[option].s);
+ break;
+
+ case OPT_SOURCE:
+ strcpy (val, s->val[option].s);
+ break;
+ default:
+ DBG (DBG_ERR, "sane_control_option: can't get unknown option %d\n",
+ option);
+ ;
+ }
+ }
+ else if (action == SANE_ACTION_SET_VALUE)
+ {
+ if (!SANE_OPTION_IS_SETTABLE (cap))
+ {
+ DBG (DBG_ERR, "sane_control_option: option %d is not settable\n",
+ option);
+ return SANE_STATUS_INVAL;
+ }
+
+ status = sanei_constrain_value (s->opt + option, val, &myinfo);
+ if (status != SANE_STATUS_GOOD)
+ {
+ DBG (2, "sane_control_option: sanei_constrain_value returned %s\n",
+ sane_strstatus (status));
+ return status;
+ }
+
+ switch (option)
+ {
+ /* (mostly) side-effect-free word options: */
+ case OPT_RESOLUTION:
+ case OPT_PREVIEW:
+ case OPT_TL_X:
+ case OPT_TL_Y:
+ case OPT_BR_X:
+ case OPT_BR_Y:
+ s->val[option].w = *(SANE_Word *) val;
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS;
+ break;
+ case OPT_THRESHOLD:
+ case OPT_AUTO_WARMUP:
+ case OPT_GAMMA_VALUE:
+ s->val[option].w = *(SANE_Word *) val;
+ break;
+ /* side-effect-free word-array options: */
+ case OPT_MODE:
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, SANE_VALUE_SCAN_MODE_LINEART) == 0)
+ {
+ ENABLE (OPT_THRESHOLD);
+ }
+ else
+ {
+ DISABLE (OPT_THRESHOLD);
+ }
+ RIE (calc_parameters (s));
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ case OPT_SOURCE:
+ if (strcmp (s->val[option].s, val) != 0)
+ { /* something changed */
+ if (s->val[option].s)
+ free (s->val[option].s);
+ s->val[option].s = strdup (val);
+ if (strcmp (s->val[option].s, "Reflective") == 0)
+ {
+ PowerControl (SANE_TRUE, SANE_FALSE);
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size;
+ y_range.max = s->model.y_size;
+ }
+ else if (0 == strcmp (s->val[option].s, "Negative"))
+ {
+ PowerControl (SANE_FALSE, SANE_TRUE);
+ s->opt[OPT_MODE].size =
+ max_string_size (negative_mode_list);
+ s->opt[OPT_MODE].constraint.string_list =
+ negative_mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size_ta;
+ y_range.max = s->model.y_size_ta;
+ }
+ else if (0 == strcmp (s->val[option].s, "Positive"))
+ {
+ PowerControl (SANE_FALSE, SANE_TRUE);
+ s->opt[OPT_MODE].size = max_string_size (mode_list);
+ s->opt[OPT_MODE].constraint.string_list = mode_list;
+ s->val[OPT_MODE].s = strdup ("Color24");
+ x_range.max = s->model.x_size_ta;
+ y_range.max = s->model.y_size_ta;
+ }
+ }
+ myinfo |= SANE_INFO_RELOAD_PARAMS | SANE_INFO_RELOAD_OPTIONS;
+ break;
+ default:
+ DBG (DBG_ERR, "sane_control_option: can't set unknown option %d\n",
+ option);
+ }
+ }
+ else
+ {
+ DBG (DBG_ERR, "sane_control_option: unknown action %d for option %d\n",
+ action, option);
+ return SANE_STATUS_INVAL;
+ }
+ if (info)
+ *info = myinfo;
+
+ DBG (DBG_FUNC, "sane_control_option: exit\n");
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_parameters (SANE_Handle handle, SANE_Parameters * params)
+{
+ Mustek_Scanner *s = handle;
+
+ DBG (DBG_FUNC, "sane_get_parameters: start\n");
+
+ DBG (DBG_INFO, "sane_get_parameters :params.format = %d\n",
+ s->params.format);
+
+ DBG (DBG_INFO, "sane_get_parameters :params.depth = %d\n", s->params.depth);
+ DBG (DBG_INFO, "sane_get_parameters :params.pixels_per_line = %d\n",
+ s->params.pixels_per_line);
+ DBG (DBG_INFO, "sane_get_parameters :params.bytes_per_line = %d\n",
+ s->params.bytes_per_line);
+ DBG (DBG_INFO, "sane_get_parameters :params.lines = %d\n", s->params.lines);
+ if (params != NULL)
+ *params = s->params;
+
+ DBG (DBG_FUNC, "sane_get_parameters: exit\n");
+
+ return SANE_STATUS_GOOD;
+
+}
+
+SANE_Status
+sane_start (SANE_Handle handle)
+{
+ int i;
+ Mustek_Scanner *s = handle;
+
+ DBG (DBG_FUNC, "sane_start: start\n");
+
+ s->scan_buffer_len = 0;
+
+ calc_parameters (s);
+
+ if (s->val[OPT_TL_X].w >= s->val[OPT_BR_X].w)
+ {
+ DBG (DBG_CRIT,
+ "sane_start: top left x >= bottom right x --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (s->val[OPT_TL_Y].w >= s->val[OPT_BR_Y].w)
+ {
+ DBG (DBG_CRIT,
+ "sane_start: top left y >= bottom right y --- exiting\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ s->setpara.pGammaTable = NULL;
+
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x1=%d\n",
+ s->setpara.fmArea.x1);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.x2=%d\n",
+ s->setpara.fmArea.x2);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y1=%d\n",
+ s->setpara.fmArea.y1);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.fmArea.y2=%d\n",
+ s->setpara.fmArea.y2);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.pfPixelFlavor=%d\n",
+ s->setpara.pfPixelFlavor);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.wLinearThreshold=%d\n",
+ s->setpara.wLinearThreshold);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.wTargetDPI=%d\n",
+ s->setpara.wTargetDPI);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.smScanMode=%d\n",
+ s->setpara.smScanMode);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.ssScanSource =%d\n",
+ s->setpara.ssScanSource);
+ DBG (DBG_INFO, "Sane_start:setpara ,setpara.pGammaTable =%p\n",
+ (void *) s->setpara.pGammaTable);
+
+ SetParameters (&s->setpara);
+
+ GetParameters (&s->getpara);
+
+ switch (s->params.format)
+ {
+ case SANE_FRAME_RGB:
+ if (s->params.depth == 8)
+
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 3;
+ if (s->params.depth == 16)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 6;
+
+
+ break;
+ case SANE_FRAME_GRAY:
+ if (s->params.depth == 1)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth * 8;
+ if (s->params.depth == 8)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth;
+ if (s->params.depth == 16)
+ s->params.pixels_per_line = s->getpara.dwLineByteWidth / 2;
+ break;
+ default:
+ DBG (DBG_INFO, "sane_start: sane_params.format = %d\n",
+ s->params.format);
+ }
+
+ s->params.bytes_per_line = s->getpara.dwLineByteWidth;
+ s->params.lines = s->getpara.dwLength;
+
+ s->params.last_frame = TRUE;
+
+
+ s->read_rows = s->getpara.dwLength;
+ DBG (DBG_INFO, "sane_start : read_rows = %d\n", s->read_rows);
+
+ /*warmming up */
+ if (s->val[OPT_AUTO_WARMUP].w)
+ {
+ for (i = 30; i > 0; i--)
+ {
+ sleep (1);
+ DBG (DBG_ERR, "warming up: %d\n", i);
+ }
+ }
+ DBG (DBG_INFO, "SCANNING ... \n");
+
+ s->bIsScanning = SANE_TRUE;
+ if (s->Scan_data_buf != NULL)
+ free (s->Scan_data_buf);
+ s->Scan_data_buf = NULL;
+
+ s->Scan_data_buf = malloc (SCAN_BUFFER_SIZE * sizeof (SANE_Byte));
+ if (s->Scan_data_buf == NULL)
+ return SANE_STATUS_NO_MEM;
+
+ StartScan ();
+
+ DBG (DBG_FUNC, "sane_start: exit\n");
+
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_read (SANE_Handle handle, SANE_Byte * buf, SANE_Int max_len,
+ SANE_Int * len)
+{
+
+ Mustek_Scanner *s = handle;
+ static SANE_Byte *tempbuf;
+ SANE_Int lines_to_read, lines_read;
+ IMAGEROWS image_row;
+
+ int maxbuffersize = max_len;
+
+ DBG (DBG_FUNC, "sane_read: start: max_len=%d\n", max_len);
+
+ if (s == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: handle is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (buf == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: buf is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+
+ if (len == NULL)
+ {
+ DBG (DBG_ERR, "sane_read: len is null!\n");
+ return SANE_STATUS_INVAL;
+ }
+ *len = 0;
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "sane_read: scan was cancelled, is over or has not been "
+ "initiated yet\n");
+ return SANE_STATUS_CANCELLED;
+ }
+ DBG (DBG_DBG, "sane_read: before read data read_row=%d\n", s->read_rows);
+ if (s->scan_buffer_len == 0)
+ {
+ if (s->read_rows > 0)
+ {
+ lines_to_read = SCAN_BUFFER_SIZE / s->getpara.dwLineByteWidth;
+
+ if (lines_to_read > s->read_rows)
+ lines_to_read = s->read_rows;
+
+ tempbuf =
+ (SANE_Byte *) malloc (sizeof (SANE_Byte) * lines_to_read *
+ s->getpara.dwLineByteWidth + 3 * 1024 + 1);
+ memset (tempbuf, 0,
+ sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
+ 3 * 1024 + 1);
+
+ DBG (DBG_INFO, "sane_read: buffer size is %ld\n",
+ (long int) sizeof (SANE_Byte) * lines_to_read * s->getpara.dwLineByteWidth +
+ 3 * 1024 + 1);
+
+ image_row.roRgbOrder = mustek_A2nu2_model.line_mode_color_order;
+ image_row.wWantedLineNum = lines_to_read;
+ image_row.pBuffer = (SANE_Byte *) tempbuf;
+ s->bIsReading = SANE_TRUE;
+
+ if (!ReadScannedData (&image_row))
+ {
+ DBG (DBG_ERR, "sane_read: ReadScannedData error\n");
+ s->bIsReading = SANE_FALSE;
+ return SANE_STATUS_INVAL;
+ }
+
+ DBG (DBG_DBG, "sane_read: Finish ReadScanedData\n");
+ s->bIsReading = SANE_FALSE;
+ memset (s->Scan_data_buf, 0, SCAN_BUFFER_SIZE);
+ s->scan_buffer_len =
+ image_row.wXferedLineNum * s->getpara.dwLineByteWidth;
+ DBG (DBG_INFO, "sane_read : s->scan_buffer_len = %ld\n",
+ (long int) s->scan_buffer_len);
+
+ memcpy (s->Scan_data_buf, tempbuf, s->scan_buffer_len);
+
+ DBG (DBG_DBG, "sane_read :after memcpy\n");
+ free (tempbuf);
+ s->Scan_data_buf_start = s->Scan_data_buf;
+ s->read_rows -= image_row.wXferedLineNum;
+
+ }
+ else
+ {
+ DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
+ sane_cancel (handle);
+ return SANE_STATUS_EOF;
+ }
+ }
+ if (s->scan_buffer_len == 0)
+ {
+ DBG (DBG_FUNC, "sane_read: scan finished -- exit\n");
+ sane_cancel (handle);
+ return SANE_STATUS_EOF;
+ }
+
+
+
+
+ lines_read =
+ (maxbuffersize <
+ (SANE_Int) s->scan_buffer_len) ? maxbuffersize : (SANE_Int) s->scan_buffer_len;
+ DBG (DBG_DBG, "sane_read: after %d\n", lines_read);
+
+ *len = (SANE_Int) lines_read;
+
+ DBG (DBG_INFO, "sane_read : get lines_read = %d\n", lines_read);
+ DBG (DBG_INFO, "sane_read : get *len = %d\n", *len);
+ memcpy (buf, s->Scan_data_buf_start, lines_read);
+
+ s->scan_buffer_len -= lines_read;
+ s->Scan_data_buf_start += lines_read;
+ DBG (DBG_FUNC, "sane_read: exit\n");
+ return SANE_STATUS_GOOD;
+
+}
+
+void
+sane_cancel (SANE_Handle handle)
+{
+ Mustek_Scanner *s = handle;
+ int i;
+ DBG (DBG_FUNC, "sane_cancel: start\n");
+ if (s->bIsScanning)
+ {
+ s->bIsScanning = SANE_FALSE;
+ if (s->read_rows > 0)
+ {
+ DBG (DBG_INFO, "sane_cancel: warning: is scanning\n");
+
+ }
+ else
+ {
+ DBG (DBG_INFO, "sane_cancel: Scan finished\n");
+ }
+
+ StopScan ();
+
+ CarriageHome ();
+ for (i = 0; i < 20; i++)
+ {
+ if (s->bIsReading == SANE_FALSE)
+ {
+ if (s->gamma_table != NULL)
+ {
+ free (s->gamma_table);
+ s->gamma_table = NULL;
+ break;
+ }
+ }
+ else
+ sleep (1);
+ }
+ if (s->Scan_data_buf != NULL)
+ {
+ free (s->Scan_data_buf);
+ s->Scan_data_buf = NULL;
+ s->Scan_data_buf_start = NULL;
+ }
+
+ s->read_rows = 0;
+ s->scan_buffer_len = 0;
+ memset (&s->setpara, 0, sizeof (s->setpara));
+ memset (&s->getpara, 0, sizeof (s->getpara));
+
+ }
+ else
+ {
+ DBG (DBG_INFO, "sane_cancel: do nothing\n");
+ }
+
+
+ DBG (DBG_FUNC, "sane_cancel: exit\n");
+
+}
+
+SANE_Status
+sane_set_io_mode (SANE_Handle handle, SANE_Bool non_blocking)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_set_io_mode: handle = %p, non_blocking = %s\n",
+ handle, non_blocking == SANE_TRUE ? "true" : "false");
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "sane_set_io_mode: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ if (non_blocking)
+ return SANE_STATUS_UNSUPPORTED;
+ return SANE_STATUS_GOOD;
+}
+
+SANE_Status
+sane_get_select_fd (SANE_Handle handle, SANE_Int * fd)
+{
+ Mustek_Scanner *s = handle;
+ DBG (DBG_FUNC, "sane_get_select_fd: handle = %p, fd = %p\n", handle,
+ (void *) fd);
+ if (!s->bIsScanning)
+ {
+ DBG (DBG_WARN, "%s", "sane_get_select_fd: not scanning\n");
+ return SANE_STATUS_INVAL;
+ }
+ return SANE_STATUS_UNSUPPORTED;
+}