kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1382 wiersze
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1382 wiersze
		
	
	
		
			47 KiB
		
	
	
	
		
			C
		
	
	
| /* 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 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).
 | |
| ***********************************************************************************/
 | |
| 
 | |
| 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",
 | |
|                      __func__, strerror(errno));
 | |
|             status = SANE_STATUS_IO_ERROR;
 | |
|         }
 | |
|         else if (bytes_read == 0)
 | |
|         {
 | |
|             /* EOF of current reading */
 | |
|             DBG(DL_DATA_TRACE, "%s: EOF\n",__func__);
 | |
|             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",
 | |
|                  __func__);
 | |
|             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",
 | |
|              __func__);
 | |
|         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",
 | |
|                  __func__);
 | |
|             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",
 | |
|              __func__);
 | |
|         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",
 | |
|                  __func__);
 | |
|             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",
 | |
|              __func__);
 | |
|         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",
 | |
|                  __func__);
 | |
|         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",
 | |
|                  __func__, 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
 | |
|  * */
 |