kopia lustrzana https://gitlab.com/sane-project/backends
1046 wiersze
35 KiB
C
1046 wiersze
35 KiB
C
/* sane - Scanner Access Now Easy.
|
|
|
|
Copyright (C) 1997, 1998 Franck Schnefra, Michel Roelofs,
|
|
Emmanuel Blot, Mikko Tyolajarvi, David Mosberger-Tang, Wolfgang Goeller,
|
|
Petter Reinholdtsen, Gary Plewa, Sebastien Sable, 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: %d; read: %d\n",
|
|
me, ps->scsi_buf_pos, ps->scsi_buf_max, ps->pss->expected_read_bytes,
|
|
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;
|
|
}
|
|
|
|
/* 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 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;
|
|
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++)
|
|
{
|
|
*s++ = ps->cbuf[r++];
|
|
*s++ = ps->cbuf[g++];
|
|
*s++ = ps->cbuf[b++];
|
|
}
|
|
|
|
/* 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=%d\n",
|
|
me,
|
|
org_len,
|
|
pself->remaining(pself),
|
|
*plen,
|
|
TxSource_remaining(pself),
|
|
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);
|
|
break;
|
|
case MD_BILEVELCOLOUR:
|
|
status = create_Expander (pss, *pps, pps);
|
|
if (status == SANE_STATUS_GOOD)
|
|
status = create_RGBRouter (pss, *pps, pps);
|
|
break;
|
|
case MD_GREYSCALE:
|
|
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);
|
|
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.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
|
|
* */
|