kopia lustrzana https://gitlab.com/sane-project/backends
2231 wiersze
60 KiB
C
2231 wiersze
60 KiB
C
/* sane - Scanner Access Now Easy.
|
|
|
|
Copyright (C) 2005 Mustek.
|
|
Originally maintained by Mustek
|
|
Author:Jack Roy 2005.5.24
|
|
|
|
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 implements a SANE backend for the Mustek BearPaw 2448 TA Pro
|
|
and similar USB2 scanners. */
|
|
|
|
#define DEBUG_DECLARE_ONLY
|
|
#include "sane/config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <pthread.h> /* TODO: use sanei_thread functions instead */
|
|
|
|
#include "byteorder.h"
|
|
#include "sane/sane.h"
|
|
#include "sane/sanei_backend.h"
|
|
|
|
#include "mustek_usb2_high.h"
|
|
|
|
|
|
void
|
|
Scanner_Init (Scanner_State * st)
|
|
{
|
|
DBG_ENTER ();
|
|
|
|
Asic_Initialize (&st->chip);
|
|
|
|
st->bOpened = SANE_FALSE;
|
|
st->bPrepared = SANE_FALSE;
|
|
st->isCanceled = SANE_FALSE;
|
|
st->bIsFirstReadBefData = SANE_TRUE;
|
|
|
|
st->pReadImageHead = NULL;
|
|
st->pGammaTable = NULL;
|
|
|
|
pthread_mutex_init (&st->scannedLinesMutex, NULL);
|
|
pthread_mutex_init (&st->readyLinesMutex, NULL);
|
|
|
|
DBG_LEAVE ();
|
|
}
|
|
|
|
SANE_Bool
|
|
Scanner_IsPresent (void)
|
|
{
|
|
ASIC chip;
|
|
DBG_ENTER ();
|
|
|
|
Asic_Initialize (&chip);
|
|
if (Asic_Open (&chip) != SANE_STATUS_GOOD)
|
|
return SANE_FALSE;
|
|
if (Asic_Close (&chip) != SANE_STATUS_GOOD)
|
|
return SANE_FALSE;
|
|
|
|
DBG_LEAVE ();
|
|
return SANE_TRUE;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_PowerControl (Scanner_State * st, SANE_Bool isLampOn,
|
|
SANE_Bool isTALampOn)
|
|
{
|
|
SANE_Status status;
|
|
SANE_Bool hasTA;
|
|
DBG_ENTER ();
|
|
|
|
status = Asic_Open (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
status = Asic_TurnLamp (&st->chip, isLampOn);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto error;
|
|
|
|
status = Asic_IsTAConnected (&st->chip, &hasTA);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto error;
|
|
|
|
if (hasTA)
|
|
{
|
|
status = Asic_TurnTA (&st->chip, isTALampOn);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto error;
|
|
}
|
|
|
|
status = Asic_Close (&st->chip);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
|
|
error:
|
|
Asic_Close (&st->chip);
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_BackHome (Scanner_State * st)
|
|
{
|
|
SANE_Status status;
|
|
DBG_ENTER ();
|
|
|
|
status = Asic_Open (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
status = Asic_CarriageHome (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
Asic_Close (&st->chip);
|
|
return status;
|
|
}
|
|
|
|
status = Asic_Close (&st->chip);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_IsTAConnected (Scanner_State * st, SANE_Bool * pHasTA)
|
|
{
|
|
SANE_Status status;
|
|
DBG_ENTER ();
|
|
|
|
status = Asic_Open (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
status = Asic_IsTAConnected (&st->chip, pHasTA);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
Asic_Close (&st->chip);
|
|
return status;
|
|
}
|
|
|
|
status = Asic_Close (&st->chip);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_GetKeyStatus (Scanner_State * st, SANE_Byte * pKey)
|
|
{
|
|
SANE_Status status;
|
|
DBG_ENTER ();
|
|
|
|
status = Asic_Open (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
status = Asic_CheckFunctionKey (&st->chip, pKey);
|
|
if (status != SANE_STATUS_GOOD)
|
|
{
|
|
Asic_Close (&st->chip);
|
|
return status;
|
|
}
|
|
|
|
status = Asic_Close (&st->chip);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
|
|
static SANE_Byte
|
|
QBET4 (SANE_Byte A, SANE_Byte B)
|
|
{
|
|
static const SANE_Byte bQBET[16][16] = {
|
|
{0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
|
|
{0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
|
|
{0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
|
|
{0, 0, 0, 0, 1, 1, 2, 2, 4, 4, 5, 5, 8, 8, 9, 9},
|
|
{1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11},
|
|
{1, 1, 1, 1, 3, 3, 3, 3, 6, 6, 6, 6, 10, 10, 11, 11},
|
|
{2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11},
|
|
{2, 2, 2, 2, 3, 3, 3, 3, 7, 7, 7, 7, 10, 10, 11, 11},
|
|
{4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
|
|
{4, 4, 4, 4, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
|
|
{5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
|
|
{5, 5, 5, 5, 6, 6, 7, 7, 12, 12, 12, 12, 13, 13, 14, 14},
|
|
{8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15},
|
|
{8, 8, 8, 8, 10, 10, 10, 10, 13, 13, 13, 13, 15, 15, 15, 15},
|
|
{9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15},
|
|
{9, 9, 9, 9, 11, 11, 11, 11, 14, 14, 14, 14, 15, 15, 15, 15}
|
|
};
|
|
|
|
return bQBET[A & 0x0f][B & 0x0f];
|
|
}
|
|
|
|
static void
|
|
SetPixel48Bit (unsigned short * pLine, unsigned short * pGammaTable,
|
|
unsigned short wRTempData, unsigned short wGTempData,
|
|
unsigned short wBTempData, SANE_Bool isOrderInvert)
|
|
{
|
|
if (!isOrderInvert)
|
|
{
|
|
pLine[0] = pGammaTable[wRTempData];
|
|
pLine[1] = pGammaTable[wGTempData + 0x10000];
|
|
pLine[2] = pGammaTable[wBTempData + 0x20000];
|
|
}
|
|
else
|
|
{
|
|
pLine[2] = pGammaTable[wRTempData];
|
|
pLine[1] = pGammaTable[wGTempData + 0x10000];
|
|
pLine[0] = pGammaTable[wBTempData + 0x20000];
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetRgb48BitLineHalf (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool isOrderInvert)
|
|
{
|
|
unsigned short wRLinePos, wGLinePos, wBLinePos;
|
|
unsigned short wRTempData, wGTempData, wBTempData;
|
|
unsigned short i;
|
|
|
|
wRLinePos = st->wtheReadyLines;
|
|
wGLinePos = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePos = st->wtheReadyLines - st->wLineDistance * 2;
|
|
wRLinePos = (wRLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePos = (wGLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePos = (wBLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
for (i = 0; i < st->SWWidth; i++)
|
|
{
|
|
wRTempData = st->pReadImageHead[wRLinePos + i * 6] |
|
|
(st->pReadImageHead[wRLinePos + i * 6 + 1] << 8);
|
|
wGTempData = st->pReadImageHead[wGLinePos + i * 6 + 2] |
|
|
(st->pReadImageHead[wGLinePos + i * 6 + 3] << 8);
|
|
wBTempData = st->pReadImageHead[wBLinePos + i * 6 + 4] |
|
|
(st->pReadImageHead[wBLinePos + i * 6 + 5] << 8);
|
|
|
|
LE16TOH (wRTempData);
|
|
LE16TOH (wGTempData);
|
|
LE16TOH (wBTempData);
|
|
|
|
SetPixel48Bit ((unsigned short *) (pLine + (i * 6)), st->pGammaTable,
|
|
wRTempData, wGTempData, wBTempData, isOrderInvert);
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetRgb48BitLineFull (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool isOrderInvert)
|
|
{
|
|
unsigned short wRLinePosOdd, wGLinePosOdd, wBLinePosOdd;
|
|
unsigned short wRLinePosEven, wGLinePosEven, wBLinePosEven;
|
|
unsigned int dwRTempData, dwGTempData, dwBTempData;
|
|
unsigned short i = 0;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wRLinePosOdd = st->wtheReadyLines - st->wPixelDistance;
|
|
wGLinePosOdd = st->wtheReadyLines - st->wLineDistance -
|
|
st->wPixelDistance;
|
|
wBLinePosOdd = st->wtheReadyLines - st->wLineDistance * 2 -
|
|
st->wPixelDistance;
|
|
wRLinePosEven = st->wtheReadyLines;
|
|
wGLinePosEven = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePosEven = st->wtheReadyLines - st->wLineDistance * 2;
|
|
}
|
|
else
|
|
{
|
|
wRLinePosOdd = st->wtheReadyLines;
|
|
wGLinePosOdd = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePosOdd = st->wtheReadyLines - st->wLineDistance * 2;
|
|
wRLinePosEven = st->wtheReadyLines - st->wPixelDistance;
|
|
wGLinePosEven = st->wtheReadyLines - st->wLineDistance -
|
|
st->wPixelDistance;
|
|
wBLinePosEven = st->wtheReadyLines - st->wLineDistance * 2 -
|
|
st->wPixelDistance;
|
|
}
|
|
wRLinePosOdd = (wRLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePosOdd = (wGLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePosOdd = (wBLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wRLinePosEven = (wRLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePosEven = (wGLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePosEven = (wBLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
while (i < st->SWWidth)
|
|
{
|
|
if ((i + 1) >= st->SWWidth)
|
|
break;
|
|
|
|
dwRTempData = st->pReadImageHead[wRLinePosOdd + i * 6] |
|
|
(st->pReadImageHead[wRLinePosOdd + i * 6 + 1] << 8);
|
|
dwRTempData += st->pReadImageHead[wRLinePosEven + (i + 1) * 6] |
|
|
(st->pReadImageHead[wRLinePosEven + (i + 1) * 6 + 1] << 8);
|
|
dwRTempData /= 2;
|
|
|
|
dwGTempData = st->pReadImageHead[wGLinePosOdd + i * 6 + 2] |
|
|
(st->pReadImageHead[wGLinePosOdd + i * 6 + 3] << 8);
|
|
dwGTempData += st->pReadImageHead[wGLinePosEven + (i + 1) * 6 + 2] |
|
|
(st->pReadImageHead[wGLinePosEven + (i + 1) * 6 + 3] << 8);
|
|
dwGTempData /= 2;
|
|
|
|
dwBTempData = st->pReadImageHead[wBLinePosOdd + i * 6 + 4] |
|
|
(st->pReadImageHead[wBLinePosOdd + i * 6 + 5] << 8);
|
|
dwBTempData += st->pReadImageHead[wBLinePosEven + (i + 1) * 6 + 4] |
|
|
(st->pReadImageHead[wBLinePosEven + (i + 1) * 6 + 5] << 8);
|
|
dwBTempData /= 2;
|
|
|
|
LE16TOH (dwRTempData);
|
|
LE16TOH (dwGTempData);
|
|
LE16TOH (dwBTempData);
|
|
|
|
SetPixel48Bit ((unsigned short *) (pLine + (i * 6)), st->pGammaTable,
|
|
dwRTempData, dwGTempData, dwBTempData, isOrderInvert);
|
|
i++;
|
|
|
|
dwRTempData = st->pReadImageHead[wRLinePosEven + i * 6] |
|
|
(st->pReadImageHead[wRLinePosEven + i * 6 + 1] << 8);
|
|
dwRTempData += st->pReadImageHead[wRLinePosOdd + (i + 1) * 6] |
|
|
(st->pReadImageHead[wRLinePosOdd + (i + 1) * 6 + 1] << 8);
|
|
dwRTempData /= 2;
|
|
|
|
dwGTempData = st->pReadImageHead[wGLinePosEven + i * 6 + 2] |
|
|
(st->pReadImageHead[wGLinePosEven + i * 6 + 3] << 8);
|
|
dwGTempData += st->pReadImageHead[wGLinePosOdd + (i + 1) * 6 + 2] |
|
|
(st->pReadImageHead[wGLinePosOdd + (i + 1) * 6 + 3] << 8);
|
|
dwGTempData /= 2;
|
|
|
|
dwBTempData = st->pReadImageHead[wBLinePosEven + i * 6 + 4] |
|
|
(st->pReadImageHead[wBLinePosEven + i * 6 + 5] << 8);
|
|
dwBTempData += st->pReadImageHead[wBLinePosOdd + (i + 1) * 6 + 4] |
|
|
(st->pReadImageHead[wBLinePosOdd + (i + 1) * 6 + 5] << 8);
|
|
dwBTempData /= 2;
|
|
|
|
LE16TOH (dwRTempData);
|
|
LE16TOH (dwGTempData);
|
|
LE16TOH (dwBTempData);
|
|
|
|
SetPixel48Bit ((unsigned short *) (pLine + (i * 6)), st->pGammaTable,
|
|
dwRTempData, dwGTempData, dwBTempData, isOrderInvert);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SetPixel24Bit (SANE_Byte * pLine, unsigned short * pGammaTable,
|
|
unsigned short tempR, unsigned short tempG, unsigned short tempB,
|
|
SANE_Bool isOrderInvert)
|
|
{
|
|
if (!isOrderInvert)
|
|
{
|
|
pLine[0] = (SANE_Byte) pGammaTable[tempR];
|
|
pLine[1] = (SANE_Byte) pGammaTable[4096 + tempG];
|
|
pLine[2] = (SANE_Byte) pGammaTable[8192 + tempB];
|
|
}
|
|
else
|
|
{
|
|
pLine[2] = (SANE_Byte) pGammaTable[tempR];
|
|
pLine[1] = (SANE_Byte) pGammaTable[4096 + tempG];
|
|
pLine[0] = (SANE_Byte) pGammaTable[8192 + tempB];
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetRgb24BitLineHalf (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool isOrderInvert)
|
|
{
|
|
unsigned short wRLinePos, wGLinePos, wBLinePos;
|
|
unsigned short wRed, wGreen, wBlue;
|
|
unsigned short tempR, tempG, tempB;
|
|
unsigned short i;
|
|
|
|
wRLinePos = st->wtheReadyLines;
|
|
wGLinePos = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePos = st->wtheReadyLines - st->wLineDistance * 2;
|
|
wRLinePos = (wRLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePos = (wGLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePos = (wBLinePos % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
for (i = 0; i < st->SWWidth; i++)
|
|
{
|
|
wRed = st->pReadImageHead[wRLinePos + i * 3];
|
|
wRed += st->pReadImageHead[wRLinePos + (i + 1) * 3];
|
|
wRed /= 2;
|
|
|
|
wGreen = st->pReadImageHead[wGLinePos + i * 3 + 1];
|
|
wGreen += st->pReadImageHead[wGLinePos + (i + 1) * 3 + 1];
|
|
wGreen /= 2;
|
|
|
|
wBlue = st->pReadImageHead[wBLinePos + i * 3 + 2];
|
|
wBlue += st->pReadImageHead[wBLinePos + (i + 1) * 3 + 2];
|
|
wBlue /= 2;
|
|
|
|
tempR = (wRed << 4) | QBET4 (wBlue, wGreen);
|
|
tempG = (wGreen << 4) | QBET4 (wRed, wBlue);
|
|
tempB = (wBlue << 4) | QBET4 (wGreen, wRed);
|
|
|
|
SetPixel24Bit (pLine + (i * 3), st->pGammaTable, tempR, tempG, tempB,
|
|
isOrderInvert);
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetRgb24BitLineFull (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool isOrderInvert)
|
|
{
|
|
unsigned short wRLinePosOdd, wGLinePosOdd, wBLinePosOdd;
|
|
unsigned short wRLinePosEven, wGLinePosEven, wBLinePosEven;
|
|
unsigned short wRed, wGreen, wBlue;
|
|
unsigned short tempR, tempG, tempB;
|
|
unsigned short i = 0;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wRLinePosOdd = st->wtheReadyLines - st->wPixelDistance;
|
|
wGLinePosOdd = st->wtheReadyLines - st->wLineDistance -
|
|
st->wPixelDistance;
|
|
wBLinePosOdd = st->wtheReadyLines - st->wLineDistance * 2 -
|
|
st->wPixelDistance;
|
|
wRLinePosEven = st->wtheReadyLines;
|
|
wGLinePosEven = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePosEven = st->wtheReadyLines - st->wLineDistance * 2;
|
|
}
|
|
else
|
|
{
|
|
wRLinePosOdd = st->wtheReadyLines;
|
|
wGLinePosOdd = st->wtheReadyLines - st->wLineDistance;
|
|
wBLinePosOdd = st->wtheReadyLines - st->wLineDistance * 2;
|
|
wRLinePosEven = st->wtheReadyLines - st->wPixelDistance;
|
|
wGLinePosEven = st->wtheReadyLines - st->wLineDistance -
|
|
st->wPixelDistance;
|
|
wBLinePosEven = st->wtheReadyLines - st->wLineDistance * 2 -
|
|
st->wPixelDistance;
|
|
}
|
|
wRLinePosOdd = (wRLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePosOdd = (wGLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePosOdd = (wBLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wRLinePosEven = (wRLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
wGLinePosEven = (wGLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
wBLinePosEven = (wBLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
while (i < st->SWWidth)
|
|
{
|
|
if ((i + 1) >= st->SWWidth)
|
|
break;
|
|
|
|
wRed = st->pReadImageHead[wRLinePosOdd + i * 3];
|
|
wRed += st->pReadImageHead[wRLinePosEven + (i + 1) * 3];
|
|
wRed /= 2;
|
|
|
|
wGreen = st->pReadImageHead[wGLinePosOdd + i * 3 + 1];
|
|
wGreen += st->pReadImageHead[wGLinePosEven + (i + 1) * 3 + 1];
|
|
wGreen /= 2;
|
|
|
|
wBlue = st->pReadImageHead[wBLinePosOdd + i * 3 + 2];
|
|
wBlue += st->pReadImageHead[wBLinePosEven + (i + 1) * 3 + 2];
|
|
wBlue /= 2;
|
|
|
|
tempR = (wRed << 4) | QBET4 (wBlue, wGreen);
|
|
tempG = (wGreen << 4) | QBET4 (wRed, wBlue);
|
|
tempB = (wBlue << 4) | QBET4 (wGreen, wRed);
|
|
|
|
SetPixel24Bit (pLine + (i * 3), st->pGammaTable, tempR, tempG, tempB,
|
|
isOrderInvert);
|
|
i++;
|
|
|
|
wRed = st->pReadImageHead[wRLinePosEven + i * 3];
|
|
wRed += st->pReadImageHead[wRLinePosOdd + (i + 1) * 3];
|
|
wRed /= 2;
|
|
|
|
wGreen = st->pReadImageHead[wGLinePosEven + i * 3 + 1];
|
|
wGreen += st->pReadImageHead[wGLinePosOdd + (i + 1) * 3 + 1];
|
|
wGreen /= 2;
|
|
|
|
wBlue = st->pReadImageHead[wBLinePosEven + i * 3 + 2];
|
|
wBlue += st->pReadImageHead[wBLinePosOdd + (i + 1) * 3 + 2];
|
|
wBlue /= 2;
|
|
|
|
tempR = (wRed << 4) | QBET4 (wBlue, wGreen);
|
|
tempG = (wGreen << 4) | QBET4 (wRed, wBlue);
|
|
tempB = (wBlue << 4) | QBET4 (wGreen, wRed);
|
|
|
|
SetPixel24Bit (pLine + (i * 3), st->pGammaTable, tempR, tempG, tempB,
|
|
isOrderInvert);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono16BitLineHalf (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned short wTempData;
|
|
unsigned short wLinePos;
|
|
unsigned short i;
|
|
|
|
wLinePos = (st->wtheReadyLines % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
for (i = 0; i < st->SWWidth; i++)
|
|
{
|
|
wTempData = st->pReadImageHead[wLinePos + i * 2] |
|
|
(st->pReadImageHead[wLinePos + i * 2 + 1] << 8);
|
|
LE16TOH (wTempData);
|
|
*((unsigned short *) (pLine + (i * 2))) = st->pGammaTable[wTempData];
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono16BitLineFull (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned int dwTempData;
|
|
unsigned short wLinePosOdd;
|
|
unsigned short wLinePosEven;
|
|
unsigned short i = 0;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wLinePosOdd = st->wtheReadyLines - st->wPixelDistance;
|
|
wLinePosEven = st->wtheReadyLines;
|
|
}
|
|
else
|
|
{
|
|
wLinePosOdd = st->wtheReadyLines;
|
|
wLinePosEven = st->wtheReadyLines - st->wPixelDistance;
|
|
}
|
|
wLinePosOdd = (wLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wLinePosEven = (wLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
while (i < st->SWWidth)
|
|
{
|
|
if ((i + 1) >= st->SWWidth)
|
|
break;
|
|
|
|
dwTempData = (unsigned int) st->pReadImageHead[wLinePosOdd + i * 2];
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosOdd + i * 2 + 1] << 8;
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosEven + (i + 1) * 2];
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosEven + (i + 1) * 2 + 1] << 8;
|
|
dwTempData /= 2;
|
|
LE16TOH (dwTempData);
|
|
|
|
*((unsigned short *) (pLine + (i * 2))) = st->pGammaTable[dwTempData];
|
|
i++;
|
|
|
|
dwTempData = (unsigned int) st->pReadImageHead[wLinePosEven + i * 2];
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosEven + i * 2 + 1] << 8;
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosOdd + (i + 1) * 2];
|
|
dwTempData += (unsigned int)
|
|
st->pReadImageHead[wLinePosOdd + (i + 1) * 2 + 1] << 8;
|
|
dwTempData /= 2;
|
|
LE16TOH (dwTempData);
|
|
|
|
*((unsigned short *) (pLine + (i * 2))) = st->pGammaTable[dwTempData];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono8BitLineHalf (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned int dwTempData;
|
|
unsigned short wLinePos;
|
|
unsigned short i;
|
|
|
|
wLinePos = (st->wtheReadyLines % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
for (i = 0; i < st->SWWidth; i++)
|
|
{
|
|
dwTempData = (st->pReadImageHead[wLinePos + i] << 4) | (rand () & 0x0f);
|
|
pLine[i] = (SANE_Byte) st->pGammaTable[dwTempData];
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono8BitLineFull (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned short wLinePosOdd;
|
|
unsigned short wLinePosEven;
|
|
unsigned short wGray;
|
|
unsigned short i = 0;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wLinePosOdd = (st->wtheReadyLines - st->wPixelDistance) %
|
|
st->wMaxScanLines;
|
|
wLinePosEven = (st->wtheReadyLines) % st->wMaxScanLines;
|
|
}
|
|
else
|
|
{
|
|
wLinePosOdd = (st->wtheReadyLines) % st->wMaxScanLines;
|
|
wLinePosEven = (st->wtheReadyLines - st->wPixelDistance) %
|
|
st->wMaxScanLines;
|
|
}
|
|
wLinePosOdd = (wLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wLinePosEven = (wLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
while (i < st->SWWidth)
|
|
{
|
|
if ((i + 1) >= st->SWWidth)
|
|
break;
|
|
|
|
wGray = st->pReadImageHead[wLinePosOdd + i];
|
|
wGray += st->pReadImageHead[wLinePosEven + i + 1];
|
|
wGray /= 2;
|
|
|
|
pLine[i] = (SANE_Byte) st->pGammaTable[(wGray << 4) | (rand () & 0x0f)];
|
|
i++;
|
|
|
|
wGray = st->pReadImageHead[wLinePosEven + i];
|
|
wGray += st->pReadImageHead[wLinePosOdd + i + 1];
|
|
wGray /= 2;
|
|
|
|
pLine[i] = (SANE_Byte) st->pGammaTable[(wGray << 4) | (rand () & 0x0f)];
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono1BitLineHalf (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned short wLinePos;
|
|
unsigned short i;
|
|
|
|
wLinePos = (st->wtheReadyLines % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
for (i = 0; i < st->SWWidth; i++)
|
|
{
|
|
if (st->pReadImageHead[wLinePos + i] <= st->Target.wLineartThreshold)
|
|
pLine[i / 8] |= 0x80 >> (i % 8);
|
|
}
|
|
}
|
|
|
|
static void
|
|
GetMono1BitLineFull (Scanner_State * st, SANE_Byte * pLine,
|
|
SANE_Bool __sane_unused__ isOrderInvert)
|
|
{
|
|
unsigned short wLinePosOdd;
|
|
unsigned short wLinePosEven;
|
|
unsigned short i = 0;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wLinePosOdd = (st->wtheReadyLines - st->wPixelDistance) %
|
|
st->wMaxScanLines;
|
|
wLinePosEven = (st->wtheReadyLines) % st->wMaxScanLines;
|
|
}
|
|
else
|
|
{
|
|
wLinePosOdd = (st->wtheReadyLines) % st->wMaxScanLines;
|
|
wLinePosEven = (st->wtheReadyLines - st->wPixelDistance) %
|
|
st->wMaxScanLines;
|
|
}
|
|
wLinePosOdd = (wLinePosOdd % st->wMaxScanLines) * st->BytesPerRow;
|
|
wLinePosEven = (wLinePosEven % st->wMaxScanLines) * st->BytesPerRow;
|
|
|
|
while (i < st->SWWidth)
|
|
{
|
|
if ((i + 1) >= st->SWWidth)
|
|
break;
|
|
|
|
if (st->pReadImageHead[wLinePosOdd + i] <= st->Target.wLineartThreshold)
|
|
pLine[i / 8] |= 0x80 >> (i % 8);
|
|
i++;
|
|
|
|
if (st->pReadImageHead[wLinePosEven + i] <= st->Target.wLineartThreshold)
|
|
pLine[i / 8] |= 0x80 >> (i % 8);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
static unsigned int
|
|
GetScannedLines (Scanner_State * st)
|
|
{
|
|
unsigned int dwScannedLines;
|
|
|
|
pthread_mutex_lock (&st->scannedLinesMutex);
|
|
dwScannedLines = st->dwScannedTotalLines;
|
|
pthread_mutex_unlock (&st->scannedLinesMutex);
|
|
|
|
return dwScannedLines;
|
|
}
|
|
|
|
static unsigned int
|
|
GetReadyLines (Scanner_State * st)
|
|
{
|
|
unsigned int dwReadyLines;
|
|
|
|
pthread_mutex_lock (&st->readyLinesMutex);
|
|
dwReadyLines = st->wtheReadyLines;
|
|
pthread_mutex_unlock (&st->readyLinesMutex);
|
|
|
|
return dwReadyLines;
|
|
}
|
|
|
|
static void
|
|
AddScannedLines (Scanner_State * st, unsigned short wAddLines)
|
|
{
|
|
pthread_mutex_lock (&st->scannedLinesMutex);
|
|
st->dwScannedTotalLines += wAddLines;
|
|
pthread_mutex_unlock (&st->scannedLinesMutex);
|
|
}
|
|
|
|
static void
|
|
AddReadyLines (Scanner_State * st)
|
|
{
|
|
pthread_mutex_lock (&st->readyLinesMutex);
|
|
st->wtheReadyLines++;
|
|
pthread_mutex_unlock (&st->readyLinesMutex);
|
|
}
|
|
|
|
static void
|
|
ModifyLinePoint (SANE_Byte * pImageData, SANE_Byte * pImageDataBefore,
|
|
unsigned int dwBytesPerLine, unsigned int dwLinesCount,
|
|
unsigned short wPixDistance, unsigned short wModPtCount)
|
|
{
|
|
unsigned short i, j;
|
|
unsigned short wLines;
|
|
unsigned int dwWidth = dwBytesPerLine / wPixDistance;
|
|
for (i = wModPtCount; i > 0; i--)
|
|
{
|
|
for (j = 0; j < wPixDistance; j++)
|
|
{
|
|
unsigned int lineOffset = (dwWidth - i) * wPixDistance + j;
|
|
unsigned int prevLineOffset = (dwWidth - i - 1) * wPixDistance + j;
|
|
|
|
/* modify the first line */
|
|
pImageData[lineOffset] =
|
|
(pImageData[prevLineOffset] + pImageDataBefore[prevLineOffset]) / 2;
|
|
|
|
/* modify other lines */
|
|
for (wLines = 1; wLines < dwLinesCount; wLines++)
|
|
{
|
|
unsigned int dwBytesBefore = (wLines - 1) * dwBytesPerLine;
|
|
unsigned int dwBytes = wLines * dwBytesPerLine;
|
|
pImageData[dwBytes + lineOffset] =
|
|
(pImageData[dwBytes + prevLineOffset] +
|
|
pImageData[dwBytesBefore + prevLineOffset]) / 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void *
|
|
ReadDataFromScanner (void * param)
|
|
{
|
|
Scanner_State * st = param;
|
|
unsigned short wTotalReadImageLines = 0;
|
|
unsigned short wWantedLines = st->Target.wHeight;
|
|
SANE_Byte * pReadImage = st->pReadImageHead;
|
|
SANE_Bool isWaitImageLineDiff = SANE_FALSE;
|
|
unsigned int wMaxScanLines = st->wMaxScanLines;
|
|
unsigned short wReadImageLines = 0;
|
|
unsigned short wScanLinesThisBlock;
|
|
unsigned short wBufferLines = st->wLineDistance * 2 + st->wPixelDistance;
|
|
DBG_ENTER ();
|
|
DBG (DBG_FUNC, "ReadDataFromScanner: new thread\n");
|
|
|
|
while (wTotalReadImageLines < wWantedLines)
|
|
{
|
|
if (!isWaitImageLineDiff)
|
|
{
|
|
wScanLinesThisBlock =
|
|
(wWantedLines - wTotalReadImageLines) < st->wScanLinesPerBlock ?
|
|
(wWantedLines - wTotalReadImageLines) : st->wScanLinesPerBlock;
|
|
|
|
DBG (DBG_FUNC, "ReadDataFromScanner: wWantedLines=%d\n",
|
|
wWantedLines);
|
|
DBG (DBG_FUNC, "ReadDataFromScanner: wScanLinesThisBlock=%d\n",
|
|
wScanLinesThisBlock);
|
|
|
|
/* This call is thread-safe under the assumption that no function that
|
|
might run concurrently will cause a register bank switch. */
|
|
if (Asic_ReadImage (&st->chip, pReadImage, wScanLinesThisBlock) !=
|
|
SANE_STATUS_GOOD)
|
|
{
|
|
DBG (DBG_FUNC, "ReadDataFromScanner: thread exit\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* has read in memory buffer */
|
|
wReadImageLines += wScanLinesThisBlock;
|
|
AddScannedLines (st, wScanLinesThisBlock);
|
|
wTotalReadImageLines += wScanLinesThisBlock;
|
|
pReadImage += wScanLinesThisBlock * st->BytesPerRow;
|
|
|
|
/* buffer is full */
|
|
if (wReadImageLines >= wMaxScanLines)
|
|
{
|
|
pReadImage = st->pReadImageHead;
|
|
wReadImageLines = 0;
|
|
}
|
|
|
|
if (st->dwScannedTotalLines >= (GetReadyLines (st) +
|
|
(wMaxScanLines - (wBufferLines + st->wScanLinesPerBlock))))
|
|
{
|
|
isWaitImageLineDiff = SANE_TRUE;
|
|
}
|
|
}
|
|
else if (st->dwScannedTotalLines <=
|
|
GetReadyLines (st) + wBufferLines + st->wScanLinesPerBlock)
|
|
{
|
|
isWaitImageLineDiff = SANE_FALSE;
|
|
}
|
|
|
|
pthread_testcancel ();
|
|
}
|
|
|
|
DBG (DBG_FUNC, "ReadDataFromScanner: read OK, exit thread\n");
|
|
DBG_LEAVE ();
|
|
return NULL;
|
|
}
|
|
|
|
static SANE_Status
|
|
GetLine (Scanner_State * st, SANE_Byte * pLine, unsigned short * wLinesCount,
|
|
void (* pFunc)(Scanner_State *, SANE_Byte *, SANE_Bool),
|
|
SANE_Bool isOrderInvert, SANE_Bool fixEvenOdd)
|
|
{
|
|
unsigned short wWantedTotalLines;
|
|
unsigned short TotalXferLines = 0;
|
|
SANE_Byte * pFirstLine = pLine;
|
|
unsigned int i;
|
|
DBG_ENTER ();
|
|
|
|
wWantedTotalLines = *wLinesCount;
|
|
|
|
if (st->bFirstReadImage)
|
|
{
|
|
pthread_create (&st->threadid_readimage, NULL, ReadDataFromScanner, st);
|
|
DBG (DBG_FUNC, "thread started\n");
|
|
st->bFirstReadImage = SANE_FALSE;
|
|
}
|
|
|
|
while (TotalXferLines < wWantedTotalLines)
|
|
{
|
|
if (st->dwTotalTotalXferLines >= st->SWHeight)
|
|
{
|
|
pthread_cancel (st->threadid_readimage);
|
|
pthread_join (st->threadid_readimage, NULL);
|
|
DBG (DBG_FUNC, "thread finished\n");
|
|
|
|
*wLinesCount = TotalXferLines;
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
if (GetScannedLines (st) > st->wtheReadyLines)
|
|
{
|
|
pFunc (st, pLine, isOrderInvert);
|
|
|
|
TotalXferLines++;
|
|
st->dwTotalTotalXferLines++;
|
|
pLine += st->SWBytesPerRow;
|
|
AddReadyLines (st);
|
|
}
|
|
|
|
if (st->isCanceled)
|
|
{
|
|
pthread_join (st->threadid_readimage, NULL);
|
|
DBG (DBG_FUNC, "thread finished\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
*wLinesCount = TotalXferLines;
|
|
|
|
if (fixEvenOdd)
|
|
{
|
|
/* modify the last point */
|
|
if (st->bIsFirstReadBefData)
|
|
{
|
|
st->pBefLineImageData = malloc (st->SWBytesPerRow);
|
|
if (!st->pBefLineImageData)
|
|
return SANE_STATUS_NO_MEM;
|
|
memcpy (st->pBefLineImageData, pFirstLine, st->SWBytesPerRow);
|
|
st->bIsFirstReadBefData = SANE_FALSE;
|
|
st->dwAlreadyGetLines = 0;
|
|
}
|
|
|
|
ModifyLinePoint (pFirstLine, st->pBefLineImageData, st->SWBytesPerRow,
|
|
wWantedTotalLines, 1, 4);
|
|
|
|
memcpy (st->pBefLineImageData,
|
|
pFirstLine + (wWantedTotalLines - 1) * st->SWBytesPerRow,
|
|
st->SWBytesPerRow);
|
|
st->dwAlreadyGetLines += wWantedTotalLines;
|
|
if (st->dwAlreadyGetLines >= st->SWHeight)
|
|
{
|
|
DBG (DBG_FUNC, "freeing pBefLineImageData\n");
|
|
free (st->pBefLineImageData);
|
|
st->bIsFirstReadBefData = SANE_TRUE;
|
|
}
|
|
}
|
|
|
|
if (st->Target.ssScanSource == SS_NEGATIVE)
|
|
{
|
|
for (i = 0; i < (*wLinesCount * st->SWBytesPerRow); i++)
|
|
pFirstLine[i] ^= 0xff;
|
|
}
|
|
|
|
DBG_LEAVE ();
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_GetRows (Scanner_State * st, SANE_Byte * pBlock,
|
|
unsigned short * pNumRows, SANE_Bool isOrderInvert)
|
|
{
|
|
SANE_Status status;
|
|
SANE_Bool fixEvenOdd = SANE_FALSE;
|
|
void (* pFunc)(Scanner_State *, SANE_Byte *, SANE_Bool) = NULL;
|
|
DBG_ENTER ();
|
|
|
|
if (!st->bOpened || !st->bPrepared)
|
|
{
|
|
DBG (DBG_FUNC, "invalid state\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
switch (st->Target.cmColorMode)
|
|
{
|
|
case CM_RGB48:
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
pFunc = GetRgb48BitLineFull;
|
|
else
|
|
pFunc = GetRgb48BitLineHalf;
|
|
break;
|
|
|
|
case CM_RGB24:
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
pFunc = GetRgb24BitLineFull;
|
|
else
|
|
pFunc = GetRgb24BitLineHalf;
|
|
break;
|
|
|
|
case CM_GRAY16:
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
{
|
|
fixEvenOdd = SANE_TRUE;
|
|
pFunc = GetMono16BitLineFull;
|
|
}
|
|
else
|
|
{
|
|
pFunc = GetMono16BitLineHalf;
|
|
}
|
|
break;
|
|
|
|
case CM_GRAY8:
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
{
|
|
fixEvenOdd = SANE_TRUE;
|
|
pFunc = GetMono8BitLineFull;
|
|
}
|
|
else
|
|
{
|
|
pFunc = GetMono8BitLineHalf;
|
|
}
|
|
break;
|
|
|
|
case CM_TEXT:
|
|
memset (pBlock, 0, *pNumRows * st->SWBytesPerRow);
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
pFunc = GetMono1BitLineFull;
|
|
else
|
|
pFunc = GetMono1BitLineHalf;
|
|
break;
|
|
}
|
|
|
|
status = GetLine (st, pBlock, pNumRows, pFunc, isOrderInvert, fixEvenOdd);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_StopScan (Scanner_State * st)
|
|
{
|
|
SANE_Status status;
|
|
DBG_ENTER ();
|
|
|
|
if (!st->bOpened || !st->bPrepared)
|
|
{
|
|
DBG (DBG_FUNC, "invalid state\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
st->isCanceled = SANE_TRUE;
|
|
|
|
pthread_cancel (st->threadid_readimage);
|
|
pthread_join (st->threadid_readimage, NULL);
|
|
DBG (DBG_FUNC, "thread finished\n");
|
|
|
|
status = Asic_ScanStop (&st->chip);
|
|
Asic_Close (&st->chip);
|
|
|
|
st->bOpened = SANE_FALSE;
|
|
|
|
free (st->pGammaTable);
|
|
st->pGammaTable = NULL;
|
|
free (st->pReadImageHead);
|
|
st->pReadImageHead = NULL;
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
static SANE_Status
|
|
PrepareScan (Scanner_State * st)
|
|
{
|
|
SANE_Status status;
|
|
DBG_ENTER ();
|
|
|
|
st->isCanceled = SANE_FALSE;
|
|
|
|
st->wScanLinesPerBlock = BLOCK_SIZE / st->BytesPerRow;
|
|
st->wMaxScanLines = ((IMAGE_BUFFER_SIZE / st->BytesPerRow) /
|
|
st->wScanLinesPerBlock) * st->wScanLinesPerBlock;
|
|
st->dwScannedTotalLines = 0;
|
|
|
|
switch (st->Target.cmColorMode)
|
|
{
|
|
case CM_RGB48:
|
|
case CM_RGB24:
|
|
st->wtheReadyLines = st->wLineDistance * 2 + st->wPixelDistance;
|
|
break;
|
|
case CM_GRAY16:
|
|
case CM_GRAY8:
|
|
case CM_TEXT:
|
|
st->wtheReadyLines = st->wPixelDistance;
|
|
break;
|
|
}
|
|
DBG (DBG_FUNC, "st->wtheReadyLines=%d\n", st->wtheReadyLines);
|
|
|
|
DBG (DBG_FUNC, "allocate %d bytes for st->pReadImageHead\n",
|
|
IMAGE_BUFFER_SIZE);
|
|
st->pReadImageHead = malloc (IMAGE_BUFFER_SIZE);
|
|
if (!st->pReadImageHead)
|
|
{
|
|
DBG (DBG_FUNC, "st->pReadImageHead == NULL\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
free (st->pReadImageHead);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_Reset (Scanner_State * st)
|
|
{
|
|
DBG_ENTER ();
|
|
|
|
if (st->bOpened)
|
|
{
|
|
DBG (DBG_FUNC, "scanner already open\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
st->dwTotalTotalXferLines = 0;
|
|
st->bFirstReadImage = SANE_TRUE;
|
|
st->bPrepared = SANE_TRUE;
|
|
|
|
DBG_LEAVE ();
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static void
|
|
PrepareCalculateMaxMin (unsigned short wResolution,
|
|
CALIBRATIONPARAM * pCalParam)
|
|
{
|
|
unsigned short wCalWidth, wDarkCalWidth;
|
|
|
|
wDarkCalWidth = 52;
|
|
if (wResolution <= (SENSOR_DPI / 2))
|
|
{
|
|
wCalWidth = (5120 * wResolution / (SENSOR_DPI / 2) + 511) & ~511;
|
|
wDarkCalWidth *= wResolution / SENSOR_DPI;
|
|
|
|
if (wResolution < 200)
|
|
{
|
|
pCalParam->nPowerNum = 3;
|
|
pCalParam->nSecLength = 8; /* 2^nPowerNum */
|
|
/* dark has at least 2 sections */
|
|
pCalParam->nDarkSecLength = wDarkCalWidth / 2;
|
|
}
|
|
else
|
|
{
|
|
pCalParam->nPowerNum = 6;
|
|
pCalParam->nSecLength = 64; /* 2^nPowerNum */
|
|
pCalParam->nDarkSecLength = wDarkCalWidth / 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pCalParam->nPowerNum = 6;
|
|
pCalParam->nSecLength = 64; /* 2^nPowerNum */
|
|
wCalWidth = 10240;
|
|
pCalParam->nDarkSecLength = wDarkCalWidth / 5;
|
|
}
|
|
|
|
if (pCalParam->nDarkSecLength <= 0)
|
|
pCalParam->nDarkSecLength = 1;
|
|
|
|
pCalParam->wStartPosition = 13 * wResolution / SENSOR_DPI;
|
|
wCalWidth -= pCalParam->wStartPosition;
|
|
|
|
/* start of find max value */
|
|
pCalParam->nSecNum = wCalWidth / pCalParam->nSecLength;
|
|
|
|
/* start of find min value */
|
|
pCalParam->nDarkSecNum = wDarkCalWidth / pCalParam->nDarkSecLength;
|
|
}
|
|
|
|
static SANE_Status
|
|
CalculateMaxMin (CALIBRATIONPARAM * pCalParam, SANE_Byte * pBuffer,
|
|
unsigned short * pMaxValue, unsigned short * pMinValue)
|
|
{
|
|
unsigned short *wSecData, *wDarkSecData;
|
|
int i, j;
|
|
|
|
wSecData = malloc (pCalParam->nSecNum * sizeof (unsigned short));
|
|
if (!wSecData)
|
|
return SANE_STATUS_NO_MEM;
|
|
memset (wSecData, 0, pCalParam->nSecNum * sizeof (unsigned short));
|
|
|
|
for (i = 0; i < pCalParam->nSecNum; i++)
|
|
{
|
|
for (j = 0; j < pCalParam->nSecLength; j++)
|
|
wSecData[i] += pBuffer[pCalParam->wStartPosition +
|
|
i * pCalParam->nSecLength + j];
|
|
wSecData[i] >>= pCalParam->nPowerNum;
|
|
}
|
|
|
|
*pMaxValue = wSecData[0];
|
|
for (i = 0; i < pCalParam->nSecNum; i++)
|
|
{
|
|
if (*pMaxValue < wSecData[i])
|
|
*pMaxValue = wSecData[i];
|
|
}
|
|
free (wSecData);
|
|
|
|
wDarkSecData = malloc (pCalParam->nDarkSecNum * sizeof (unsigned short));
|
|
if (!wDarkSecData)
|
|
return SANE_STATUS_NO_MEM;
|
|
memset (wDarkSecData, 0, pCalParam->nDarkSecNum * sizeof (unsigned short));
|
|
|
|
for (i = 0; i < pCalParam->nDarkSecNum; i++)
|
|
{
|
|
for (j = 0; j < pCalParam->nDarkSecLength; j++)
|
|
wDarkSecData[i] += pBuffer[pCalParam->wStartPosition +
|
|
i * pCalParam->nDarkSecLength + j];
|
|
wDarkSecData[i] /= pCalParam->nDarkSecLength;
|
|
}
|
|
|
|
*pMinValue = wDarkSecData[0];
|
|
for (i = 0; i < pCalParam->nDarkSecNum; i++)
|
|
{
|
|
if (*pMinValue > wDarkSecData[i])
|
|
*pMinValue = wDarkSecData[i];
|
|
}
|
|
free (wDarkSecData);
|
|
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
static SANE_Status
|
|
AdjustAD (Scanner_State * st)
|
|
{
|
|
SANE_Status status;
|
|
CALIBRATIONPARAM calParam;
|
|
SANE_Byte * pCalData;
|
|
unsigned short wCalWidth;
|
|
unsigned short wMaxValue[3], wMinValue[3];
|
|
unsigned short wAdjustADResolution;
|
|
int i, j;
|
|
double range;
|
|
#if DEBUG_SAVE_IMAGE
|
|
FILE * stream;
|
|
char buf[16];
|
|
#endif
|
|
DBG_ENTER ();
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
st->chip.AD.Direction[i] = DIR_POSITIVE;
|
|
st->chip.AD.Gain[i] = 0;
|
|
}
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
st->chip.AD.Offset[0] = 152; /* red */
|
|
st->chip.AD.Offset[1] = 56; /* green */
|
|
st->chip.AD.Offset[2] = 8; /* blue */
|
|
}
|
|
else
|
|
{
|
|
st->chip.AD.Offset[0] = 159;
|
|
st->chip.AD.Offset[1] = 50;
|
|
st->chip.AD.Offset[2] = 45;
|
|
}
|
|
|
|
if (st->Target.wXDpi <= (SENSOR_DPI / 2))
|
|
wAdjustADResolution = SENSOR_DPI / 2;
|
|
else
|
|
wAdjustADResolution = SENSOR_DPI;
|
|
|
|
wCalWidth = 10240;
|
|
pCalData = malloc (wCalWidth * 3);
|
|
if (!pCalData)
|
|
{
|
|
DBG (DBG_FUNC, "pCalData == NULL\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
status = Asic_SetWindow (&st->chip, st->Target.ssScanSource,
|
|
SCAN_TYPE_CALIBRATE_DARK, 24,
|
|
wAdjustADResolution, wAdjustADResolution, 0, 0,
|
|
wCalWidth, 1);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
PrepareCalculateMaxMin (wAdjustADResolution, &calParam);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
DBG (DBG_FUNC, "first AD offset adjustment loop\n");
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ReadCalibrationData (&st->chip, pCalData, wCalWidth * 3,
|
|
SANE_TRUE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
#ifdef DEBUG_SAVE_IMAGE
|
|
if (i == 0)
|
|
{
|
|
stream = fopen ("AD.ppm", "wb");
|
|
snprintf (buf, sizeof (buf), "P6\n%u 1\n255\n", wCalWidth);
|
|
fwrite (buf, 1, strlen (buf), stream);
|
|
fwrite (pCalData, 1, wCalWidth * 3, stream);
|
|
fclose (stream);
|
|
}
|
|
#endif
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
status = CalculateMaxMin (&calParam, pCalData + (wCalWidth * j),
|
|
&wMaxValue[j], &wMinValue[j]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
if (st->chip.AD.Direction[j] == DIR_POSITIVE)
|
|
{
|
|
if (wMinValue[j] > 15)
|
|
{
|
|
if (st->chip.AD.Offset[j] < 8)
|
|
st->chip.AD.Direction[j] = DIR_NEGATIVE;
|
|
else
|
|
st->chip.AD.Offset[j] -= 8;
|
|
}
|
|
else if (wMinValue[j] < 5)
|
|
st->chip.AD.Offset[j] += 8;
|
|
}
|
|
else
|
|
{
|
|
if (wMinValue[j] > 15)
|
|
st->chip.AD.Offset[j] += 8;
|
|
else if (wMinValue[j] < 5)
|
|
st->chip.AD.Offset[j] -= 8;
|
|
}
|
|
}
|
|
|
|
if (!(wMinValue[0] > 15 || wMinValue[0] < 5 ||
|
|
wMinValue[1] > 15 || wMinValue[1] < 5 ||
|
|
wMinValue[2] > 15 || wMinValue[2] < 5))
|
|
break;
|
|
}
|
|
|
|
DBG (DBG_FUNC, "OffsetR=%d,OffsetG=%d,OffsetB=%d\n",
|
|
st->chip.AD.Offset[0], st->chip.AD.Offset[1], st->chip.AD.Offset[2]);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
range = 1.0 - (double) (wMaxValue[i] - wMinValue[i]) / MAX_LEVEL_RANGE;
|
|
if (range > 0)
|
|
{
|
|
st->chip.AD.Gain[i] = (SANE_Byte) range * 63 * 6 / 5;
|
|
st->chip.AD.Gain[i] = _MIN (st->chip.AD.Gain[i], 63);
|
|
}
|
|
else
|
|
st->chip.AD.Gain[i] = 0;
|
|
}
|
|
|
|
DBG (DBG_FUNC, "GainR=%d,GainG=%d,GainB=%d\n",
|
|
st->chip.AD.Gain[0], st->chip.AD.Gain[1], st->chip.AD.Gain[2]);
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ReadCalibrationData (&st->chip, pCalData, wCalWidth * 3,
|
|
SANE_TRUE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
status = CalculateMaxMin (&calParam, pCalData + (wCalWidth * j),
|
|
&wMaxValue[j], &wMinValue[j]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
DBG (DBG_FUNC, "%d: Max=%d, Min=%d\n", j, wMaxValue[j], wMinValue[j]);
|
|
|
|
if ((wMaxValue[j] - wMinValue[j]) > MAX_LEVEL_RANGE)
|
|
{
|
|
if (st->chip.AD.Gain[j] != 0)
|
|
st->chip.AD.Gain[j]--;
|
|
}
|
|
else if ((wMaxValue[j] - wMinValue[j]) < MIN_LEVEL_RANGE)
|
|
{
|
|
if (wMaxValue[j] > WHITE_MAX_LEVEL)
|
|
{
|
|
if (st->chip.AD.Gain[j] != 0)
|
|
st->chip.AD.Gain[j]--;
|
|
}
|
|
else
|
|
{
|
|
if (st->chip.AD.Gain[j] < 63)
|
|
st->chip.AD.Gain[j]++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wMaxValue[j] > WHITE_MAX_LEVEL)
|
|
{
|
|
if (st->chip.AD.Gain[j] != 0)
|
|
st->chip.AD.Gain[j]--;
|
|
}
|
|
else if (wMaxValue[j] < WHITE_MIN_LEVEL)
|
|
{
|
|
if (st->chip.AD.Gain[j] < 63)
|
|
st->chip.AD.Gain[j]++;
|
|
}
|
|
}
|
|
|
|
DBG (DBG_FUNC, "%d: Gain=%d,Offset=%d,Dir=%d\n",
|
|
j, st->chip.AD.Gain[j], st->chip.AD.Offset[j],
|
|
st->chip.AD.Direction[j]);
|
|
}
|
|
|
|
if (!((wMaxValue[0] - wMinValue[0]) > MAX_LEVEL_RANGE ||
|
|
(wMaxValue[0] - wMinValue[0]) < MIN_LEVEL_RANGE ||
|
|
(wMaxValue[1] - wMinValue[1]) > MAX_LEVEL_RANGE ||
|
|
(wMaxValue[1] - wMinValue[1]) < MIN_LEVEL_RANGE ||
|
|
(wMaxValue[2] - wMinValue[2]) > MAX_LEVEL_RANGE ||
|
|
(wMaxValue[2] - wMinValue[2]) < MIN_LEVEL_RANGE))
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
DBG (DBG_FUNC, "second AD offset adjustment loop\n");
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ReadCalibrationData (&st->chip, pCalData, wCalWidth * 3,
|
|
SANE_TRUE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
status = CalculateMaxMin (&calParam, pCalData + (wCalWidth * j),
|
|
&wMaxValue[j], &wMinValue[j]);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
DBG (DBG_FUNC, "%d: Max=%d, Min=%d\n", j, wMaxValue[j], wMinValue[j]);
|
|
|
|
if (st->chip.AD.Direction[j] == DIR_POSITIVE)
|
|
{
|
|
if (wMinValue[j] > 20)
|
|
{
|
|
if (st->chip.AD.Offset[j] < 8)
|
|
st->chip.AD.Direction[j] = DIR_NEGATIVE;
|
|
else
|
|
st->chip.AD.Offset[j] -= 8;
|
|
}
|
|
else if (wMinValue[j] < 10)
|
|
{
|
|
st->chip.AD.Offset[j] += 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (wMinValue[j] > 20)
|
|
st->chip.AD.Offset[j] += 8;
|
|
else if (wMinValue[j] < 10)
|
|
st->chip.AD.Offset[j] -= 8;
|
|
}
|
|
|
|
DBG (DBG_FUNC, "%d: Gain=%d,Offset=%d,Dir=%d\n",
|
|
j, st->chip.AD.Gain[j], st->chip.AD.Offset[j],
|
|
st->chip.AD.Direction[j]);
|
|
}
|
|
|
|
if (!(wMinValue[0] > 20 || wMinValue[0] < 10 ||
|
|
wMinValue[1] > 20 || wMinValue[1] < 10 ||
|
|
wMinValue[2] > 20 || wMinValue[2] < 10))
|
|
break;
|
|
}
|
|
|
|
out:
|
|
free (pCalData);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
static SANE_Status
|
|
FindTopLeft (Scanner_State * st, unsigned short * pwStartX,
|
|
unsigned short * pwStartY)
|
|
{
|
|
SANE_Status status;
|
|
unsigned short wCalWidth, wCalHeight;
|
|
unsigned int dwTotalSize;
|
|
int nScanBlock;
|
|
SANE_Byte * pCalData;
|
|
unsigned short wLeftSide, wTopSide;
|
|
int i, j;
|
|
#ifdef DEBUG_SAVE_IMAGE
|
|
FILE * stream;
|
|
char buf[20];
|
|
#endif
|
|
DBG_ENTER ();
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
wCalWidth = FIND_LEFT_TOP_WIDTH_IN_DIP;
|
|
wCalHeight = FIND_LEFT_TOP_HEIGHT_IN_DIP;
|
|
}
|
|
else
|
|
{
|
|
wCalWidth = TA_FIND_LEFT_TOP_WIDTH_IN_DIP;
|
|
wCalHeight = TA_FIND_LEFT_TOP_HEIGHT_IN_DIP;
|
|
}
|
|
|
|
dwTotalSize = wCalWidth * wCalHeight;
|
|
pCalData = malloc (dwTotalSize);
|
|
if (!pCalData)
|
|
{
|
|
DBG (DBG_FUNC, "pCalData == NULL\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
nScanBlock = dwTotalSize / CALIBRATION_BLOCK_SIZE;
|
|
|
|
status = Asic_SetWindow (&st->chip, st->Target.ssScanSource,
|
|
SCAN_TYPE_CALIBRATE_LIGHT, 8,
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION,
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION, 0, 0,
|
|
wCalWidth, wCalHeight);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
for (i = 0; i < nScanBlock; i++)
|
|
{
|
|
status = Asic_ReadCalibrationData (&st->chip,
|
|
pCalData + (i * CALIBRATION_BLOCK_SIZE), CALIBRATION_BLOCK_SIZE,
|
|
SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
}
|
|
|
|
status = Asic_ReadCalibrationData (&st->chip,
|
|
pCalData + (nScanBlock * CALIBRATION_BLOCK_SIZE),
|
|
dwTotalSize - CALIBRATION_BLOCK_SIZE * nScanBlock, SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
|
|
#ifdef DEBUG_SAVE_IMAGE
|
|
stream = fopen ("bounds.pgm", "wb");
|
|
snprintf (buf, sizeof (buf), "P5\n%d %d\n255\n", wCalWidth, wCalHeight);
|
|
fwrite (buf, 1, strlen (buf), stream);
|
|
fwrite (pCalData, 1, dwTotalSize, stream);
|
|
fclose (stream);
|
|
#endif
|
|
|
|
/* find left side */
|
|
for (i = wCalWidth - 1; i > 0; i--)
|
|
{
|
|
wLeftSide = pCalData[i];
|
|
wLeftSide += pCalData[wCalWidth * 2 + i];
|
|
wLeftSide += pCalData[wCalWidth * 4 + i];
|
|
wLeftSide += pCalData[wCalWidth * 6 + i];
|
|
wLeftSide += pCalData[wCalWidth * 8 + i];
|
|
wLeftSide /= 5;
|
|
|
|
if (wLeftSide < 60)
|
|
{
|
|
if (i < (wCalWidth - 1))
|
|
*pwStartX = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
/* find top side, i = left side */
|
|
for (j = 0; j < wCalHeight; j++)
|
|
{
|
|
wTopSide = pCalData[wCalWidth * j + i - 2];
|
|
wTopSide += pCalData[wCalWidth * j + i - 4];
|
|
wTopSide += pCalData[wCalWidth * j + i - 6];
|
|
wTopSide += pCalData[wCalWidth * j + i - 8];
|
|
wTopSide += pCalData[wCalWidth * j + i - 10];
|
|
wTopSide /= 5;
|
|
|
|
if (wTopSide > 60)
|
|
{
|
|
if (j > 0)
|
|
*pwStartY = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((*pwStartX < 100) || (*pwStartX > 250))
|
|
*pwStartX = 187;
|
|
if ((*pwStartY < 10) || (*pwStartY > 100))
|
|
*pwStartY = 43;
|
|
|
|
status = Asic_MotorMove (&st->chip, SANE_FALSE,
|
|
(wCalHeight - *pwStartY + LINE_CALIBRATION_HEIGHT) *
|
|
SENSOR_DPI / FIND_LEFT_TOP_CALIBRATE_RESOLUTION);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
}
|
|
else
|
|
{
|
|
/* find top side, i = left side */
|
|
for (j = 0; j < wCalHeight; j++)
|
|
{
|
|
wTopSide = pCalData[wCalWidth * j + i + 2];
|
|
wTopSide += pCalData[wCalWidth * j + i + 4];
|
|
wTopSide += pCalData[wCalWidth * j + i + 6];
|
|
wTopSide += pCalData[wCalWidth * j + i + 8];
|
|
wTopSide += pCalData[wCalWidth * j + i + 10];
|
|
wTopSide /= 5;
|
|
|
|
if (wTopSide < 60)
|
|
{
|
|
if (j > 0)
|
|
*pwStartY = j;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((*pwStartX < 2200) || (*pwStartX > 2300))
|
|
*pwStartX = 2260;
|
|
if ((*pwStartY < 100) || (*pwStartY > 200))
|
|
*pwStartY = 124;
|
|
|
|
status = Asic_MotorMove (&st->chip, SANE_FALSE,
|
|
(wCalHeight - *pwStartY) * SENSOR_DPI /
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION + 300);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out;
|
|
}
|
|
|
|
DBG (DBG_FUNC, "pwStartY=%d,pwStartX=%d\n", *pwStartY, *pwStartX);
|
|
|
|
out:
|
|
free (pCalData);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
static unsigned short
|
|
FiltLower (unsigned short * pSort, unsigned short TotalCount,
|
|
unsigned short LowCount, unsigned short HighCount)
|
|
{
|
|
unsigned short Bound = TotalCount - 1;
|
|
unsigned short LeftCount = HighCount - LowCount;
|
|
int Temp;
|
|
unsigned int Sum = 0;
|
|
unsigned short i, j;
|
|
|
|
for (i = 0; i < Bound; i++)
|
|
{
|
|
for (j = 0; j < Bound - i; j++)
|
|
{
|
|
if (pSort[j + 1] > pSort[j])
|
|
{
|
|
Temp = pSort[j];
|
|
pSort[j] = pSort[j + 1];
|
|
pSort[j + 1] = Temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < LeftCount; i++)
|
|
Sum += pSort[i + LowCount];
|
|
return (unsigned short) (Sum / LeftCount);
|
|
}
|
|
|
|
static SANE_Status
|
|
LineCalibration16Bits (Scanner_State * st)
|
|
{
|
|
SANE_Status status;
|
|
unsigned short * pWhiteData, * pDarkData;
|
|
unsigned int dwTotalSize;
|
|
unsigned short wCalWidth, wCalHeight;
|
|
unsigned short * pWhiteShading, * pDarkShading;
|
|
double wRWhiteLevel, wGWhiteLevel, wBWhiteLevel;
|
|
unsigned int dwRDarkLevel = 0, dwGDarkLevel = 0, dwBDarkLevel = 0;
|
|
unsigned int dwREvenDarkLevel = 0, dwGEvenDarkLevel = 0, dwBEvenDarkLevel = 0;
|
|
unsigned short * pRWhiteSort, * pGWhiteSort, * pBWhiteSort;
|
|
unsigned short * pRDarkSort, * pGDarkSort, * pBDarkSort;
|
|
int i, j;
|
|
#ifdef DEBUG_SAVE_IMAGE
|
|
FILE * stream;
|
|
char buf[22];
|
|
#endif
|
|
DBG_ENTER ();
|
|
|
|
wCalWidth = st->Target.wWidth;
|
|
wCalHeight = LINE_CALIBRATION_HEIGHT;
|
|
dwTotalSize = wCalWidth * wCalHeight * 3 * sizeof (unsigned short);
|
|
DBG (DBG_FUNC, "wCalWidth=%d,wCalHeight=%d\n", wCalWidth, wCalHeight);
|
|
|
|
pWhiteData = malloc (dwTotalSize);
|
|
pDarkData = malloc (dwTotalSize);
|
|
if (!pWhiteData || !pDarkData)
|
|
{
|
|
DBG (DBG_FUNC, "malloc failed\n");
|
|
status = SANE_STATUS_NO_MEM;
|
|
goto out1;
|
|
}
|
|
|
|
/* read white level data */
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_SetWindow (&st->chip, st->Target.ssScanSource,
|
|
SCAN_TYPE_CALIBRATE_LIGHT, 48, st->Target.wXDpi, 600,
|
|
st->Target.wX, 0, wCalWidth, wCalHeight);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
status = Asic_ReadCalibrationData (&st->chip, (SANE_Byte *) pWhiteData,
|
|
dwTotalSize, SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
/* read dark level data */
|
|
SetAFEGainOffset (&st->chip);
|
|
status = Asic_SetWindow (&st->chip, st->Target.ssScanSource,
|
|
SCAN_TYPE_CALIBRATE_DARK, 48, st->Target.wXDpi, 600,
|
|
st->Target.wX, 0, wCalWidth, wCalHeight);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
status = Asic_TurnLamp (&st->chip, SANE_FALSE);
|
|
else
|
|
status = Asic_TurnTA (&st->chip, SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
usleep (500000);
|
|
|
|
status = Asic_ScanStart (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
status = Asic_ReadCalibrationData (&st->chip, (SANE_Byte *) pDarkData,
|
|
dwTotalSize, SANE_FALSE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
status = Asic_ScanStop (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
status = Asic_TurnLamp (&st->chip, SANE_TRUE);
|
|
else
|
|
status = Asic_TurnTA (&st->chip, SANE_TRUE);
|
|
if (status != SANE_STATUS_GOOD)
|
|
goto out1;
|
|
|
|
#ifdef DEBUG_SAVE_IMAGE
|
|
stream = fopen ("whiteshading.ppm", "wb");
|
|
snprintf (buf, sizeof (buf), "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
|
|
fwrite (buf, 1, strlen (buf), stream);
|
|
fwrite (pWhiteData, 1, dwTotalSize, stream);
|
|
fclose (stream);
|
|
|
|
stream = fopen ("darkshading.ppm", "wb");
|
|
snprintf (buf, sizeof (buf), "P6\n%d %d\n65535\n", wCalWidth, wCalHeight);
|
|
fwrite (buf, 1, strlen (buf), stream);
|
|
fwrite (pDarkData, 1, dwTotalSize, stream);
|
|
fclose (stream);
|
|
#endif
|
|
|
|
sleep (1);
|
|
|
|
pWhiteShading = malloc (sizeof (unsigned short) * wCalWidth * 3);
|
|
pDarkShading = malloc (sizeof (unsigned short) * wCalWidth * 3);
|
|
|
|
pRWhiteSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
pGWhiteSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
pBWhiteSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
pRDarkSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
pGDarkSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
pBDarkSort = malloc (sizeof (unsigned short) * wCalHeight);
|
|
|
|
if (!pWhiteShading || !pDarkShading ||
|
|
!pRWhiteSort || !pGWhiteSort || !pBWhiteSort ||
|
|
!pRDarkSort || !pGDarkSort || !pBDarkSort)
|
|
{
|
|
DBG (DBG_FUNC, "malloc failed\n");
|
|
status = SANE_STATUS_NO_MEM;
|
|
goto out2;
|
|
}
|
|
|
|
/* compute dark level */
|
|
for (i = 0; i < wCalWidth; i++)
|
|
{
|
|
for (j = 0; j < wCalHeight; j++)
|
|
{
|
|
pRDarkSort[j] = le16toh (pDarkData[j * wCalWidth * 3 + i * 3]);
|
|
pGDarkSort[j] = le16toh (pDarkData[j * wCalWidth * 3 + i * 3 + 1]);
|
|
pBDarkSort[j] = le16toh (pDarkData[j * wCalWidth * 3 + i * 3 + 2]);
|
|
}
|
|
|
|
/* sum of dark level for all pixels */
|
|
if ((st->Target.wXDpi == SENSOR_DPI) && ((i % 2) == 0))
|
|
{
|
|
/* compute dark shading table with mean */
|
|
dwREvenDarkLevel += FiltLower (pRDarkSort, wCalHeight, 20, 30);
|
|
dwGEvenDarkLevel += FiltLower (pGDarkSort, wCalHeight, 20, 30);
|
|
dwBEvenDarkLevel += FiltLower (pBDarkSort, wCalHeight, 20, 30);
|
|
}
|
|
else
|
|
{
|
|
dwRDarkLevel += FiltLower (pRDarkSort, wCalHeight, 20, 30);
|
|
dwGDarkLevel += FiltLower (pGDarkSort, wCalHeight, 20, 30);
|
|
dwBDarkLevel += FiltLower (pBDarkSort, wCalHeight, 20, 30);
|
|
}
|
|
}
|
|
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
{
|
|
dwRDarkLevel /= wCalWidth / 2;
|
|
dwGDarkLevel /= wCalWidth / 2;
|
|
dwBDarkLevel /= wCalWidth / 2;
|
|
dwREvenDarkLevel /= wCalWidth / 2;
|
|
dwGEvenDarkLevel /= wCalWidth / 2;
|
|
dwBEvenDarkLevel /= wCalWidth / 2;
|
|
}
|
|
else
|
|
{
|
|
dwRDarkLevel /= wCalWidth;
|
|
dwGDarkLevel /= wCalWidth;
|
|
dwBDarkLevel /= wCalWidth;
|
|
}
|
|
if (st->Target.ssScanSource != SS_REFLECTIVE)
|
|
{
|
|
dwRDarkLevel -= 512;
|
|
dwGDarkLevel -= 512;
|
|
dwBDarkLevel -= 512;
|
|
dwREvenDarkLevel -= 512;
|
|
dwGEvenDarkLevel -= 512;
|
|
dwBEvenDarkLevel -= 512;
|
|
}
|
|
|
|
/* compute white level and create shading tables */
|
|
for (i = 0; i < wCalWidth; i++)
|
|
{
|
|
for (j = 0; j < wCalHeight; j++)
|
|
{
|
|
pRWhiteSort[j] = le16toh (pWhiteData[j * wCalWidth * 3 + i * 3]);
|
|
pGWhiteSort[j] = le16toh (pWhiteData[j * wCalWidth * 3 + i * 3 + 1]);
|
|
pBWhiteSort[j] = le16toh (pWhiteData[j * wCalWidth * 3 + i * 3 + 2]);
|
|
}
|
|
|
|
if ((st->Target.wXDpi == SENSOR_DPI) && ((i % 2) == 0))
|
|
{
|
|
pDarkShading[i * 3] = dwREvenDarkLevel;
|
|
pDarkShading[i * 3 + 1] = dwGEvenDarkLevel;
|
|
if (st->Target.ssScanSource == SS_POSITIVE)
|
|
pDarkShading[i * 3 + 1] *= 0.78;
|
|
pDarkShading[i * 3 + 2] = dwBEvenDarkLevel;
|
|
}
|
|
else
|
|
{
|
|
pDarkShading[i * 3] = dwRDarkLevel;
|
|
pDarkShading[i * 3 + 1] = dwGDarkLevel;
|
|
if (st->Target.ssScanSource == SS_POSITIVE)
|
|
pDarkShading[i * 3 + 1] *= 0.78;
|
|
pDarkShading[i * 3 + 2] = dwBDarkLevel;
|
|
}
|
|
|
|
wRWhiteLevel = FiltLower (pRWhiteSort, wCalHeight, 20, 30) -
|
|
pDarkShading[i * 3];
|
|
wGWhiteLevel = FiltLower (pGWhiteSort, wCalHeight, 20, 30) -
|
|
pDarkShading[i * 3 + 1];
|
|
wBWhiteLevel = FiltLower (pBWhiteSort, wCalHeight, 20, 30) -
|
|
pDarkShading[i * 3 + 2];
|
|
|
|
switch (st->Target.ssScanSource)
|
|
{
|
|
case SS_REFLECTIVE:
|
|
if (wRWhiteLevel > 0)
|
|
pWhiteShading[i * 3] = (unsigned short)
|
|
(65535.0 / wRWhiteLevel * 0x2000);
|
|
else
|
|
pWhiteShading[i * 3] = 0x2000;
|
|
|
|
if (wGWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 1] = (unsigned short)
|
|
(65535.0 / wGWhiteLevel * 0x2000);
|
|
else
|
|
pWhiteShading[i * 3 + 1] = 0x2000;
|
|
|
|
if (wBWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 2] = (unsigned short)
|
|
(65535.0 / wBWhiteLevel * 0x2000);
|
|
else
|
|
pWhiteShading[i * 3 + 2] = 0x2000;
|
|
break;
|
|
|
|
case SS_NEGATIVE:
|
|
if (wRWhiteLevel > 0)
|
|
pWhiteShading[i * 3] = (unsigned short)
|
|
(65536.0 / wRWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3] = 0x1000;
|
|
|
|
if (wGWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 1] = (unsigned short)
|
|
((65536 * 1.5) / wGWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3 + 1] = 0x1000;
|
|
|
|
if (wBWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 2] = (unsigned short)
|
|
((65536 * 2.0) / wBWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3 + 2] = 0x1000;
|
|
break;
|
|
|
|
case SS_POSITIVE:
|
|
if (wRWhiteLevel > 0)
|
|
pWhiteShading[i * 3] = (unsigned short)
|
|
(65536.0 / wRWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3] = 0x1000;
|
|
|
|
if (wGWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 1] = (unsigned short)
|
|
((65536 * 1.04) / wGWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3 + 1] = 0x1000;
|
|
|
|
if (wBWhiteLevel > 0)
|
|
pWhiteShading[i * 3 + 2] = (unsigned short)
|
|
(65536.0 / wBWhiteLevel * 0x1000);
|
|
else
|
|
pWhiteShading[i * 3 + 2] = 0x1000;
|
|
break;
|
|
}
|
|
}
|
|
|
|
status = Asic_SetShadingTable (&st->chip, pWhiteShading, pDarkShading,
|
|
st->Target.wXDpi, wCalWidth);
|
|
|
|
out2:
|
|
free (pWhiteShading);
|
|
free (pDarkShading);
|
|
|
|
free (pRWhiteSort);
|
|
free (pGWhiteSort);
|
|
free (pBWhiteSort);
|
|
free (pRDarkSort);
|
|
free (pGDarkSort);
|
|
free (pBDarkSort);
|
|
|
|
out1:
|
|
free (pWhiteData);
|
|
free (pDarkData);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|
|
|
|
static SANE_Status
|
|
CreateGammaTable (COLORMODE cmColorMode, unsigned short ** pGammaTable)
|
|
{
|
|
unsigned short * pTable = NULL;
|
|
DBG_ENTER ();
|
|
|
|
if ((cmColorMode == CM_GRAY8) || (cmColorMode == CM_RGB24))
|
|
{
|
|
unsigned short i;
|
|
SANE_Byte bGammaData;
|
|
|
|
pTable = malloc (sizeof (unsigned short) * 4096 * 3);
|
|
if (!pTable)
|
|
{
|
|
DBG (DBG_ERR, "pTable == NULL\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
for (i = 0; i < 4096; i++)
|
|
{
|
|
bGammaData = (SANE_Byte) (pow ((double) i / 4095, 0.625) * 255);
|
|
pTable[i] = bGammaData;
|
|
pTable[i + 4096] = bGammaData;
|
|
pTable[i + 8192] = bGammaData;
|
|
}
|
|
}
|
|
else if ((cmColorMode == CM_GRAY16) || (cmColorMode == CM_RGB48))
|
|
{
|
|
unsigned int i;
|
|
unsigned short wGammaData;
|
|
|
|
pTable = malloc (sizeof (unsigned short) * 65536 * 3);
|
|
if (!pTable)
|
|
{
|
|
DBG (DBG_ERR, "pTable == NULL\n");
|
|
return SANE_STATUS_NO_MEM;
|
|
}
|
|
|
|
for (i = 0; i < 65536; i++)
|
|
{
|
|
wGammaData = (unsigned short)
|
|
(pow ((double) i / 65535, 0.625) * 65535);
|
|
pTable[i] = wGammaData;
|
|
pTable[i + 65536] = wGammaData;
|
|
pTable[i + 65536 * 2] = wGammaData;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBG (DBG_INFO, "no gamma table for this color mode\n");
|
|
}
|
|
|
|
*pGammaTable = pTable;
|
|
|
|
DBG_LEAVE ();
|
|
return SANE_STATUS_GOOD;
|
|
}
|
|
|
|
SANE_Status
|
|
Scanner_SetupScan (Scanner_State * st, TARGETIMAGE * pTarget)
|
|
{
|
|
SANE_Status status;
|
|
unsigned short finalY;
|
|
SANE_Byte bScanBits;
|
|
DBG_ENTER ();
|
|
|
|
if (st->bOpened || !st->bPrepared)
|
|
{
|
|
DBG (DBG_FUNC, "invalid state\n");
|
|
return SANE_STATUS_INVAL;
|
|
}
|
|
|
|
st->Target = *pTarget;
|
|
st->SWWidth = st->Target.wWidth;
|
|
st->SWHeight = st->Target.wHeight;
|
|
/* set real scan width according to ASIC limit: width must be 8x */
|
|
st->Target.wWidth = (st->Target.wWidth + 15) & ~15;
|
|
|
|
status = CreateGammaTable (st->Target.cmColorMode, &st->pGammaTable);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
switch (st->Target.wYDpi)
|
|
{
|
|
case 1200:
|
|
st->wPixelDistance = 4; /* even & odd sensor problem */
|
|
st->wLineDistance = 24;
|
|
st->Target.wHeight += st->wPixelDistance;
|
|
break;
|
|
case 600:
|
|
st->wPixelDistance = 0; /* no even & odd problem */
|
|
st->wLineDistance = 12;
|
|
break;
|
|
case 300:
|
|
st->wPixelDistance = 0;
|
|
st->wLineDistance = 6;
|
|
break;
|
|
case 150:
|
|
st->wPixelDistance = 0;
|
|
st->wLineDistance = 3;
|
|
break;
|
|
case 75:
|
|
case 50:
|
|
st->wPixelDistance = 0;
|
|
st->wLineDistance = 1;
|
|
break;
|
|
default:
|
|
st->wPixelDistance = 0;
|
|
st->wLineDistance = 0;
|
|
}
|
|
|
|
DBG (DBG_FUNC, "wYDpi=%d,st->wLineDistance=%d,st->wPixelDistance=%d\n",
|
|
st->Target.wYDpi, st->wLineDistance, st->wPixelDistance);
|
|
|
|
switch (st->Target.cmColorMode)
|
|
{
|
|
case CM_RGB48:
|
|
st->BytesPerRow = 6 * st->Target.wWidth;
|
|
st->SWBytesPerRow = 6 * st->SWWidth;
|
|
st->Target.wHeight += st->wLineDistance * 2;
|
|
bScanBits = 48;
|
|
break;
|
|
default:
|
|
case CM_RGB24:
|
|
st->BytesPerRow = 3 * st->Target.wWidth;
|
|
st->SWBytesPerRow = 3 * st->SWWidth;
|
|
st->Target.wHeight += st->wLineDistance * 2;
|
|
bScanBits = 24;
|
|
break;
|
|
case CM_GRAY16:
|
|
st->BytesPerRow = 2 * st->Target.wWidth;
|
|
st->SWBytesPerRow = 2 * st->SWWidth;
|
|
bScanBits = 16;
|
|
break;
|
|
case CM_GRAY8:
|
|
st->BytesPerRow = st->Target.wWidth;
|
|
st->SWBytesPerRow = st->SWWidth;
|
|
bScanBits = 8;
|
|
break;
|
|
case CM_TEXT:
|
|
/* 1-bit image data is generated from an 8-bit grayscale scan */
|
|
st->BytesPerRow = st->Target.wWidth;
|
|
st->SWBytesPerRow = st->SWWidth / 8;
|
|
bScanBits = 8;
|
|
break;
|
|
}
|
|
|
|
status = Asic_Open (&st->chip);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
st->bOpened = SANE_TRUE;
|
|
|
|
/* find left & top side */
|
|
if (st->Target.ssScanSource != SS_REFLECTIVE)
|
|
{
|
|
status = Asic_MotorMove (&st->chip, SANE_TRUE, TRAN_START_POS);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
}
|
|
|
|
if (st->Target.wXDpi == SENSOR_DPI)
|
|
{
|
|
st->Target.wXDpi = SENSOR_DPI / 2;
|
|
status = AdjustAD (st);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
if (FindTopLeft (st, &st->Target.wX, &st->Target.wY) != SANE_STATUS_GOOD)
|
|
{
|
|
st->Target.wX = 187;
|
|
st->Target.wY = 43;
|
|
}
|
|
|
|
st->Target.wXDpi = SENSOR_DPI;
|
|
status = AdjustAD (st);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
st->Target.wX = st->Target.wX * SENSOR_DPI /
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION + pTarget->wX;
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
st->Target.wX += 47;
|
|
st->Target.wY = st->Target.wY * SENSOR_DPI /
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION +
|
|
pTarget->wY * SENSOR_DPI / st->Target.wYDpi + 47;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = AdjustAD (st);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
if (FindTopLeft (st, &st->Target.wX, &st->Target.wY) != SANE_STATUS_GOOD)
|
|
{
|
|
st->Target.wX = 187;
|
|
st->Target.wY = 43;
|
|
}
|
|
|
|
st->Target.wX += pTarget->wX * (SENSOR_DPI / 2) / st->Target.wXDpi;
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
if (st->Target.wXDpi != 75)
|
|
st->Target.wX += 23;
|
|
st->Target.wY = st->Target.wY * SENSOR_DPI /
|
|
FIND_LEFT_TOP_CALIBRATE_RESOLUTION +
|
|
pTarget->wY * SENSOR_DPI / st->Target.wYDpi + 47;
|
|
}
|
|
else
|
|
{
|
|
if (st->Target.wXDpi == 75)
|
|
st->Target.wX -= 23;
|
|
}
|
|
}
|
|
|
|
DBG (DBG_FUNC, "before LineCalibration16Bits wX=%d,wY=%d\n",
|
|
st->Target.wX, st->Target.wY);
|
|
|
|
status = LineCalibration16Bits (st);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
DBG (DBG_FUNC, "after LineCalibration16Bits wX=%d,wY=%d\n",
|
|
st->Target.wX, st->Target.wY);
|
|
|
|
DBG (DBG_FUNC, "bScanBits=%d,wXDpi=%d,wYDpi=%d,wWidth=%d,wHeight=%d\n",
|
|
bScanBits, st->Target.wXDpi, st->Target.wYDpi,
|
|
st->Target.wWidth, st->Target.wHeight);
|
|
|
|
if (st->Target.ssScanSource == SS_REFLECTIVE)
|
|
{
|
|
finalY = 300;
|
|
}
|
|
else
|
|
{
|
|
st->Target.wY = pTarget->wY * SENSOR_DPI / st->Target.wYDpi +
|
|
(300 - 40) + 189;
|
|
finalY = 360;
|
|
}
|
|
|
|
if (st->Target.wY > finalY)
|
|
status = Asic_MotorMove (&st->chip, SANE_TRUE, st->Target.wY - finalY);
|
|
else
|
|
status = Asic_MotorMove (&st->chip, SANE_FALSE, finalY - st->Target.wY);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
st->Target.wY = finalY;
|
|
|
|
status = Asic_SetWindow (&st->chip, st->Target.ssScanSource, SCAN_TYPE_NORMAL,
|
|
bScanBits, st->Target.wXDpi, st->Target.wYDpi,
|
|
st->Target.wX, st->Target.wY,
|
|
st->Target.wWidth, st->Target.wHeight);
|
|
if (status != SANE_STATUS_GOOD)
|
|
return status;
|
|
|
|
status = PrepareScan(st);
|
|
|
|
DBG_LEAVE ();
|
|
return status;
|
|
}
|