summaryrefslogtreecommitdiff
path: root/backend/snapscan-sources.c
diff options
context:
space:
mode:
Diffstat (limited to 'backend/snapscan-sources.c')
-rw-r--r--backend/snapscan-sources.c1385
1 files changed, 1385 insertions, 0 deletions
diff --git a/backend/snapscan-sources.c b/backend/snapscan-sources.c
new file mode 100644
index 0000000..d91060e
--- /dev/null
+++ b/backend/snapscan-sources.c
@@ -0,0 +1,1385 @@
+/* sane - Scanner Access Now Easy.
+
+ Copyright (C) 1997, 1998, 2002, 2013 Franck Schnefra, Michel Roelofs,
+ Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
+ Petter Reinholdtsen, Gary Plewa, Sebastien Sable, Max Ushakov,
+ Andrew Goodbody, Oliver Schwartz and Kevin Charter
+
+ 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 is a component of the implementation of a backend for many
+ of the the AGFA SnapScan and Acer Vuego/Prisa flatbed scanners. */
+
+
+/* $Id$
+ SnapScan backend data sources (implementation) */
+
+/**************************************************************************************
+If you get confused from all the structs (like I did when I first saw them),
+think of it as "C++ in C". If you're accustomed to OO and UML maybe the
+following diagram helps you to make sense of it:
+
+ ------------------------
+ ! Source !
+ ------------------------
+ !pss: SnapScan_Scanner*!
+ ------------------------ +psub
+ !init() = 0 !-----------------
+ !remaining() = 0 ! !
+ !bytesPerLine() ! !
+ !pixelsPerLine() ! !
+ !get() = 0 ! !{TransformerSource forwards
+ !done() = 0 ! ! function calls to corres-
+ ------------------------ ! ponding functions in psub}
+ ^ !
+ /_\ !
+ ! !
+ -------------------------------------------------- /\
+ ! ! ! ! \/
+------------- ------------- ------------- -------------------
+!SCSISource ! ! FDSource ! !BufSource ! !TransformerSource!
+============= ============= ============= ===================
+!remaining()! !remaining()! !remaining()! !init() !
+!get() ! !get() ! !get() ! !remaining() !
+!done() ! !done() ! !done() ! !bytesPerLine() !
+!init() ! !init() ! !init() ! !pixelsPerLine() !
+------------- ------------- ------------- !get() !
+ !done() !
+ -------------------
+ ^
+ /_\
+ !
+ ------------------------------------
+ ! ! !
+ ---------------- ------------- -------------
+ ! Expander ! ! RGBRouter ! ! Inverter !
+ ================ ============= =============
+ !remaining() ! !remaining()! !remaining()!
+ !bytesPerLine()! !get() ! !get() !
+ !get() ! !done() ! !done() !
+ !done() ! !init() ! !init() !
+ !init() ! ------------- -------------
+ ----------------
+All instances of the descendants of TransformerSource can be chained together. For
+color scanning, a typical source chain would consist of an RGBRouter sitting on top
+of a SCSISource. In the get() method, RGBRouter will then call the get() method of
+the subsource, process the data and return it.
+
+I hope this makes sense to you (and I got the right idea of the original author's
+intention).
+***********************************************************************************/
+
+#ifndef __FUNCTION__
+#define __FUNCTION__ "(undef)"
+#endif
+
+static SANE_Status Source_init (Source *pself,
+ SnapScan_Scanner *pss,
+ SourceRemaining remaining,
+ SourceBytesPerLine bytesPerLine,
+ SourcePixelsPerLine pixelsPerLine,
+ SourceGet get,
+ SourceDone done)
+{
+ pself->pss = pss;
+ pself->remaining = remaining;
+ pself->bytesPerLine = bytesPerLine;
+ pself->pixelsPerLine = pixelsPerLine;
+ pself->get = get;
+ pself->done = done;
+ return SANE_STATUS_GOOD;
+}
+
+/* these are defaults, normally used only by base sources */
+
+static SANE_Int Source_bytesPerLine (Source *pself)
+{
+ return pself->pss->bytes_per_line;
+}
+
+static SANE_Int Source_pixelsPerLine (Source *pself)
+{
+ return pself->pss->pixels_per_line;
+}
+
+/**********************************************************************/
+
+/* the base sources */
+typedef enum
+{
+ SCSI_SRC,
+ FD_SRC,
+ BUF_SRC
+} BaseSourceType;
+
+
+typedef struct
+{
+ SOURCE_GUTS;
+ SANE_Int scsi_buf_pos; /* current position in scsi buffer */
+ SANE_Int scsi_buf_max; /* data limit */
+ SANE_Int absolute_max; /* largest possible data read */
+} SCSISource;
+
+static SANE_Int SCSISource_remaining (Source *pself)
+{
+ SCSISource *ps = (SCSISource *) pself;
+ return ps->pss->bytes_remaining + (ps->scsi_buf_max - ps->scsi_buf_pos);
+}
+
+static SANE_Status SCSISource_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ SCSISource *ps = (SCSISource *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ char* me = "SCSISource_get";
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ while (remaining > 0
+ && pself->remaining(pself) > 0
+ && status == SANE_STATUS_GOOD
+ && !cancelRead)
+ {
+ SANE_Int ndata = ps->scsi_buf_max - ps->scsi_buf_pos;
+ DBG (DL_DATA_TRACE, "%s: ndata %d; remaining %d\n", me, ndata, remaining);
+ if (ndata == 0)
+ {
+ ps->pss->expected_read_bytes = MIN((size_t)ps->absolute_max,
+ ps->pss->bytes_remaining);
+ ps->scsi_buf_pos = 0;
+ ps->scsi_buf_max = 0;
+ status = scsi_read (ps->pss, READ_IMAGE);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ ps->scsi_buf_max = ps->pss->read_bytes;
+ ndata = ps->pss->read_bytes;
+ ps->pss->bytes_remaining -= ps->pss->read_bytes;
+ DBG (DL_DATA_TRACE, "%s: pos: %d; max: %d; expected: %lu; read: %lu\n",
+ me, ps->scsi_buf_pos, ps->scsi_buf_max, (u_long) ps->pss->expected_read_bytes,
+ (u_long) ps->pss->read_bytes);
+ }
+ ndata = MIN(ndata, remaining);
+ memcpy (pbuf, ps->pss->buf + ps->scsi_buf_pos, (size_t)ndata);
+ pbuf += ndata;
+ ps->scsi_buf_pos += ndata;
+ remaining -= ndata;
+ }
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status SCSISource_done (Source *pself)
+{
+ DBG(DL_MINOR_INFO, "SCSISource_done\n");
+ UNREFERENCED_PARAMETER(pself);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status SCSISource_init (SCSISource *pself, SnapScan_Scanner *pss)
+{
+ SANE_Status status = Source_init ((Source *) pself, pss,
+ SCSISource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ SCSISource_get,
+ SCSISource_done);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->scsi_buf_max = 0;
+ pself->scsi_buf_pos = 0;
+ pself->absolute_max =
+ (pss->phys_buf_sz/pss->bytes_per_line)*pss->bytes_per_line;
+ }
+ return status;
+}
+
+/* File sources */
+
+typedef struct
+{
+ SOURCE_GUTS;
+ int fd;
+ SANE_Int bytes_remaining;
+} FDSource;
+
+static SANE_Int FDSource_remaining (Source *pself)
+{
+ FDSource *ps = (FDSource *) pself;
+ return ps->bytes_remaining;
+}
+
+static SANE_Status FDSource_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ FDSource *ps = (FDSource *) pself;
+ SANE_Int remaining = *plen;
+
+ while (remaining > 0
+ && pself->remaining(pself) > 0
+ && status == SANE_STATUS_GOOD)
+ {
+ SANE_Int bytes_read = read (ps->fd, pbuf, remaining);
+ if (bytes_read == -1)
+ {
+ if (errno == EAGAIN)
+ {
+ /* No data currently available */
+ break;
+ }
+ /* It's an IO error */
+ DBG (DL_MAJOR_ERROR, "%s: read failed: %s\n",
+ __FUNCTION__, strerror(errno));
+ status = SANE_STATUS_IO_ERROR;
+ }
+ else if (bytes_read == 0)
+ {
+ /* EOF of current reading */
+ DBG(DL_DATA_TRACE, "%s: EOF\n",__FUNCTION__);
+ break;
+ }
+ ps->bytes_remaining -= bytes_read;
+ remaining -= bytes_read;
+ pbuf += bytes_read;
+ }
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status FDSource_done (Source *pself)
+{
+ close(((FDSource *) pself)->fd);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status FDSource_init (FDSource *pself,
+ SnapScan_Scanner *pss,
+ int fd)
+{
+ SANE_Status status = Source_init ((Source *) pself,
+ pss,
+ FDSource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ FDSource_get,
+ FDSource_done);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->fd = fd;
+ pself->bytes_remaining = pss->bytes_per_line * (pss->lines + pss->chroma);
+ }
+ return status;
+}
+
+
+/* buffer sources simply read from a pre-filled buffer; we have these
+ so that we can include source chain processing overhead in the
+ measure_transfer_rate() function */
+
+typedef struct
+{
+ SOURCE_GUTS;
+ SANE_Byte *buf;
+ SANE_Int buf_size;
+ SANE_Int buf_pos;
+} BufSource;
+
+static SANE_Int BufSource_remaining (Source *pself)
+{
+ BufSource *ps = (BufSource *) pself;
+ return ps->buf_size - ps->buf_pos;
+}
+
+static SANE_Status BufSource_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ BufSource *ps = (BufSource *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int to_move = MIN(*plen, pself->remaining(pself));
+ if (to_move == 0)
+ {
+ status = SANE_STATUS_EOF;
+ }
+ else
+ {
+ memcpy (pbuf, ps->buf + ps->buf_pos, to_move);
+ ps->buf_pos += to_move;
+ *plen = to_move;
+ }
+ return status;
+}
+
+static SANE_Status BufSource_done (Source *pself)
+{
+ UNREFERENCED_PARAMETER(pself);
+ return SANE_STATUS_GOOD;
+}
+
+static SANE_Status BufSource_init (BufSource *pself,
+ SnapScan_Scanner *pss,
+ SANE_Byte *buf,
+ SANE_Int buf_size)
+{
+ SANE_Status status = Source_init ((Source *) pself,
+ pss,
+ BufSource_remaining,
+ Source_bytesPerLine,
+ Source_pixelsPerLine,
+ BufSource_get,
+ BufSource_done);
+ DBG(DL_DATA_TRACE, "BufSource_init: buf_size=%d\n", buf_size);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->buf = buf;
+ pself->buf_size = buf_size;
+ pself->buf_pos = 0;
+ }
+ return status;
+}
+
+/* base source creation */
+
+static SANE_Status create_base_source (SnapScan_Scanner *pss,
+ BaseSourceType st,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = NULL;
+ switch (st)
+ {
+ case SCSI_SRC:
+ *pps = (Source *) malloc(sizeof(SCSISource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate SCSISource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = SCSISource_init ((SCSISource *) *pps, pss);
+ }
+ break;
+ case FD_SRC:
+ *pps = (Source *) malloc(sizeof(FDSource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate FDSource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = FDSource_init ((FDSource *) *pps, pss, pss->rpipe[0]);
+ }
+ break;
+ case BUF_SRC:
+ *pps = (Source *) malloc(sizeof(BufSource));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "failed to allocate BufSource");
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = BufSource_init ((BufSource *) *pps,
+ pss,
+ pss->buf,
+ pss->read_bytes);
+ }
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "illegal base source type %d", st);
+ break;
+ }
+ return status;
+}
+
+/**********************************************************************/
+
+/* The transformer sources */
+
+#define TX_SOURCE_GUTS \
+ SOURCE_GUTS;\
+ Source *psub /* sub-source */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+} TxSource;
+
+static SANE_Int TxSource_remaining (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->remaining(ps->psub);
+}
+
+static SANE_Int TxSource_bytesPerLine (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->bytesPerLine(ps->psub);
+}
+
+static SANE_Int TxSource_pixelsPerLine (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->pixelsPerLine(ps->psub);
+}
+
+static SANE_Status TxSource_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ TxSource *ps = (TxSource *) pself;
+ return ps->psub->get(ps->psub, pbuf, plen);
+}
+
+static SANE_Status TxSource_done (Source *pself)
+{
+ TxSource *ps = (TxSource *) pself;
+ SANE_Status status = ps->psub->done(ps->psub);
+ free(ps->psub);
+ ps->psub = NULL;
+ return status;
+}
+
+static SANE_Status TxSource_init (TxSource *pself,
+ SnapScan_Scanner *pss,
+ SourceRemaining remaining,
+ SourceBytesPerLine bytesPerLine,
+ SourcePixelsPerLine pixelsPerLine,
+ SourceGet get,
+ SourceDone done,
+ Source *psub)
+{
+ SANE_Status status = Source_init((Source *) pself,
+ pss,
+ remaining,
+ bytesPerLine,
+ pixelsPerLine,
+ get,
+ done);
+ if (status == SANE_STATUS_GOOD)
+ pself->psub = psub;
+ return status;
+}
+
+/* The expander makes three-channel, one-bit, raw scanner data into
+ 8-bit data. It is used to support the bilevel colour scanning mode */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *ch_buf; /* channel buffer */
+ SANE_Int ch_size; /* channel buffer size = #bytes in a channel */
+ SANE_Int ch_ndata; /* actual #bytes in channel buffer */
+ SANE_Int ch_pos; /* position in buffer */
+ SANE_Int bit; /* current bit */
+ SANE_Int last_bit; /* current last bit (counting down) */
+ SANE_Int last_last_bit; /* last bit in the last byte of the channel */
+} Expander;
+
+static SANE_Int Expander_remaining (Source *pself)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Int sub_remaining = TxSource_remaining(pself);
+ SANE_Int sub_bits_per_channel = TxSource_pixelsPerLine(pself);
+ SANE_Int whole_channels = sub_remaining/ps->ch_size;
+ SANE_Int result = whole_channels*sub_bits_per_channel;
+
+ if (ps->ch_pos < ps->ch_size)
+ {
+ SANE_Int bits_covered = MAX((ps->ch_pos - 1)*8, 0) + 7 - ps->bit;
+ result += sub_bits_per_channel - bits_covered;
+ }
+
+ return result;
+}
+
+static SANE_Int Expander_bytesPerLine (Source *pself)
+{
+ return TxSource_pixelsPerLine(pself)*3;
+}
+
+static SANE_Status Expander_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+
+ while (remaining > 0
+ &&
+ pself->remaining(pself) > 0 &&
+ !cancelRead)
+ {
+ if (ps->ch_pos == ps->ch_ndata)
+ {
+ /* we need more data; try to get the remainder of the current
+ channel, or else the next channel */
+ SANE_Int ndata = ps->ch_size - ps->ch_ndata;
+ if (ndata == 0)
+ {
+ ps->ch_ndata = 0;
+ ps->ch_pos = 0;
+ ndata = ps->ch_size;
+ }
+ status = TxSource_get(pself, ps->ch_buf + ps->ch_pos, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ if (ndata == 0)
+ break;
+ ps->ch_ndata += ndata;
+ if (ps->ch_pos == (ps->ch_size - 1))
+ ps->last_bit = ps->last_last_bit;
+ else
+ ps->last_bit = 0;
+ ps->bit = 7;
+ }
+ *pbuf = ((ps->ch_buf[ps->ch_pos] >> ps->bit) & 0x01) ? 0xFF : 0x00;
+ pbuf++;
+ remaining--;
+
+ if (ps->bit == ps->last_bit)
+ {
+ ps->bit = 7;
+ ps->ch_pos++;
+ if (ps->ch_pos == (ps->ch_size - 1))
+ ps->last_bit = ps->last_last_bit;
+ else
+ ps->last_bit = 0;
+ }
+ else
+ {
+ ps->bit--;
+ }
+ }
+
+ *plen -= remaining;
+ return status;
+}
+
+static SANE_Status Expander_done (Source *pself)
+{
+ Expander *ps = (Expander *) pself;
+ SANE_Status status = TxSource_done(pself);
+ free(ps->ch_buf);
+ ps->ch_buf = NULL;
+ ps->ch_size = 0;
+ ps->ch_pos = 0;
+ return status;
+}
+
+static SANE_Status Expander_init (Expander *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ Expander_remaining,
+ Expander_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Expander_get,
+ Expander_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->ch_size = TxSource_bytesPerLine((Source *) pself)/3;
+ pself->ch_buf = (SANE_Byte *) malloc(pself->ch_size);
+ if (pself->ch_buf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: couldn't allocate channel buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ pself->ch_ndata = 0;
+ pself->ch_pos = 0;
+ pself->last_last_bit = pself->pixelsPerLine((Source *) pself)%8;
+ if (pself->last_last_bit == 0)
+ pself->last_last_bit = 7;
+ pself->last_last_bit = 7 - pself->last_last_bit;
+ pself->bit = 7;
+ if (pself->ch_size > 1)
+ pself->last_bit = 0;
+ else
+ pself->last_bit = pself->last_last_bit;
+ }
+ }
+ return status;
+}
+
+static SANE_Status create_Expander (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Expander));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate Expander.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Expander_init ((Expander *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/*
+ This filter implements a fix for scanners that have some columns
+ of pixels offset. Currently it only shifts every other column
+ starting with the first one down ch_offset pixels.
+
+ The Deinterlacer detects if data is in SANE RGB frame format (3 bytes/pixel)
+ or in Grayscale (1 byte/pixel).
+
+ The first ch_offset lines of data in the output are fudged so that even indexed
+ add odd indexed pixels will have the same value. This is necessary because
+ the real pixel values of the columns that are shifted down are not
+ in the data for the first ch_offset lines. A better way to handle this would be to
+ scan in ch_offset extra lines of data, but I haven't figured out how to do this
+ yet.
+
+*/
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *ch_buf; /* channel buffer */
+ SANE_Int ch_size; /* channel buffer size */
+ SANE_Int ch_line_size; /* size of one line */
+ SANE_Int ch_ndata; /* actual #bytes in channel buffer */
+ SANE_Int ch_pos; /* position in buffer */
+ SANE_Int ch_bytes_per_pixel;
+ SANE_Bool ch_lineart;
+ SANE_Int ch_offset; /* The number of lines to be shifted */
+ SANE_Bool ch_past_init; /* flag indicating if we have enough data to shift pixels down */
+ SANE_Bool ch_shift_even; /* flag indicating wether even or odd pixels are shifted */
+} Deinterlacer;
+
+static SANE_Int Deinterlacer_remaining (Source *pself)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Int result = TxSource_remaining(pself);
+ result += ps->ch_ndata - ps->ch_pos;
+ return result;
+}
+
+static SANE_Status Deinterlacer_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ SANE_Int org_len = *plen;
+ char *me = "Deinterlacer_get";
+
+ DBG(DL_DATA_TRACE, "%s: remaining=%d, pself->remaining=%d, ch_ndata=%d, ch_pos=%d\n",
+ me, remaining, pself->remaining(pself), ps->ch_ndata, ps->ch_pos);
+
+ while (remaining > 0
+ &&
+ pself->remaining(pself) > 0 &&
+ !cancelRead)
+ {
+ if (ps->ch_pos % (ps->ch_line_size) == ps->ch_ndata % (ps->ch_line_size) )
+ {
+ /* we need more data; try to get the remainder of the current
+ line, or else the next line */
+ SANE_Int ndata = (ps->ch_line_size) - ps->ch_ndata % (ps->ch_line_size);
+ if (ps->ch_pos >= ps->ch_size)
+ {
+ /* wrap to the beginning of the buffer if we need to */
+ ps->ch_ndata = 0;
+ ps->ch_pos = 0;
+ ndata = ps->ch_line_size;
+ }
+ status = TxSource_get(pself, ps->ch_buf + ps->ch_pos, &ndata);
+ if (status != SANE_STATUS_GOOD)
+ break;
+ if (ndata == 0)
+ break;
+ ps->ch_ndata += ndata;
+ }
+ /* Handle special lineart mode: Valid pixels need to be masked */
+ if (ps->ch_lineart)
+ {
+ if (ps->ch_past_init)
+ {
+ if (ps->ch_shift_even)
+ {
+ /* Even columns need to be shifted, i.e. bits 1,3,5,7 -> 0xaa */
+ /* use valid pixels from this line and shifted pixels from ch_size lines back */
+ *pbuf = (ps->ch_buf[ps->ch_pos] & 0x55) |
+ (ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size] & 0xaa);
+ }
+ else
+ {
+ /* Odd columns need to be shifted, i.e. bits 0,2,4,6 -> 0x55 */
+ *pbuf = (ps->ch_buf[ps->ch_pos] & 0xaa) |
+ (ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size] & 0x55);
+ }
+ }
+ else
+ {
+ /* not enough data. duplicate pixel values from previous column */
+ if (ps->ch_shift_even)
+ {
+ /* bits 0,2,4,6 contain valid data -> 0x55 */
+ SANE_Byte valid_pixel = ps->ch_buf[ps->ch_pos] & 0x55;
+ *pbuf = valid_pixel | (valid_pixel >> 1);
+ }
+ else
+ {
+
+ /* bits 1,3,5,7 contain valid data -> 0xaa */
+ SANE_Byte valid_pixel = ps->ch_buf[ps->ch_pos] & 0xaa;
+ *pbuf = valid_pixel | (valid_pixel << 1);
+ }
+ }
+ }
+ else /* colour / grayscale mode */
+ {
+ if ((ps->ch_shift_even && ((ps->ch_pos/ps->ch_bytes_per_pixel) % 2 == 0)) ||
+ (!ps->ch_shift_even && ((ps->ch_pos/ps->ch_bytes_per_pixel) % 2 == 1)))
+ {
+ /* the even indexed pixels need to be shifted down */
+ if (ps->ch_past_init){
+ /* We need to use data 4 lines back */
+ /* So we just go one forward and it will wrap around to 4 back. */
+ *pbuf = ps->ch_buf[(ps->ch_pos + (ps->ch_line_size)) % ps->ch_size];
+ }else{
+ /* Use data from the next pixel for even indexed pixels
+ if we are on the first few lines.
+ TODO: also we will overread the buffer if the buffer read ended
+ on the first pixel. */
+ if (ps->ch_pos % (ps->ch_line_size) == 0 )
+ *pbuf = ps->ch_buf[ps->ch_pos+ps->ch_bytes_per_pixel];
+ else
+ *pbuf = ps->ch_buf[ps->ch_pos-ps->ch_bytes_per_pixel];
+ }
+ }else{
+ /* odd indexed pixels are okay */
+ *pbuf = ps->ch_buf[ps->ch_pos];
+ }
+ }
+ /* set the flag so we know we have enough data to start shifting columns */
+ if (ps->ch_pos >= ps->ch_line_size * ps->ch_offset)
+ ps->ch_past_init = SANE_TRUE;
+
+ pbuf++;
+ remaining--;
+ ps->ch_pos++;
+ }
+
+ *plen -= remaining;
+
+ DBG(DL_DATA_TRACE,
+ "%s: Request=%d, remaining=%d, read=%d, TXSource_rem=%d, bytes_rem=%lu\n",
+ me,
+ org_len,
+ pself->remaining(pself),
+ *plen,
+ TxSource_remaining(pself),
+ (u_long) ps->pss->bytes_remaining);
+ return status;
+}
+
+static SANE_Status Deinterlacer_done (Source *pself)
+{
+ Deinterlacer *ps = (Deinterlacer *) pself;
+ SANE_Status status = TxSource_done(pself);
+ free(ps->ch_buf);
+ ps->ch_buf = NULL;
+ ps->ch_size = 0;
+ ps->ch_line_size = 0;
+ ps->ch_pos = 0;
+ return status;
+}
+
+static SANE_Status Deinterlacer_init (Deinterlacer *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ Deinterlacer_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Deinterlacer_get,
+ Deinterlacer_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ pself->ch_shift_even = SANE_TRUE;
+ switch (pss->pdev->model)
+ {
+ case PERFECTION3490:
+ pself->ch_offset = 8;
+ if ((actual_mode(pss) == MD_GREYSCALE) || (actual_mode(pss) == MD_LINEART))
+ pself->ch_shift_even = SANE_FALSE;
+ break;
+ case PERFECTION2480:
+ default:
+ pself->ch_offset = 4;
+ break;
+ }
+ pself->ch_line_size = TxSource_bytesPerLine((Source *) pself);
+ /* We need at least ch_offset+1 lines of buffer in order
+ to shift up ch_offset pixels. */
+ pself->ch_size = pself->ch_line_size * (pself->ch_offset + 1);
+ pself->ch_buf = (SANE_Byte *) malloc(pself->ch_size);
+ if (pself->ch_buf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: couldn't allocate channel buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ pself->ch_ndata = 0;
+ pself->ch_pos = 0;
+ pself->ch_past_init = SANE_FALSE;
+ if ((actual_mode(pss) == MD_GREYSCALE) || (actual_mode(pss) == MD_LINEART))
+ pself->ch_bytes_per_pixel = 1;
+ else
+ pself->ch_bytes_per_pixel = 3;
+ if (pss->bpp_scan == 16)
+ pself->ch_bytes_per_pixel *= 2;
+ }
+ pself->ch_lineart = (actual_mode(pss) == MD_LINEART);
+ }
+ return status;
+}
+
+static SANE_Status create_Deinterlacer (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Deinterlacer));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate Deinterlacer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Deinterlacer_init ((Deinterlacer *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* ----------------------------------------------------- */
+
+/* the RGB router assumes 8-bit RGB data arranged in contiguous
+ channels, possibly with R-G and R-B offsets, and rearranges the
+ data into SANE RGB frame format */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+ SANE_Byte *cbuf; /* circular line buffer */
+ SANE_Byte *xbuf; /* single line buffer */
+ SANE_Int pos; /* current position in xbuf */
+ SANE_Int cb_size; /* size of the circular buffer */
+ SANE_Int cb_line_size;/* size of a line in the circular buffer */
+ SANE_Int cb_start; /* start of valid data in the circular buffer */
+ SANE_Int cb_finish; /* finish of valid data, for next read */
+ SANE_Int ch_offset[3];/* offset in cbuf */
+ SANE_Int round_req;
+ SANE_Int round_read;
+} RGBRouter;
+
+static void put_int16r (int n, u_char *p)
+{
+ p[0] = (n & 0x00ff);
+ p[1] = (n & 0xff00) >> 8;
+}
+
+
+static SANE_Int RGBRouter_remaining (Source *pself)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Int remaining;
+ if (ps->round_req == ps->cb_size)
+ remaining = TxSource_remaining(pself) - ps->cb_size + ps->cb_line_size;
+ else
+ remaining = TxSource_remaining(pself) + ps->cb_line_size - ps->pos;
+ return (remaining);
+}
+
+static SANE_Status RGBRouter_get (Source *pself,
+ SANE_Byte *pbuf,
+ SANE_Int *plen)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Status status = SANE_STATUS_GOOD;
+ SANE_Int remaining = *plen;
+ SANE_Byte *s;
+ SANE_Int i, t;
+ SANE_Int r, g, b;
+ SANE_Int run_req;
+ SANE_Int org_len = *plen;
+ char *me = "RGBRouter_get";
+
+ while (remaining > 0 && pself->remaining(pself) > 0 && !cancelRead)
+ {
+ DBG(DL_DATA_TRACE, "%s: remaining=%d, pself->remaining=%d, round_req=%d, cb_size=%d\n",
+ me, remaining, pself->remaining(pself), ps->round_req, ps->cb_size);
+ /* Check if there is no valid data left from previous get */
+ if (ps->pos >= ps->cb_line_size)
+ {
+ /* Try to get more data. either one line or
+ full buffer (first time) */
+ do
+ {
+ run_req = ps->round_req - ps->round_read;
+ status = TxSource_get (pself,
+ ps->cbuf + ps->cb_start + ps->round_read,
+ &run_req);
+ if (status != SANE_STATUS_GOOD || run_req==0)
+ {
+ *plen -= remaining;
+ if ( *plen > 0 )
+ DBG(DL_DATA_TRACE, "%s: request=%d, read=%d\n",
+ me, org_len, *plen);
+ return status;
+ }
+ ps->round_read += run_req;
+ }
+ while ((ps->round_req > ps->round_read) && !cancelRead);
+
+ /* route RGB */
+ ps->cb_start = (ps->cb_start + ps->round_read)%ps->cb_size;
+ s = ps->xbuf;
+ r = (ps->cb_start + ps->ch_offset[0])%ps->cb_size;
+ g = (ps->cb_start + ps->ch_offset[1])%ps->cb_size;
+ b = (ps->cb_start + ps->ch_offset[2])%ps->cb_size;
+ for (i = 0; i < ps->cb_line_size/3; i++)
+ {
+ if (pself->pss->bpp_scan == 8)
+ {
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[b++];
+ }
+ else if (pself->pss->pdev->model == SCANWIT2720S)
+ {
+ t = (((ps->cbuf[r+1] << 8) | ps->cbuf[r]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ r += 2;
+ t = (((ps->cbuf[g+1] << 8) | ps->cbuf[g]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ g += 2;
+ t = (((ps->cbuf[b+1] << 8) | ps->cbuf[b]) & 0xfff) << 4;
+ put_int16r (t, s);
+ s += 2;
+ b += 2;
+ i++;
+ }
+ else
+ {
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[r++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[g++];
+ *s++ = ps->cbuf[b++];
+ *s++ = ps->cbuf[b++];
+ i++;
+ }
+ }
+
+ /* end of reading & offsetiing whole line data;
+ reset valid position */
+ ps->pos = 0;
+
+ /* prepare for next round */
+ ps->round_req = ps->cb_line_size;
+ ps->round_read =0;
+ }
+
+ /* Repack the whole scan line and copy to caller's buffer */
+ while (remaining > 0 && ps->pos < ps->cb_line_size)
+ {
+ *pbuf++ = ps->xbuf[ps->pos++];
+ remaining--;
+ }
+ }
+ *plen -= remaining;
+ DBG(DL_DATA_TRACE,
+ "%s: Request=%d, remaining=%d, read=%d, TXSource_rem=%d, bytes_rem=%lu\n",
+ me,
+ org_len,
+ pself->remaining(pself),
+ *plen,
+ TxSource_remaining(pself),
+ (u_long) ps->pss->bytes_remaining);
+ return status;
+}
+
+static SANE_Status RGBRouter_done (Source *pself)
+{
+ RGBRouter *ps = (RGBRouter *) pself;
+ SANE_Status status = TxSource_done(pself);
+
+ free(ps->cbuf);
+ free(ps->xbuf);
+ ps->cbuf = NULL;
+ ps->cb_start = -1;
+ ps->pos = 0;
+ return status;
+}
+
+static SANE_Status RGBRouter_init (RGBRouter *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ SANE_Status status = TxSource_init((TxSource *) pself,
+ pss,
+ RGBRouter_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ RGBRouter_get,
+ RGBRouter_done,
+ psub);
+ if (status == SANE_STATUS_GOOD)
+ {
+ SANE_Int lines_in_buffer = 0;
+
+ /* Size the buffer to accomodate the necessary number of scan
+ lines to cater for the offset between R, G and B */
+ lines_in_buffer = pss->chroma + 1;
+ pself->cb_line_size = pself->bytesPerLine((Source *) pself);
+ pself->cb_size = pself->cb_line_size*lines_in_buffer;
+ pself->pos = pself->cb_line_size;
+
+ pself->round_req = pself->cb_size;
+ pself->round_read = 0;
+
+ pself->cbuf = (SANE_Byte *) malloc(pself->cb_size);
+ pself->xbuf = (SANE_Byte *) malloc(pself->cb_line_size);
+ if (pself->cbuf == NULL || pself->xbuf == NULL)
+ {
+ DBG (DL_MAJOR_ERROR,
+ "%s: failed to allocate circular buffer.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ SANE_Int ch;
+
+ pself->cb_start = 0;
+ for (ch = 0; ch < 3; ch++)
+ {
+ pself->ch_offset[ch] =
+ pss->chroma_offset[ch] * pself->cb_line_size
+ + ch * (pself->cb_line_size / 3);
+ }
+ }
+ DBG(DL_MINOR_INFO, "RGBRouter_init: buf_size: %d x %d = %d\n",
+ pself->cb_line_size, lines_in_buffer, pself->cb_size);
+ DBG(DL_MINOR_INFO, "RGBRouter_init: buf offset R:%d G:%d B:%d\n",
+ pself->ch_offset[0], pself->ch_offset[1],pself->ch_offset[2]);
+ }
+ return status;
+}
+
+static SANE_Status create_RGBRouter (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ static char me[] = "create_RGBRouter";
+ SANE_Status status = SANE_STATUS_GOOD;
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ *pps = (Source *) malloc(sizeof(RGBRouter));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate RGBRouter.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = RGBRouter_init ((RGBRouter *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* An Inverter is used to invert the bits in a lineart image */
+
+typedef struct
+{
+ TX_SOURCE_GUTS;
+} Inverter;
+
+static SANE_Status Inverter_get (Source *pself, SANE_Byte *pbuf, SANE_Int *plen)
+{
+ SANE_Status status = TxSource_get (pself, pbuf, plen);
+ if (status == SANE_STATUS_GOOD)
+ {
+ int i;
+ for (i = 0; i < *plen; i++)
+ pbuf[i] ^= 0xFF;
+ }
+ return status;
+}
+
+static SANE_Status Inverter_init (Inverter *pself,
+ SnapScan_Scanner *pss,
+ Source *psub)
+{
+ return TxSource_init ((TxSource *) pself,
+ pss,
+ TxSource_remaining,
+ TxSource_bytesPerLine,
+ TxSource_pixelsPerLine,
+ Inverter_get,
+ TxSource_done,
+ psub);
+}
+
+static SANE_Status create_Inverter (SnapScan_Scanner *pss,
+ Source *psub,
+ Source **pps)
+{
+ SANE_Status status = SANE_STATUS_GOOD;
+ *pps = (Source *) malloc(sizeof(Inverter));
+ if (*pps == NULL)
+ {
+ DBG (DL_MAJOR_ERROR, "%s: failed to allocate Inverter.\n",
+ __FUNCTION__);
+ status = SANE_STATUS_NO_MEM;
+ }
+ else
+ {
+ status = Inverter_init ((Inverter *) *pps, pss, psub);
+ }
+ return status;
+}
+
+/* Source chain creation */
+
+static SANE_Status create_source_chain (SnapScan_Scanner *pss,
+ BaseSourceType bst,
+ Source **pps)
+{
+ static char me[] = "create_source_chain";
+ SANE_Status status = create_base_source (pss, bst, pps);
+
+ DBG (DL_CALL_TRACE, "%s\n", me);
+ if (status == SANE_STATUS_GOOD)
+ {
+ SnapScan_Mode mode = actual_mode(pss);
+ switch (mode)
+ {
+ case MD_COLOUR:
+ status = create_RGBRouter (pss, *pps, pps);
+ /* We only have the interlace probelms on
+ some scanners like the Epson Perfection 2480/2580
+ at 2400 dpi. */
+ if (status == SANE_STATUS_GOOD &&
+ ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200)))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_BILEVELCOLOUR:
+ status = create_Expander (pss, *pps, pps);
+ if (status == SANE_STATUS_GOOD)
+ status = create_RGBRouter (pss, *pps, pps);
+ if (status == SANE_STATUS_GOOD &&
+ ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200)))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_GREYSCALE:
+ if ((pss->pdev->model == PERFECTION2480 && pss->res == 2400) ||
+ (pss->pdev->model == PERFECTION3490 && pss->res == 3200) ||
+ (pss->pdev->model == PRISA5000E && pss->res == 1200))
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ case MD_LINEART:
+ /* The SnapScan creates a negative image by
+ default... so for the user interface to make sense,
+ the internal meaning of "negative" is reversed */
+ if (pss->negative == SANE_FALSE)
+ status = create_Inverter (pss, *pps, pps);
+ if (pss->pdev->model == PERFECTION3490 && pss->res == 3200)
+ status = create_Deinterlacer (pss, *pps, pps);
+ break;
+ default:
+ DBG (DL_MAJOR_ERROR, "%s: bad mode value %d (internal error)\n",
+ __FUNCTION__, mode);
+ status = SANE_STATUS_INVAL;
+ break;
+ }
+ }
+ return status;
+}
+
+/*
+ * $Log$
+ * Revision 1.21 2005/12/02 19:12:54 oliver-guest
+ * Another fix for lineart mode for the Epson 3490 @ 3200 DPI
+ *
+ * Revision 1.20 2005/11/28 19:28:29 oliver-guest
+ * Fix for lineart mode of Epson 3490 @ 3200 DPI
+ *
+ * Revision 1.19 2005/11/25 17:24:48 oliver-guest
+ * Fix for Epson 3490 @ 3200 DPI for grayscale and lineart mode
+ *
+ * Revision 1.18 2005/11/17 23:47:11 oliver-guest
+ * Revert previous 'fix', disable 2400 dpi for Epson 3490, use 1600 dpi instead
+ *
+ * Revision 1.17 2005/11/17 23:32:23 oliver-guest
+ * Fixes for Epson 3490 @ 2400 DPI
+ *
+ * Revision 1.16 2005/11/10 19:42:02 oliver-guest
+ * Added deinterlacing for Epson 3490
+ *
+ * Revision 1.15 2005/10/31 21:08:47 oliver-guest
+ * Distinguish between Benq 5000/5000E/5000U
+ *
+ * Revision 1.14 2005/10/13 22:43:30 oliver-guest
+ * Fixes for 16 bit scan mode from Simon Munton
+ *
+ * Revision 1.13 2005/10/11 18:47:07 oliver-guest
+ * Fixes for Epson 3490 and 16 bit scan mode
+ *
+ * Revision 1.12 2004/11/14 19:26:38 oliver-guest
+ * Applied patch from Julien Blache to change ch_past_init from SANE_Int to SANE_Bool
+ *
+ * Revision 1.11 2004/11/09 23:17:38 oliver-guest
+ * First implementation of deinterlacer for Epson scanners at high resolutions (thanks to Brad Johnson)
+ *
+ * Revision 1.10 2004/10/03 17:34:36 hmg-guest
+ * 64 bit platform fixes (bug #300799).
+ *
+ * Revision 1.9 2004/04/09 16:18:37 oliver-guest
+ * Fix initialization of FDSource.bytes_remaining
+ *
+ * Revision 1.8 2004/04/09 11:59:02 oliver-guest
+ * Fixes for pthread implementation
+ *
+ * Revision 1.7 2004/04/08 21:53:10 oliver-guest
+ * Use sanei_thread in snapscan backend
+ *
+ * Revision 1.6 2001/12/17 22:51:49 oliverschwartz
+ * Update to snapscan-20011212 (snapscan 1.4.3)
+ *
+ * Revision 1.18 2001/12/12 19:44:59 oliverschwartz
+ * Clean up CVS log
+ *
+ * Revision 1.17 2001/11/27 23:16:17 oliverschwartz
+ * - Fix color alignment for SnapScan 600
+ * - Added documentation in snapscan-sources.c
+ * - Guard against TL_X < BR_X and TL_Y < BR_Y
+ *
+ * Revision 1.16 2001/10/08 18:22:02 oliverschwartz
+ * - Disable quality calibration for Acer Vuego 310F
+ * - Use sanei_scsi_max_request_size as scanner buffer size
+ * for SCSI devices
+ * - Added new devices to snapscan.desc
+ *
+ * Revision 1.15 2001/09/28 15:56:51 oliverschwartz
+ * - fix hanging for SNAPSCAN300 / VUEGO 310
+ *
+ * Revision 1.14 2001/09/28 13:39:16 oliverschwartz
+ * - Added "Snapscan 300" ID string
+ * - cleanup
+ * - more debugging messages in snapscan-sources.c
+ *
+ * Revision 1.13 2001/09/18 15:01:07 oliverschwartz
+ * - Read scanner id string again after firmware upload
+ * to indentify correct model
+ * - Make firmware upload work for AGFA scanners
+ * - Change copyright notice
+ *
+ * Revision 1.12 2001/09/09 18:06:32 oliverschwartz
+ * add changes from Acer (new models; automatic firmware upload for USB scanners); fix distorted colour scans after greyscale scans (call set_window only in sane_start); code cleanup
+ *
+ * Revision 1.11 2001/04/13 13:12:18 oliverschwartz
+ * use absolute_max as expected_read_bytes for PRISA620S
+ *
+ * Revision 1.10 2001/04/10 11:04:31 sable
+ * Adding support for snapscan e40 an e50 thanks to Giuseppe Tanzilli
+ *
+ * Revision 1.9 2001/03/17 22:53:21 sable
+ * Applying Mikael Magnusson patch concerning Gamma correction
+ * Support for 1212U_2
+ *
+ * Revision 1.8 2000/11/28 03:55:07 cbagwell
+ * Reverting a fix to RGBRouter_remaining to original fix. This allows
+ * most scanners to scan at 600 dpi by ignoring insufficent data in
+ * the RGB circular buffer and always returning size = 1 in those cases.
+ * This should probably be fixed at a higher level.
+ *
+ * Revision 1.7 2000/11/20 01:02:42 cbagwell
+ * Updates so that USB will continue reading when it receives an EAGAIN error.
+ * Also, changed RGBRouter_remaining to not be able to return a negative
+ * value.
+ *
+ * Revision 1.6 2000/11/04 01:53:58 cbagwell
+ * Commiting some needed USB updates. Added extra test logic to detect
+ * bad bytes_expected values. Just to help debug faster on scanners
+ * that tickle the bug.
+ *
+ * Revision 1.5 2000/10/30 22:32:20 sable
+ * Support for vuego310s vuego610s and 1236s
+ *
+ * Revision 1.4 2000/10/28 14:16:10 sable
+ * Bug correction for SnapScan310
+ *
+ * Revision 1.3 2000/10/28 14:06:35 sable
+ * Add support for Acer300f
+ *
+ * Revision 1.2 2000/10/13 03:50:27 cbagwell
+ * Updating to source from SANE 1.0.3. Calling this versin 1.1
+ * */