diff --git a/ChangeLog b/ChangeLog index 38aa9d760..c6881bc08 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-11-10 Oliver Schwartz + + * snapscan-sources.c: First implementation of deinterlacer for epson + scanners at high resolutions (thanks to Brad Johnson) + 2004-11-07 Henning Meier-Geinitz * configure configure.in: Updated version. Enabled compilation diff --git a/backend/snapscan-sources.c b/backend/snapscan-sources.c index 6d3345920..0bb64ab3d 100644 --- a/backend/snapscan-sources.c +++ b/backend/snapscan-sources.c @@ -664,6 +664,192 @@ static SANE_Status create_Expander (SnapScan_Scanner *pss, 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 4 pixels. + + The Deinterlacer detects if data is in SANE RGB frame format (3 bytes/pixel) + or in Grayscale (1 byte/pixel). + + The first 4 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 4 lines. A better way to handle this would be to + scan in 4 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_ndata; /* actual #bytes in channel buffer */ + SANE_Int ch_pos; /* position in buffer */ + SANE_Int ch_past_init; /* flag indicating if we have enough data to shift pixels down */ + SANE_Int ch_bytes_per_pixel; +} 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_size/5) == ps->ch_ndata % (ps->ch_size/5) ) + { + /* we need more data; try to get the remainder of the current + line, or else the next line */ + SANE_Int ndata = (ps->ch_size/5) - ps->ch_ndata % (ps->ch_size/5); + 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_size /5; + } + 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_bytes_per_pixel) % 2 == 0) + { + /* 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_size/5)) % 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_size/5) == 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_size /5 *4) + ps->ch_past_init = 1; + + 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_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) + { + /* We need at least 5 lines of buffer in order to shift up 4 pixels. */ + pself->ch_size = TxSource_bytesPerLine((Source *) pself) *5; + pself->ch_buf = (SANE_Byte *) malloc(pself->ch_size); + if (pself->ch_buf == NULL) + { + DBG (DL_MAJOR_ERROR, + "%s: couldn't allocate channel buffer.\n", + __FUNCTION__); + status = SANE_STATUS_NO_MEM; + } + else + { + pself->ch_ndata = 0; + pself->ch_pos = 0; + pself->ch_past_init = 0; + if (actual_mode(pss) == MD_GREYSCALE) + pself->ch_bytes_per_pixel = 1; + else + pself->ch_bytes_per_pixel = 3; + } + } + return status; +} + +static SANE_Status create_Deinterlacer (SnapScan_Scanner *pss, + Source *psub, + Source **pps) +{ + SANE_Status status = SANE_STATUS_GOOD; + *pps = (Source *) malloc(sizeof(Deinterlacer)); + if (*pps == NULL) + { + DBG (DL_MAJOR_ERROR, + "%s: failed to allocate Deinterlacer.\n", + __FUNCTION__); + status = SANE_STATUS_NO_MEM; + } + else + { + status = Deinterlacer_init ((Deinterlacer *) *pps, pss, psub); + } + return status; +} + +/* ----------------------------------------------------- */ + /* the RGB router assumes 8-bit RGB data arranged in contiguous channels, possibly with R-G and R-B offsets, and rearranges the data into SANE RGB frame format */ @@ -935,14 +1121,29 @@ static SANE_Status create_source_chain (SnapScan_Scanner *pss, { 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) + 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) + status = create_Deinterlacer (pss, *pps, pps); break; case MD_GREYSCALE: + if (pss->pdev->model == PERFECTION2480 && + pss->res == 2400) + 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, @@ -962,6 +1163,9 @@ static SANE_Status create_source_chain (SnapScan_Scanner *pss, /* * $Log$ + * 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). *