kopia lustrzana https://gitlab.com/sane-project/backends
SnapScan scanner data sources for version 1.0 (implementation)
rodzic
d0dd2385c1
commit
cf90633b56
|
@ -0,0 +1,847 @@
|
|||
/* $Id$
|
||||
SnapScan backend data sources (implementation) */
|
||||
|
||||
#ifdef TEMPORARY
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
*plen -= remaining;
|
||||
return status;
|
||||
#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;
|
||||
|
||||
|
||||
#define SCSISOURCE_BAD_TIME -1
|
||||
|
||||
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 */
|
||||
struct timeval time; /* time of last scsi read (usec) */
|
||||
} 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;
|
||||
while (remaining > 0 &&
|
||||
pself->remaining(pself) > 0 &&
|
||||
status == SANE_STATUS_GOOD)
|
||||
{
|
||||
SANE_Int ndata = ps->scsi_buf_max - ps->scsi_buf_pos;
|
||||
if (ndata == 0)
|
||||
/* read more data */
|
||||
{
|
||||
struct timeval oldtime = ps->time;
|
||||
if (ps->time.tv_sec != SCSISOURCE_BAD_TIME &&
|
||||
gettimeofday(&(ps->time), NULL) == 0)
|
||||
/* estimate number of lines to read from the elapsed time
|
||||
since the last read and the scanner's reported speed */
|
||||
{
|
||||
double msecs =
|
||||
(ps->time.tv_sec - oldtime.tv_sec) * 1000.0 +
|
||||
(ps->time.tv_usec - oldtime.tv_usec) / 1000.0;
|
||||
ps->pss->expected_read_bytes =
|
||||
((int)(msecs/ps->pss->ms_per_line)) * ps->pss->bytes_per_line;
|
||||
}
|
||||
else
|
||||
/* use the "lines_per_read" values */
|
||||
{
|
||||
SANE_Int lines;
|
||||
if (is_colour_mode(actual_mode(ps->pss)) == SANE_TRUE)
|
||||
lines = ps->pss->rgb_lpr;
|
||||
else
|
||||
lines = ps->pss->gs_lpr;
|
||||
ps->pss->expected_read_bytes = lines * ps->pss->bytes_per_line;
|
||||
}
|
||||
ps->pss->expected_read_bytes =
|
||||
MIN(ps->pss->expected_read_bytes, ps->pss->bytes_remaining);
|
||||
ps->pss->expected_read_bytes =
|
||||
MIN(ps->pss->expected_read_bytes, ps->absolute_max);
|
||||
ps->scsi_buf_pos = 0;
|
||||
ps->scsi_buf_max = 0;
|
||||
status = scsi_read (ps->pss, READ_IMAGE);
|
||||
if (status == SANE_STATUS_GOOD)
|
||||
{
|
||||
ps->scsi_buf_max = ps->pss->read_bytes;
|
||||
ndata = ps->pss->read_bytes;
|
||||
ps->pss->bytes_remaining -= ps->pss->read_bytes;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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 =
|
||||
(SCANNER_BUF_SZ / pss->bytes_per_line) * pss->bytes_per_line;
|
||||
if(gettimeofday(&(pself->time), NULL) != 0)
|
||||
{
|
||||
DBG (DL_MAJOR_ERROR, "%s: error in gettimeofday(): %s\n",
|
||||
__FUNCTION__, sys_errlist[errno]);
|
||||
pself->time.tv_sec = SCSISOURCE_BAD_TIME;
|
||||
pself->time.tv_usec = SCSISOURCE_BAD_TIME;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* File sources */
|
||||
|
||||
typedef struct {
|
||||
SOURCE_GUTS;
|
||||
int fd;
|
||||
} FDSource;
|
||||
|
||||
static SANE_Int
|
||||
FDSource_remaining (Source *pself)
|
||||
{
|
||||
return pself->pss->bytes_remaining;
|
||||
}
|
||||
|
||||
#ifdef TEMPORARY
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
*plen -= remaining;
|
||||
return status;
|
||||
#endif
|
||||
|
||||
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;
|
||||
else
|
||||
/* it's an IO error */
|
||||
{
|
||||
DBG (DL_MAJOR_ERROR, "%s: read failed: %s\n",
|
||||
__FUNCTION__, sys_errlist[errno]);
|
||||
status = SANE_STATUS_IO_ERROR;
|
||||
}
|
||||
}
|
||||
else if (bytes_read == 0)
|
||||
break;
|
||||
else
|
||||
{
|
||||
ps->pss->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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
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)
|
||||
{
|
||||
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
|
||||
|
||||
NOTE: at first, assume NO R-G and R-B offsets; thus, the buffer
|
||||
is not circular, and cb_bot is fixed at 0 */
|
||||
|
||||
typedef struct {
|
||||
SANE_Int offset; /* offset in the master buffer */
|
||||
SANE_Int size; /* size of a channel (bytes) */
|
||||
SANE_Int ndata; /* total bytes present for this channel */
|
||||
SANE_Int pos; /* current position */
|
||||
} Channel;
|
||||
|
||||
typedef struct {
|
||||
TX_SOURCE_GUTS;
|
||||
SANE_Byte *cbuf; /* circular line buffer */
|
||||
SANE_Int cb_size; /* size of the circular buffer */
|
||||
SANE_Int cb_bot; /* bottom of the circular buffer */
|
||||
SANE_Int cb_top; /* top of the circular buffer */
|
||||
Channel channels[3]; /* the channel book-keeping stuff */
|
||||
SANE_Int ch; /* current channel */
|
||||
} RGBRouter;
|
||||
|
||||
static inline SANE_Int
|
||||
Channel_remaining (Channel *pc)
|
||||
{
|
||||
return pc->ndata - pc->pos;
|
||||
}
|
||||
|
||||
static SANE_Int
|
||||
RGBRouter_remaining (Source *pself)
|
||||
{
|
||||
RGBRouter *ps = (RGBRouter*)pself;
|
||||
return (TxSource_remaining(pself) +
|
||||
Channel_remaining(&(ps->channels[0])) +
|
||||
Channel_remaining(&(ps->channels[1])) +
|
||||
Channel_remaining(&(ps->channels[2])));
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
Channel_update_ndata (Channel *pc, SANE_Int cb_top)
|
||||
{
|
||||
SANE_Int t = cb_top - pc->offset + 1;
|
||||
if (t <= 0)
|
||||
pc->ndata = 0;
|
||||
else
|
||||
pc->ndata = MIN(t, pc->size);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
while (remaining > 0 &&
|
||||
pself->remaining(pself) > 0)
|
||||
{
|
||||
Channel *pc = &(ps->channels[ps->ch]);
|
||||
|
||||
if (pc->pos == pc->ndata)
|
||||
/* try to get more data */
|
||||
{
|
||||
SANE_Int ndata = ps->cb_size - (ps->cb_top + 1);
|
||||
if (ndata == 0)
|
||||
{
|
||||
ndata = ps->cb_size;
|
||||
ps->cb_top = -1;
|
||||
ps->channels[0].pos = 0;
|
||||
ps->channels[1].pos = 0;
|
||||
ps->channels[2].pos = 0;
|
||||
}
|
||||
status = TxSource_get (pself, ps->cbuf + ps->cb_top + 1, &ndata);
|
||||
if (status != SANE_STATUS_GOOD)
|
||||
break;
|
||||
if (ndata == 0)
|
||||
break;
|
||||
ps->cb_top += ndata;
|
||||
Channel_update_ndata(&(ps->channels[0]), ps->cb_top);
|
||||
Channel_update_ndata(&(ps->channels[1]), ps->cb_top);
|
||||
Channel_update_ndata(&(ps->channels[2]), ps->cb_top);
|
||||
if (pc->pos == pc->ndata)
|
||||
/* not enough data to continue with this channel */
|
||||
break;
|
||||
}
|
||||
*pbuf = ps->cbuf[pc->offset + pc->pos];
|
||||
pbuf++;
|
||||
pc->pos++;
|
||||
remaining--;
|
||||
ps->ch = (ps->ch + 1) % 3;
|
||||
}
|
||||
|
||||
*plen -= remaining;
|
||||
return status;
|
||||
}
|
||||
|
||||
static SANE_Status
|
||||
RGBRouter_done (Source *pself)
|
||||
{
|
||||
RGBRouter *ps = (RGBRouter*)pself;
|
||||
SANE_Status status = TxSource_done (pself);
|
||||
free(ps->cbuf);
|
||||
ps->cbuf = NULL;
|
||||
ps->cb_bot = 0;
|
||||
ps->cb_top = -1;
|
||||
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)
|
||||
{
|
||||
pself->cb_size = pself->bytesPerLine((Source*)pself);
|
||||
pself->cbuf = (SANE_Byte*)malloc(pself->cb_size);
|
||||
if (pself->cbuf == NULL)
|
||||
{
|
||||
DBG (DL_MAJOR_ERROR, "%s: failed to allocate circular buffer.\n",
|
||||
__FUNCTION__);
|
||||
status = SANE_STATUS_NO_MEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
SANE_Int ch_size = pself->cb_size / 3;
|
||||
SANE_Int ch;
|
||||
pself->cb_bot = 0;
|
||||
pself->cb_top = -1;
|
||||
for (ch = 0; ch < 3; ch++)
|
||||
{
|
||||
Channel *pc = &(pself->channels[ch]);
|
||||
pc->pos = 0;
|
||||
pc->ndata = 0;
|
||||
pc->size = ch_size;
|
||||
}
|
||||
pself->channels[0].offset = 0;
|
||||
pself->channels[1].offset = ch_size;
|
||||
pself->channels[2].offset = 2 * ch_size;
|
||||
pself->ch = 0;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static SANE_Status
|
||||
create_RGBRouter (SnapScan_Scanner *pss, Source *psub, Source **pps)
|
||||
{
|
||||
SANE_Status status = SANE_STATUS_GOOD;
|
||||
*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)
|
||||
{
|
||||
SANE_Status status = create_base_source (pss, bst, pps);
|
||||
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;
|
||||
}
|
||||
|
Ładowanie…
Reference in New Issue