/* 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, see <https://www.gnu.org/licenses/>.

   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. */

#include <pthread.h>		/* HOLD */
#include <stdlib.h>

/* local header files */
#include "mustek_usb2_asic.c"

#include "mustek_usb2_high.h"

/* ******************++ spuicall_g.h ****************************/

/*global variable HOLD: these should go to scanner structure */

/*base type*/
static SANE_Bool g_bOpened;
static SANE_Bool g_bPrepared;
static SANE_Bool g_isCanceled;
static SANE_Bool g_bSharpen;
static SANE_Bool g_bFirstReadImage;
static SANE_Bool g_isScanning;
static SANE_Bool g_isSelfGamma;

static SANE_Byte g_bScanBits;
static SANE_Byte *g_lpReadImageHead;

static unsigned short s_wOpticalYDpi[] = { 1200, 600, 300, 150, 75, 0 };
static unsigned short s_wOpticalXDpi[] = { 1200, 600, 300, 150, 75, 0 };
static unsigned short g_X;
static unsigned short g_Y;
static unsigned short g_Width;
static unsigned short g_Height;
static unsigned short g_XDpi;
static unsigned short g_YDpi;
static unsigned short g_SWWidth;
static unsigned short g_SWHeight;
static unsigned short g_wPixelDistance;		/*even & odd sensor problem */
static unsigned short g_wLineDistance;
static unsigned short g_wScanLinesPerBlock;
static unsigned short g_wReadedLines;
static unsigned short g_wReadImageLines;
static unsigned short g_wReadyShadingLine;
static unsigned short g_wStartShadingLinePos;
static unsigned short g_wLineartThreshold;

static unsigned int g_wtheReadyLines;
static unsigned int g_wMaxScanLines;
static unsigned int g_dwScannedTotalLines;
static unsigned int g_dwImageBufferSize;
static unsigned int g_BytesPerRow;
static unsigned int g_SWBytesPerRow;
static unsigned int g_dwCalibrationSize;
static unsigned int g_dwBufferSize;

static unsigned int g_dwTotalTotalXferLines;

static unsigned short *g_pGammaTable;
static unsigned char *g_pDeviceFile;

static pthread_t g_threadid_readimage;

/*user define type*/
static COLORMODE g_ScanMode;
static TARGETIMAGE g_tiTarget;
static SCANTYPE g_ScanType = ST_Reflective;
static SCANSOURCE g_ssScanSource;
static PIXELFLAVOR g_PixelFlavor;

static SUGGESTSETTING g_ssSuggest;
static Asic g_chip;

static int g_nSecLength, g_nDarkSecLength;
static int g_nSecNum, g_nDarkSecNum;
static unsigned short g_wCalWidth;
static unsigned short g_wDarkCalWidth;
static int g_nPowerNum;
static unsigned short g_wStartPosition;

static pthread_mutex_t g_scannedLinesMutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t g_readyLinesMutex = PTHREAD_MUTEX_INITIALIZER;

/*for modify the last point*/
static SANE_Byte * g_lpBefLineImageData = NULL;
static SANE_Bool g_bIsFirstReadBefData = TRUE;
static unsigned int g_dwAlreadyGetLines = 0;

/* forward declarations */
static SANE_Bool MustScanner_Init (void);
static SANE_Bool MustScanner_GetScannerState (void);
static SANE_Bool MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn);
static SANE_Bool MustScanner_BackHome (void);
static SANE_Bool MustScanner_Prepare (SANE_Byte bScanSource);
#ifdef SANE_UNUSED
static SANE_Bool MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
				      SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
				      unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
				      SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
				      unsigned short wStdMaxLevel);
static SANE_Bool MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection,
					    SANE_Byte * bOffset, SANE_Byte * bLastMin,
					    SANE_Byte * bLastOffset, unsigned short * wMinValue,
					    SANE_Byte * bOffsetUpperBound,
					    SANE_Byte * bOffsetLowerBound,
					    unsigned short wStdMinLevel, unsigned short wStdMaxLevel);
#endif
static unsigned short MustScanner_FiltLower (unsigned short * pSort, unsigned short TotalCount, unsigned short LowCount,
				   unsigned short HighCount);
static SANE_Bool MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
					 unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
						unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
					 unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
						unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
					  unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
						 unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
					 unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
						unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
					 unsigned short * wLinesCount);
static SANE_Bool MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
						unsigned short * wLinesCount);
static void *MustScanner_ReadDataFromScanner (void * dummy);
static void MustScanner_PrepareCalculateMaxMin (unsigned short wResolution);
static void MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue,
					 unsigned short * lpMinValue, unsigned short wResolution);

static SANE_Byte QBET4 (SANE_Byte A, SANE_Byte B);
static unsigned int GetScannedLines (void);
static unsigned int GetReadyLines (void);
static void AddScannedLines (unsigned short wAddLines);
static void AddReadyLines (void);
static void ModifyLinePoint (SANE_Byte * lpImageData,
			     SANE_Byte * lpImageDataBefore,
			     unsigned int dwBytesPerLine,
			     unsigned int dwLinesCount,
			     unsigned short wPixDistance, unsigned short wModPtCount);

#include "mustek_usb2_reflective.c"
#include "mustek_usb2_transparent.c"

/**********************************************************************
Author: Jack            Date: 2005/05/13
	Routine Description:
Parameters:
	none
Return value:
	if initialize the scanner success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_Init ()
{
  DBG (DBG_FUNC, "MustScanner_Init: Call in\n");

  g_chip.firmwarestate = FS_NULL;
  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
    {
      DBG (DBG_FUNC, "MustScanner_Init: Asic_Open return error\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != Asic_Initialize (&g_chip))
    {
      DBG (DBG_FUNC, "MustScanner_Init: Asic_Initialize return error\n");
      return FALSE;
    }

  g_dwImageBufferSize = 24L * 1024L * 1024L;
  g_dwBufferSize = 64L * 1024L;
  g_dwCalibrationSize = 64L * 1024L;
  g_lpReadImageHead = NULL;

  g_isCanceled = FALSE;
  g_bFirstReadImage = TRUE;
  g_bOpened = FALSE;
  g_bPrepared = FALSE;
  g_bSharpen = FALSE;

  g_isScanning = FALSE;
  g_isSelfGamma = FALSE;
  g_pGammaTable = NULL;

  if (NULL != g_pDeviceFile)
    {
      free (g_pDeviceFile);
      g_pDeviceFile = NULL;
    }

  g_ssScanSource = SS_Reflective;
  g_PixelFlavor = PF_BlackIs0;

  Asic_Close (&g_chip);

  DBG (DBG_FUNC, "MustScanner_Init: leave MustScanner_Init\n");

  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	check the scanner connect status
Parameters:
	none
Return value:
	if scanner's status is OK
	return TRUE
	else
	return FASLE
***********************************************************************/
static SANE_Bool
MustScanner_GetScannerState ()
{

  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
    {
      DBG (DBG_FUNC, "MustScanner_GetScannerState: Asic_Open return error\n");
      return FALSE;
    }
  else
    {
      Asic_Close (&g_chip);
      return TRUE;
    }
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Turn the lamp on or off
Parameters:
	isLampOn: turn the lamp on or off
	isTALampOn: turn the TA lamp on or off
Return value:
	if operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_PowerControl (SANE_Bool isLampOn, SANE_Bool isTALampOn)
{
  SANE_Bool hasTA;
  DBG (DBG_FUNC, "MustScanner_PowerControl: Call in\n");
  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
    {
      DBG (DBG_FUNC, "MustScanner_PowerControl: Asic_Open return error\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, isLampOn))
    {
      DBG (DBG_FUNC,
	   "MustScanner_PowerControl: Asic_TurnLamp return error\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != Asic_IsTAConnected (&g_chip, &hasTA))
    {
      DBG (DBG_FUNC,
	   "MustScanner_PowerControl: Asic_IsTAConnected return error\n");
      return FALSE;
    }

  if (hasTA)
    {
      if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, isTALampOn))
	{
	  DBG (DBG_FUNC,
	       "MustScanner_PowerControl: Asic_TurnTA return error\n");
	  return FALSE;
	}
    }

  Asic_Close (&g_chip);

  DBG (DBG_FUNC,
       "MustScanner_PowerControl: leave MustScanner_PowerControl\n");
  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	Turn the carriage home
Parameters:
	none
Return value:
	if the operation success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_BackHome ()
{
  DBG (DBG_FUNC, "MustScanner_BackHome: call in \n");

  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))
    {
      DBG (DBG_FUNC, "MustScanner_BackHome: Asic_Open return error\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != Asic_CarriageHome (&g_chip, FALSE))
    {
      DBG (DBG_FUNC,
	   "MustScanner_BackHome: Asic_CarriageHome return error\n");
      return FALSE;
    }

  if (SANE_STATUS_GOOD != Asic_WaitUnitReady (&g_chip))
    {
      DBG (DBG_FUNC,
	   "MustScanner_BackHome: Asic_WaitUnitReady return error\n");
      return FALSE;
    }

  Asic_Close (&g_chip);

  DBG (DBG_FUNC, "MustScanner_BackHome: leave  MustScanner_BackHome\n");
  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/13
Routine Description:
	prepare the scan image
Parameters:
	bScanSource: the scan source
Return value:
	if operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_Prepare (SANE_Byte bScanSource)
{
  DBG (DBG_FUNC, "MustScanner_Prepare: call in\n");

  if (SANE_STATUS_GOOD != Asic_Open (&g_chip, g_pDeviceFile))


    {
      DBG (DBG_FUNC, "MustScanner_Prepare: Asic_Open return error\n");
      return FALSE;
    }
  if (SANE_STATUS_GOOD != Asic_WaitUnitReady (&g_chip))
    {
      DBG (DBG_FUNC,
	   "MustScanner_Prepare: Asic_WaitUnitReady return error\n");
      return FALSE;
    }

  if (SS_Reflective == bScanSource)
    {
      DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Reflective\n");
      if (SANE_STATUS_GOOD != Asic_TurnLamp (&g_chip, TRUE))
	{
	  DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnLamp return error\n");
	  return FALSE;
	}

      if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_REFLECTIVE))
	{
	  DBG (DBG_FUNC,
	       "MustScanner_Prepare: Asic_SetSource return error\n");
	  return FALSE;
	}
    }
  else if (SS_Positive == bScanSource)
    {
      DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Positive\n");
      if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE))
	{
	  DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n");
	  return FALSE;
	}
      if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_POSITIVE))
	{
	  DBG (DBG_FUNC,
	       "MustScanner_Prepare: Asic_SetSource return error\n");
	  return FALSE;
	}
    }
  else if (SS_Negative == bScanSource)
    {
      DBG (DBG_FUNC, "MustScanner_Prepare:ScanSource is SS_Negative\n");

      if (SANE_STATUS_GOOD != Asic_TurnTA (&g_chip, TRUE))
	{
	  DBG (DBG_FUNC, "MustScanner_Prepare: Asic_TurnTA return error\n");
	  return FALSE;
	}

      if (SANE_STATUS_GOOD != Asic_SetSource (&g_chip, LS_NEGATIVE))
	{
	  DBG (DBG_FUNC,
	       "MustScanner_Prepare: Asic_SetSource return error\n");
	  return FALSE;
	}
      DBG (DBG_FUNC, "MustScanner_Prepare: Asic_SetSource return good\n");
    }

  Asic_Close (&g_chip);
  g_bPrepared = TRUE;

  DBG (DBG_FUNC, "MustScanner_Prepare: leave MustScanner_Prepare\n");
  return TRUE;
}

#ifdef SANE_UNUSED
/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Adjuest the offset
Parameters:
	nTimes: Adjuest offset the times
	bDirection: whether direction
	bOffset: the data of offset
	bLastMin: the last min data
	bLastOffset: the last offset data
	wMinValue: the min value of offset
	bOffsetUpperBound: the upper bound of offset
	bOffsetLowerBound: the lower bound of offset
	wStdMinLevel: the min level of offset
	wStdMaxLevel: the max level of offset
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_AdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
			  SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
			  unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
			  SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
			  unsigned short wStdMaxLevel)
{
  if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel))
    {
      return TRUE;
    }

  if (nTimes == 0)
    {
      *bLastMin = LOSANE_Byte (*wMinValue);
      *bLastOffset = *bOffset;

      if (*wMinValue == 255)
	{
	  *bOffset = 0;
	}
      else
	{
	  *bOffset = 255;
	}
    }

  if (nTimes == 1)
    {
      if (*wMinValue > *bLastMin)
	{
	  if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel)
	    {
	      *bDirection = !(*bDirection);
	      return TRUE;
	    }

	  if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel)
	    return TRUE;
	}

      if (*wMinValue < *bLastMin)
	{
	  if (*wMinValue < wStdMinLevel && *bLastMin < wStdMinLevel)
	    *bDirection = !(*bDirection);

	  if (*wMinValue > wStdMaxLevel && *bLastMin > wStdMaxLevel)
	    return TRUE;
	}
    }

  if (nTimes > 1)
    {
      if (*wMinValue > *bLastMin)
	{
	  SANE_Byte bTemp;

	  bTemp = *bOffset;

	  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;

	  if (nTimes > 2)
	    {
	      if (*wMinValue > wStdMaxLevel)
		if (bDirection)
		  *bOffsetLowerBound = bTemp;
		else
		  *bOffsetUpperBound = bTemp;


	      else if (bDirection)
		*bOffsetUpperBound = bTemp;
	      else
		*bOffsetLowerBound = bTemp;
	    }

	  *bLastOffset = bTemp;
	  *bLastMin = (SANE_Byte) * wMinValue;
	}
      else
	{
	  SANE_Byte bTemp;

	  bTemp = *bOffset;

	  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;

	  if (nTimes > 2)
	    {
	      if (*wMinValue == *bLastMin)
		{
		  if (*wMinValue > wStdMaxLevel)
		    {
		      if (!bDirection)
			*bOffsetUpperBound = bTemp;
		      else
			*bOffsetLowerBound = bTemp;
		    }
		  else
		    {
		      if (!bDirection)
			*bOffsetLowerBound = bTemp;
		      else
			*bOffsetUpperBound = bTemp;
		    }
		}
	      else
		{
		  if (*wMinValue > wStdMaxLevel)
		    {
		      if (bDirection)
			*bOffsetUpperBound = bTemp;
		      else
			*bOffsetLowerBound = bTemp;
		    }
		  else
		    {
		      if (bDirection)
			*bOffsetLowerBound = bTemp;
		      else
			*bOffsetUpperBound = bTemp;
		    }
		}
	    }

	  *bLastOffset = bTemp;
	  *bLastMin = (SANE_Byte) * wMinValue;

	}
    }				/* end of if(nTimes > 1) */

  return TRUE;
}
#endif

#ifdef SANE_UNUSED
/**********************************************************************

Author: Jack             Date: 2005/05/15
Routine Description:
	Adjuest the offset second times
Parameters:
	nTimes: Adjuest offset the times
	bDirection: whether direction
	bOffset: the data of offset
	bLastMin: the last min data
	bLastOffset: the last offset data
	wMinValue: the min value of offset
	bOffsetUpperBound: the upper bound of offset
	bOffsetLowerBound: the lower bound of offset
	wStdMinLevel: the min level of offset
	wStdMaxLevel: the max level of offset
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_SecondAdjustOffset (int nTimes, SANE_Bool * bDirection, SANE_Byte * bOffset,
				SANE_Byte * bLastMin, SANE_Byte * bLastOffset,
				unsigned short * wMinValue, SANE_Byte * bOffsetUpperBound,
				SANE_Byte * bOffsetLowerBound, unsigned short wStdMinLevel,
				unsigned short wStdMaxLevel)
{
  if ((*wMinValue <= wStdMaxLevel) && (*wMinValue >= wStdMinLevel))
    {
      return TRUE;
    }
  if (nTimes == 0)
    {
      *bLastMin = LOSANE_Byte (*wMinValue);
      *bLastOffset = *bOffset;

      if (*bDirection == 0)
	{
	  *bOffsetUpperBound = *bLastOffset;
	  *bOffsetLowerBound = 0;
	  *bOffset = 0;
	}
      else
	{
	  *bOffsetUpperBound = 255;
	  *bOffsetLowerBound = *bLastOffset;
	  *bOffset = 255;
	}
    }

  if (nTimes >= 1)
    {
      if (*wMinValue > wStdMaxLevel)
	{
	  if (*wMinValue > *bLastMin)
	    {
	      if (*bDirection == 0)
		{
		  *bOffsetUpperBound = *bOffset;
		}
	      else
		{
		  *bOffsetLowerBound = *bOffset;
		}

	      *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
	    }
	  else
	    {
	      if (*bDirection == 1)
		{
		  *bOffsetUpperBound = *bOffset;
		  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
		}
	      else
		{
		  *bOffsetLowerBound = *bOffset;
		  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
		}
	    }
	}			/*end of if(*wMinValue > MAX_OFFSET) */


      if (*wMinValue < wStdMinLevel)
	{
	  if (*wMinValue > *bLastMin)
	    {
	      if (*bDirection == 0)
		{
		  *bOffsetLowerBound = *bOffset;
		}
	      else
		{
		  *bOffsetUpperBound = *bOffset;
		}

	      *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
	    }
	  else
	    {
	      if (*bDirection == 1)
		{
		  *bOffsetUpperBound = *bOffset;
		  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
		}
	      else
		{
		  *bOffsetLowerBound = *bOffset;
		  *bOffset = (*bOffsetUpperBound + *bOffsetLowerBound) / 2;
		}
	    }
	}			/*end of if(*wMinValue > MIN_OFFSET) */
      *bLastMin = (SANE_Byte) * wMinValue;
    }				/*end of if(nTimes >= 1)     */

  /* HOLD: missing return value! Correct? */
  return FALSE;
}
#endif

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Filter the data
Parameters:
	pSort: the sort data
	TotalCount: the total count
	LowCount: the low count
	HighCount: the upper count
Return value:
	the data of Filter
***********************************************************************/
static unsigned short
MustScanner_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 = 0;
  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);
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when single CCD and color is 48bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetRgb48BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
			     unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned short wRLinePos = 0;
  unsigned short wGLinePos = 0;
  unsigned short wBLinePos = 0;
  unsigned short wRTempData;
  unsigned short wGTempData;
  unsigned short wBTempData;
  unsigned short i;

  DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: call in \n");

  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;
  TotalXferLines = 0;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  if (!isOrderInvert)
    {
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
	      wGLinePos =
		(g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
	      wBLinePos =
		(g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;

	      for (i = 0; i < g_SWWidth; i++)
		{
		  wRTempData =
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
		      0);
		  wRTempData +=
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
		      1) << 8;
		  wGTempData =
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
		      2);
		  wGTempData +=
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
		      3) << 8;
		  wBTempData =
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
		      4);
		  wBTempData +=
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
		      5) << 8;
		  *(lpLine + i * 6 + 0) = LOBYTE (g_pGammaTable[wRTempData]);
		  *(lpLine + i * 6 + 1) = HIBYTE (g_pGammaTable[wRTempData]);
		  *(lpLine + i * 6 + 2) =
		    LOBYTE (g_pGammaTable[wGTempData + 65536]);
		  *(lpLine + i * 6 + 3) =
		    HIBYTE (g_pGammaTable[wGTempData + 65536]);
		  *(lpLine + i * 6 + 4) =
		    LOBYTE (g_pGammaTable[wBTempData + 131072]);
		  *(lpLine + i * 6 + 5) =
		    HIBYTE (g_pGammaTable[wBTempData + 131072]);
		}
	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();
	    }

	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);

	      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");

	      break;
	    }
	}
    }
  else
    {
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);

	      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");


	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
	      wGLinePos =
		(g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
	      wBLinePos =
		(g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;

	      for (i = 0; i < g_SWWidth; i++)
		{
		  wRTempData =
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
		      0);
		  wRTempData +=
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 6 +
		      1) << 8;
		  wGTempData =
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
		      2);
		  wGTempData +=
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 6 +
		      3) << 8;
		  wBTempData =
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
		      4);
		  wBTempData +=
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 6 +
		      5) << 8;
		  *(lpLine + i * 6 + 4) = LOBYTE (g_pGammaTable[wRTempData]);
		  *(lpLine + i * 6 + 5) = HIBYTE (g_pGammaTable[wRTempData]);
		  *(lpLine + i * 6 + 2) =
		    LOBYTE (g_pGammaTable[wGTempData + 65536]);
		  *(lpLine + i * 6 + 3) =
		    HIBYTE (g_pGammaTable[wGTempData + 65536]);
		  *(lpLine + i * 6 + 0) =
		    LOBYTE (g_pGammaTable[wBTempData + 131072]);
		  *(lpLine + i * 6 + 1) =
		    HIBYTE (g_pGammaTable[wBTempData + 131072]);
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();

	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);

	      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine: thread exit\n");
	      break;
	    }
	}			/*end for */
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetRgb48BitLine: leave MustScanner_GetRgb48BitLine\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when double CCD and color is 48bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetRgb48BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
				    unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;

  unsigned short wRLinePosOdd = 0;
  unsigned short wGLinePosOdd = 0;
  unsigned short wBLinePosOdd = 0;
  unsigned short wRLinePosEven = 0;
  unsigned short wGLinePosEven = 0;
  unsigned short wBLinePosEven = 0;
  unsigned int wRTempData;
  unsigned int wGTempData;
  unsigned int wBTempData;
  unsigned int wNextTempData;
  unsigned short i;

  DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: call in \n");

  TotalXferLines = 0;
  wWantedTotalLines = *wLinesCount;

  g_isCanceled = FALSE;
  g_isScanning = TRUE;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetRgb48BitLine1200DPI: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  if (!isOrderInvert)
    {
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      if (ST_Reflective == g_ScanType)
		{
		  wRLinePosOdd =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}
	      else
		{
		  wRLinePosEven =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}

	      for (i = 0; i < g_SWWidth;)
		{
		  if (i + 1 != g_SWWidth)
		    {
		      wRTempData =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 6 + 0);
		      wRTempData +=
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 6 + 1) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 0);
		      wNextTempData +=
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 1) << 8;
		      wRTempData = (wRTempData + wNextTempData) >> 1;

		      wGTempData =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 6 + 2);
		      wGTempData +=
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 6 + 3) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 2);
		      wNextTempData +=
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 3) << 8;
		      wGTempData = (wGTempData + wNextTempData) >> 1;

		      wBTempData =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 6 + 4);
		      wBTempData +=
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 6 + 5) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 4);
		      wNextTempData +=
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 5) << 8;
		      wBTempData = (wBTempData + wNextTempData) >> 1;

		      *(lpLine + i * 6 + 0) =
			LOBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 1) =
			HIBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 2) =
			LOBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 3) =
			HIBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 4) =
			LOBYTE (g_pGammaTable[wBTempData + 131072]);
		      *(lpLine + i * 6 + 5) =
			HIBYTE (g_pGammaTable[wBTempData + 131072]);
		      i++;
		      if (i >= g_SWWidth)
			{
			  break;
			}

		      wRTempData =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 6 + 0);
		      wRTempData +=
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 6 + 1) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 0);
		      wNextTempData +=
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 1) << 8;
		      wRTempData = (wRTempData + wNextTempData) >> 1;

		      wGTempData =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 6 + 2);
		      wGTempData +=
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 6 + 3) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 2);
		      wNextTempData +=
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 3) << 8;
		      wGTempData = (wGTempData + wNextTempData) >> 1;

		      wBTempData =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 6 + 4);
		      wBTempData +=
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 6 + 5) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 4);
		      wNextTempData +=
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 5) << 8;
		      wBTempData = (wBTempData + wNextTempData) >> 1;

		      *(lpLine + i * 6 + 0) =
			LOBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 1) =
			HIBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 2) =
			LOBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 3) =
			HIBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 4) =
			LOBYTE (g_pGammaTable[wBTempData + 131072]);
		      *(lpLine + i * 6 + 5) =
			HIBYTE (g_pGammaTable[wBTempData + 131072]);

		      i++;
		    }
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();
	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");
	      break;

	    }
	}


    }
  else
    {
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      if (ST_Reflective == g_ScanType)
		{
		  wRLinePosOdd =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}
	      else
		{
		  wRLinePosEven =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}

	      for (i = 0; i < g_SWWidth;)
		{
		  if ((i + 1) != g_SWWidth)
		    {
		      wRTempData =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 6 + 0);
		      wRTempData +=
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 6 + 1) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 0);
		      wNextTempData +=
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 1) << 8;
		      wRTempData = (wRTempData + wNextTempData) >> 1;

		      wGTempData =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 6 + 2);
		      wGTempData +=
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 6 + 3) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 2);
		      wNextTempData +=
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 3) << 8;
		      wGTempData = (wGTempData + wNextTempData) >> 1;

		      wBTempData =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 6 + 4);
		      wBTempData +=
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 6 + 5) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 4);
		      wNextTempData +=
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  (i + 1) * 6 + 5) << 8;
		      wBTempData = (wBTempData + wNextTempData) >> 1;

		      *(lpLine + i * 6 + 4) =
			LOBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 5) =
			HIBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 2) =
			LOBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 3) =
			HIBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 0) =
			LOBYTE (g_pGammaTable[wBTempData + 131072]);
		      *(lpLine + i * 6 + 1) =
			HIBYTE (g_pGammaTable[wBTempData + 131072]);
		      i++;
		      if (i >= g_SWWidth)
			{
			  break;
			}

		      wRTempData =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 6 + 0);
		      wRTempData +=
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 6 + 1) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 0);
		      wNextTempData +=
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 1) << 8;
		      wRTempData = (wRTempData + wNextTempData) >> 1;

		      wGTempData =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 6 + 2);
		      wGTempData +=
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 6 + 3) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 2);
		      wNextTempData +=
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 3) << 8;
		      wGTempData = (wGTempData + wNextTempData) >> 1;


		      wBTempData =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 6 + 4);
		      wBTempData +=
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 6 + 5) << 8;
		      wNextTempData =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 4);
		      wNextTempData +=
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 6 + 5) << 8;
		      wBTempData = (wBTempData + wNextTempData) >> 1;

		      *(lpLine + i * 6 + 4) =
			LOBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 5) =
			HIBYTE (g_pGammaTable[wRTempData]);
		      *(lpLine + i * 6 + 2) =
			LOBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 3) =
			HIBYTE (g_pGammaTable[wGTempData + 65536]);
		      *(lpLine + i * 6 + 0) =
			LOBYTE (g_pGammaTable[wBTempData + 131072]);
		      *(lpLine + i * 6 + 1) =
			HIBYTE (g_pGammaTable[wBTempData + 131072]);
		      i++;
		    }
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();
	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);

	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb48BitLine1200DPI: thread exit\n");

	      break;
	    }
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetRgb48BitLine1200DPI: leave MustScanner_GetRgb48BitLine1200DPI\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when single CCD and color is 24bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetRgb24BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
			     unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned short wRLinePos = 0;
  unsigned short wGLinePos = 0;
  unsigned short wBLinePos = 0;
  SANE_Byte byRed;
  SANE_Byte byGreen;
  SANE_Byte byBlue;
  SANE_Byte bNextPixel = 0;
  unsigned short i;

  unsigned short tempR, tempG, tempB;

  DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: call in\n");

  g_isCanceled = FALSE;
  g_isScanning = TRUE;

  wWantedTotalLines = *wLinesCount;
  DBG (DBG_FUNC,
       "MustScanner_GetRgb24BitLine: get wWantedTotalLines= %d\n",
       wWantedTotalLines);

  TotalXferLines = 0;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread create\n");

      g_bFirstReadImage = FALSE;
    }

  if (!isOrderInvert)
    {
      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: !isOrderInvert\n");

      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
	      wGLinePos =
		(g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
	      wBLinePos =
		(g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;

	      for (i = 0; i < g_SWWidth; i++)
		{
		  byRed =
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 +
		      0);
		  bNextPixel =
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow +
		      (i + 1) * 3 + 0);
		  byRed = (byRed + bNextPixel) >> 1;

		  byGreen =
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 +
		      1);
		  bNextPixel =
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow +
		      (i + 1) * 3 + 1);
		  byGreen = (byGreen + bNextPixel) >> 1;

		  byBlue =
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 +
		      2);
		  bNextPixel =
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow +
		      (i + 1) * 3 + 2);
		  byBlue = (byBlue + bNextPixel) >> 1;

#ifdef ENABLE_GAMMA
		  tempR = (unsigned short) ((byRed << 4) | QBET4 (byBlue, byGreen));
		  tempG = (unsigned short) ((byGreen << 4) | QBET4 (byRed, byBlue));
		  tempB = (unsigned short) ((byBlue << 4) | QBET4 (byGreen, byRed));

		  *(lpLine + i * 3 + 0) =
		    (unsigned char) (*(g_pGammaTable + tempR));
		  *(lpLine + i * 3 + 1) =
		    (unsigned char) (*(g_pGammaTable + 4096 + tempG));
		  *(lpLine + i * 3 + 2) =
		    (unsigned char) (*(g_pGammaTable + 8192 + tempB));
#else
		  *(lpLine + i * 3 + 0) = (unsigned char) byRed;
		  *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		  *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
#endif
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();

	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n",
		   g_dwTotalTotalXferLines, g_SWHeight);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n",
		   g_SWBytesPerRow);
	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");

	      break;
	    }
	}
    }
  else
    {
      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: isOrderInvert is TRUE\n");
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      wRLinePos = g_wtheReadyLines % g_wMaxScanLines;
	      wGLinePos =
		(g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
	      wBLinePos =
		(g_wtheReadyLines - g_wLineDistance * 2) % g_wMaxScanLines;

	      for (i = 0; i < g_SWWidth; i++)
		{
		  DBG (DBG_FUNC,
		       "MustScanner_GetRgb24BitLine: before byRed\n");
		  byRed =
		    *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + i * 3 +
		      0);
		  bNextPixel = *(g_lpReadImageHead + wRLinePos * g_BytesPerRow + (i + 1) * 3 + 0);	/*R-channel */
		  byRed = (byRed + bNextPixel) >> 1;

		  DBG (DBG_FUNC,
		       "MustScanner_GetRgb24BitLine: before byGreen\n");

		  byGreen =
		    *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + i * 3 +
		      1);
		  bNextPixel = *(g_lpReadImageHead + wGLinePos * g_BytesPerRow + (i + 1) * 3 + 1);	/*G-channel */
		  byGreen = (byGreen + bNextPixel) >> 1;

		  DBG (DBG_FUNC,
		       "MustScanner_GetRgb24BitLine: before byBlue\n");

		  byBlue =
		    *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + i * 3 +
		      2);
		  bNextPixel = *(g_lpReadImageHead + wBLinePos * g_BytesPerRow + (i + 1) * 3 + 2);	/*B-channel */
		  byBlue = (byBlue + bNextPixel) >> 1;


		  DBG (DBG_FUNC,
		       "MustScanner_GetRgb24BitLine: before set lpLine\n");
		  DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: i=%d\n", i);
#ifdef ENABLE_GAMMA
		  *(lpLine + i * 3 + 2) =
		    (unsigned
		     char) (*(g_pGammaTable +
			      (unsigned short) ((byRed << 4) |
				      QBET4 (byBlue, byGreen))));
		  *(lpLine + i * 3 + 1) =
		    (unsigned
		     char) (*(g_pGammaTable + 4096 +
			      (unsigned short) ((byGreen << 4) |
				      QBET4 (byRed, byBlue))));
		  *(lpLine + i * 3 + 0) =
		    (unsigned
		     char) (*(g_pGammaTable + 8192 +
			      (unsigned short) ((byBlue << 4) |
				      QBET4 (byGreen, byRed))));
#else
		  *(lpLine + i * 3 + 2) = (unsigned char) byRed;
		  *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		  *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
#endif
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();

	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine: g_dwTotalTotalXferLines=%d,g_SWHeight=%d\n",
		   g_dwTotalTotalXferLines, g_SWHeight);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine: g_SWBytesPerRow=%d\n",
		   g_SWBytesPerRow);
	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine: thread exit\n");

	      break;
	    }
	}			/*end for */
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetRgb24BitLine: leave MustScanner_GetRgb24BitLine\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when double CCD and color is 24bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetRgb24BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
				    unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned short wRLinePosOdd = 0;
  unsigned short wGLinePosOdd = 0;
  unsigned short wBLinePosOdd = 0;
  unsigned short wRLinePosEven = 0;
  unsigned short wGLinePosEven = 0;
  unsigned short wBLinePosEven = 0;
  SANE_Byte byRed;
  SANE_Byte byGreen;
  SANE_Byte byBlue;
  SANE_Byte bNextPixel = 0;
  unsigned short i;

  DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: call in\n");

  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  TotalXferLines = 0;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetRgb24BitLine1200DPI: thread create\n");

      g_bFirstReadImage = FALSE;
    }

  if (!isOrderInvert)
    {
      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
		   g_dwTotalTotalXferLines);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
		   g_Height);

	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      if (ST_Reflective == g_ScanType)
		{
		  wRLinePosOdd =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}
	      else
		{
		  wRLinePosEven =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}



	      for (i = 0; i < g_SWWidth;)
		{
		  if ((i + 1) != g_SWWidth)
		    {
		      byRed =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 3 + 0);
		      bNextPixel = *(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow + (i + 1) * 3 + 0);	/*R-channel */
		      byRed = (byRed + bNextPixel) >> 1;

		      byGreen =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 3 + 1);
		      bNextPixel = *(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow + (i + 1) * 3 + 1);	/*G-channel */
		      byGreen = (byGreen + bNextPixel) >> 1;

		      byBlue =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 3 + 2);
		      bNextPixel = *(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow + (i + 1) * 3 + 2);	/*B-channel */
		      byBlue = (byBlue + bNextPixel) >> 1;
#ifdef ENABLE_GAMMA
		      *(lpLine + i * 3 + 0) =
			(unsigned
			 char) (*(g_pGammaTable +
				  (unsigned short) ((byRed << 4) |
					  QBET4 (byBlue, byGreen))));
		      *(lpLine + i * 3 + 1) =
			(unsigned
			 char) (*(g_pGammaTable + 4096 +
				  (unsigned short) ((byGreen << 4) |
					  QBET4 (byRed, byBlue))));
		      *(lpLine + i * 3 + 2) =
			(unsigned
			 char) (*(g_pGammaTable + 8192 +
				  (unsigned short) ((byBlue << 4) |
					  QBET4 (byGreen, byRed))));
#else
		      *(lpLine + i * 3 + 0) = (unsigned char) byRed;
		      *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		      *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
#endif

		      i++;
		      if (i >= g_SWWidth)
			{
			  break;
			}

		      byRed =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 3 + 0);
		      bNextPixel =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 0);
		      byRed = (byRed + bNextPixel) >> 1;

		      byGreen =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 3 + 1);
		      bNextPixel =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 1);
		      byGreen = (byGreen + bNextPixel) >> 1;

		      byBlue =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 3 + 2);
		      bNextPixel =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 2);
		      byBlue = (byBlue + bNextPixel) >> 1;
#ifdef ENABLE_GAMMA
		      *(lpLine + i * 3 + 0) =
			(unsigned
			 char) (*(g_pGammaTable +
				  (unsigned short) ((byRed << 4) |
					  QBET4 (byBlue, byGreen))));
		      *(lpLine + i * 3 + 1) =
			(unsigned
			 char) (*(g_pGammaTable + 4096 +
				  (unsigned short) ((byGreen << 4) |
					  QBET4 (byRed, byBlue))));
		      *(lpLine + i * 3 + 2) =
			(unsigned
			 char) (*(g_pGammaTable + 8192 +
				  (unsigned short) ((byBlue << 4) |
					  QBET4 (byGreen, byRed))));
#else
		      *(lpLine + i * 3 + 0) = (unsigned char) byRed;
		      *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		      *(lpLine + i * 3 + 2) = (unsigned char) byBlue;
#endif
		      i++;
		    }
		}


	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();

	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
		   g_dwTotalTotalXferLines);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
		   g_Height);

	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");

	      break;
	    }

	}
    }
  else
    {

      for (; TotalXferLines < wWantedTotalLines;)
	{
	  if (g_dwTotalTotalXferLines >= g_SWHeight)
	    {
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
		   g_dwTotalTotalXferLines);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
		   g_Height);

	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");

	      *wLinesCount = TotalXferLines;
	      g_isScanning = FALSE;
	      return TRUE;
	    }

	  if (GetScannedLines () > g_wtheReadyLines)
	    {
	      if (ST_Reflective == g_ScanType)
		{
		  wRLinePosOdd =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}
	      else
		{
		  wRLinePosEven =
		    (g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
		  wGLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wBLinePosEven =
		    (g_wtheReadyLines - g_wLineDistance * 2 -
		     g_wPixelDistance) % g_wMaxScanLines;
		  wRLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
		  wGLinePosOdd =
		    (g_wtheReadyLines - g_wLineDistance) % g_wMaxScanLines;
		  wBLinePosOdd =
		    (g_wtheReadyLines -
		     g_wLineDistance * 2) % g_wMaxScanLines;
		}

	      for (i = 0; i < g_SWWidth;)
		{
		  if ((i + 1) != g_SWWidth)
		    {
		      byRed =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  i * 3 + 0);
		      bNextPixel =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  (i + 1) * 3 + 0);
		      byRed = (byRed + bNextPixel) >> 1;

		      byGreen =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  i * 3 + 1);
		      bNextPixel =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  (i + 1) * 3 + 1);
		      byGreen = (byGreen + bNextPixel) >> 1;

		      byBlue =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  i * 3 + 2);
		      bNextPixel =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  (i + 1) * 3 + 2);
		      byBlue = (byBlue + bNextPixel) >> 1;

#ifdef ENABLE_GAMMA
		      *(lpLine + i * 3 + 2) =
			(unsigned
			 char) (*(g_pGammaTable +
				  (unsigned short) ((byRed << 4) |
					  QBET4 (byBlue, byGreen))));
		      *(lpLine + i * 3 + 1) =
			(unsigned
			 char) (*(g_pGammaTable + 4096 +
				  (unsigned short) ((byGreen << 4) |
					  QBET4 (byRed, byBlue))));
		      *(lpLine + i * 3 + 0) =
			(unsigned
			 char) (*(g_pGammaTable + 8192 +
				  (unsigned short) ((byBlue << 4) |
					  QBET4 (byGreen, byRed))));
#else
		      *(lpLine + i * 3 + 2) = (unsigned char) byRed;
		      *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		      *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
#endif
		      i++;
		      if (i >= g_SWWidth)
			{
			  break;
			}

		      byRed =
			*(g_lpReadImageHead + wRLinePosEven * g_BytesPerRow +
			  i * 3 + 0);
		      bNextPixel =
			*(g_lpReadImageHead + wRLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 0);
		      byRed = (byRed + bNextPixel) >> 1;

		      byGreen =
			*(g_lpReadImageHead + wGLinePosEven * g_BytesPerRow +
			  i * 3 + 1);
		      bNextPixel =
			*(g_lpReadImageHead + wGLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 1);
		      byGreen = (byGreen + bNextPixel) >> 1;

		      byBlue =
			*(g_lpReadImageHead + wBLinePosEven * g_BytesPerRow +
			  i * 3 + 2);
		      bNextPixel =
			*(g_lpReadImageHead + wBLinePosOdd * g_BytesPerRow +
			  (i + 1) * 3 + 2);
		      byBlue = (byBlue + bNextPixel) >> 1;
#ifdef ENABLE_GAMMA
		      *(lpLine + i * 3 + 2) =
			(unsigned
			 char) (*(g_pGammaTable +
				  (unsigned short) ((byRed << 4) |
					  QBET4 (byBlue, byGreen))));
		      *(lpLine + i * 3 + 1) =
			(unsigned
			 char) (*(g_pGammaTable + 4096 +
				  (unsigned short) ((byGreen << 4) |
					  QBET4 (byRed, byBlue))));
		      *(lpLine + i * 3 + 0) =
			(unsigned
			 char) (*(g_pGammaTable + 8192 +
				  (unsigned short) ((byBlue << 4) |
					  QBET4 (byGreen, byRed))));
#else
		      *(lpLine + i * 3 + 2) = (unsigned char) byRed;
		      *(lpLine + i * 3 + 1) = (unsigned char) byGreen;
		      *(lpLine + i * 3 + 0) = (unsigned char) byBlue;
#endif
		      i++;
		    }
		}

	      TotalXferLines++;
	      g_dwTotalTotalXferLines++;
	      lpLine += g_SWBytesPerRow;
	      AddReadyLines ();

	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_dwTotalTotalXferLines=%d\n",
		   g_dwTotalTotalXferLines);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: g_Height=%d\n",
		   g_Height);

	    }
	  if (g_isCanceled)
	    {
	      pthread_cancel (g_threadid_readimage);
	      pthread_join (g_threadid_readimage, NULL);
	      DBG (DBG_FUNC,
		   "MustScanner_GetRgb24BitLine1200DPI: thread exit\n");


	      break;
	    }
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetRgb24BitLine1200DPI: leave MustScanner_GetRgb24BitLine1200DPI\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when single CCD and color is 16bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono16BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
			      unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned int wTempData;

  unsigned short wLinePos = 0;
  unsigned short i;

  (void) isOrderInvert;

  DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: call in\n");

  TotalXferLines = 0;
  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  for (; TotalXferLines < wWantedTotalLines;)
    {

      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  wLinePos = g_wtheReadyLines % g_wMaxScanLines;

	  for (i = 0; i < g_SWWidth; i++)
	    {
	      wTempData =
		*(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 + 0);
	      wTempData +=
		*(g_lpReadImageHead + wLinePos * g_BytesPerRow + i * 2 +
		  1) << 8;
	      *(lpLine + i * 2 + 0) = LOBYTE (g_pGammaTable[wTempData]);
	      *(lpLine + i * 2 + 1) = HIBYTE (g_pGammaTable[wTempData]);
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;

	  lpLine += g_SWBytesPerRow;
	  AddReadyLines ();
	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono16BitLine: thread exit\n");

	  break;
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetMono16BitLine: leave MustScanner_GetMono16BitLine\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when double CCD and color is 16bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono16BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
				     unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned int dwTempData;
  unsigned short wLinePosOdd = 0;
  unsigned short wLinePosEven = 0;
  unsigned short i;
  SANE_Byte * lpTemp = lpLine;

  (void) isOrderInvert;
  DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: call in\n");

  TotalXferLines = 0;
  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono16BitLine1200DPI: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  for (; TotalXferLines < wWantedTotalLines;)
    {
      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC,
	       "MustScanner_GetMono16BitLine1200DPI: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  if (ST_Reflective == g_ScanType)
	    {
	      wLinePosOdd =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	      wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
	    }
	  else
	    {
	      wLinePosEven =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	      wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
	    }


	  for (i = 0; i < g_SWWidth;)
	    {
	      if ((i + 1) != g_SWWidth)
		{
		  dwTempData =
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosOdd * g_BytesPerRow + i * 2 + 0));
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosOdd * g_BytesPerRow + i * 2 + 1) << 8);
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosEven * g_BytesPerRow + (i + 1) * 2 +
			      0));
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosEven * g_BytesPerRow + (i + 1) * 2 +
			      1) << 8);
		  dwTempData = g_pGammaTable[dwTempData >> 1];
		  *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData);
		  *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData);
		  i++;
		  if (i >= g_SWWidth)
		    {
		      break;
		    }

		  dwTempData =
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosEven * g_BytesPerRow + i * 2 + 0));
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosEven * g_BytesPerRow + i * 2 + 1) << 8);
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosOdd * g_BytesPerRow + (i + 1) * 2 + 0));
		  dwTempData +=
		    (unsigned int) (*
			     (g_lpReadImageHead +
			      wLinePosOdd * g_BytesPerRow + (i + 1) * 2 +
			      1) << 8);
		  dwTempData = g_pGammaTable[dwTempData >> 1];
		  *(lpLine + i * 2 + 0) = LOBYTE ((unsigned short) dwTempData);
		  *(lpLine + i * 2 + 1) = HIBYTE ((unsigned short) dwTempData);
		  i++;
		}
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;
	  lpLine += g_SWBytesPerRow;
	  AddReadyLines ();
	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC,
	       "MustScanner_GetMono16BitLine1200DPI: thread exit\n");

	  break;
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  /*for modify the last point */
  if (g_bIsFirstReadBefData)
    {
      g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow);
      if (NULL == g_lpBefLineImageData)
	{
	  return FALSE;
	}
      memset (g_lpBefLineImageData, 0, g_SWBytesPerRow);
      memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow);
      g_bIsFirstReadBefData = FALSE;
    }

  ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow,
		   wWantedTotalLines, 2, 4);

  memcpy (g_lpBefLineImageData,
	  lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow,
	  g_SWBytesPerRow);
  g_dwAlreadyGetLines += wWantedTotalLines;
  if (g_dwAlreadyGetLines >= g_SWHeight)
    {
      DBG (DBG_FUNC,
	   "MustScanner_GetMono16BitLine1200DPI: free before line data!\n");
      free (g_lpBefLineImageData);
      g_lpBefLineImageData = NULL;
      g_dwAlreadyGetLines = 0;
      g_bIsFirstReadBefData = TRUE;
    }

  DBG (DBG_FUNC,
       "MustScanner_GetMono16BitLine1200DPI: leave MustScanner_GetMono16BitLine1200DPI\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when single CCD and color is 8bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono8BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
			     unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;

  unsigned short i;
  unsigned short wLinePos = 0;

  (void) isOrderInvert;
  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: call in\n");

  TotalXferLines = 0;
  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  for (; TotalXferLines < wWantedTotalLines;)
    {
      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  wLinePos = g_wtheReadyLines % g_wMaxScanLines;

	  for (i = 0; i < g_SWWidth; i++)
	    {
	      *(lpLine + i) =
		(SANE_Byte) * (g_pGammaTable +
			  (unsigned short) ((*
				   (g_lpReadImageHead +
				    wLinePos * g_BytesPerRow +
				    i) << 4) | (rand () & 0x0f)));
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;
	  lpLine += g_SWBytesPerRow;
	  AddReadyLines ();

	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine: thread exit\n");

	  break;
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetMono8BitLine: leave MustScanner_GetMono8BitLine\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when double CCD and color is 8bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono8BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
				    unsigned short * wLinesCount)
{
  SANE_Byte *lpTemp;
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;

  unsigned short wLinePosOdd = 0;
  unsigned short wLinePosEven = 0;
  SANE_Byte byGray;
  unsigned short i;
  SANE_Byte bNextPixel = 0;

  (void) isOrderInvert;
  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: call in\n");

  TotalXferLines = 0;
  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;
  lpTemp = lpLine;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  for (; TotalXferLines < wWantedTotalLines;)
    {
      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  if (ST_Reflective == g_ScanType)

	    {
	      wLinePosOdd =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	      wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
	    }
	  else
	    {
	      wLinePosEven =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	      wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
	    }


	  for (i = 0; i < g_SWWidth;)
	    {
	      if ((i + 1) != g_SWWidth)
		{
		  byGray =
		    *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i);
		  bNextPixel =
		    *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow +
		      (i + 1));
		  byGray = (byGray + bNextPixel) >> 1;

		  *(lpLine + i) =
		    (SANE_Byte) * (g_pGammaTable +
			      (byGray << 4 | (rand () & 0x0f)));
		  i++;
		  if (i >= g_SWWidth)
		    {
		      break;
		    }

		  byGray =
		    *(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i);
		  bNextPixel =
		    *(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow +
		      (i + 1));
		  byGray = (byGray + bNextPixel) >> 1;

		  *(lpLine + i) =
		    (SANE_Byte) * (g_pGammaTable +
			      (byGray << 4 | (rand () & 0x0f)));
		  i++;
		}
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;
	  lpLine += g_SWBytesPerRow;
	  AddReadyLines ();
	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono8BitLine1200DPI: thread exit\n");

	  break;
	}
    }


  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  /*for modify the last point */
  if (g_bIsFirstReadBefData)
    {
      g_lpBefLineImageData = (SANE_Byte *) malloc (g_SWBytesPerRow);
      if (NULL == g_lpBefLineImageData)
	{
	  return FALSE;
	}
      memset (g_lpBefLineImageData, 0, g_SWBytesPerRow);
      memcpy (g_lpBefLineImageData, lpTemp, g_SWBytesPerRow);
      g_bIsFirstReadBefData = FALSE;
    }

  ModifyLinePoint (lpTemp, g_lpBefLineImageData, g_SWBytesPerRow,
		   wWantedTotalLines, 1, 4);

  memcpy (g_lpBefLineImageData,
	  lpTemp + (wWantedTotalLines - 1) * g_SWBytesPerRow,
	  g_SWBytesPerRow);
  g_dwAlreadyGetLines += wWantedTotalLines;
  if (g_dwAlreadyGetLines >= g_SWHeight)
    {
      DBG (DBG_FUNC,
	   "MustScanner_GetMono8BitLine1200DPI: free the before line data!\n");
      free (g_lpBefLineImageData);
      g_lpBefLineImageData = NULL;
      g_dwAlreadyGetLines = 0;
      g_bIsFirstReadBefData = TRUE;
    }

  DBG (DBG_FUNC,
       "MustScanner_GetMono8BitLine1200DPI: leave MustScanner_GetMono8BitLine1200DPI\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when single CCD and color is 1bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono1BitLine (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
			     unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned short wLinePos;
  unsigned short i;

  (void) isOrderInvert;

  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: call in\n");

  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8);

  for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;)

    {
      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  wLinePos = g_wtheReadyLines % g_wMaxScanLines;

	  for (i = 0; i < g_SWWidth; i++)
	    {
	      if (*(g_lpReadImageHead + wLinePos * g_BytesPerRow + i) >
		  g_wLineartThreshold)
		{
		  *(lpLine + i / 8) += (0x80 >> (i % 8));
		}
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;
	  lpLine += (g_SWBytesPerRow / 8);
	  AddReadyLines ();
	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine: thread exit\n");

	  break;
	}
    }

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetMono1BitLine: leave MustScanner_GetMono1BitLine\n");
  return TRUE;
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Repair line when double CCD and color is 1bit
Parameters:
	lpLine: point to image be repaired
	isOrderInvert: RGB or BGR
	wLinesCount: how many line be repaired
Return value:
	if the operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static SANE_Bool
MustScanner_GetMono1BitLine1200DPI (SANE_Byte * lpLine, SANE_Bool isOrderInvert,
				    unsigned short * wLinesCount)
{
  unsigned short wWantedTotalLines;
  unsigned short TotalXferLines;
  unsigned short i;
  unsigned short wLinePosOdd;
  unsigned short wLinePosEven;

  (void) isOrderInvert;

  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: call in\n");

  g_isCanceled = FALSE;
  g_isScanning = TRUE;
  wWantedTotalLines = *wLinesCount;

  if (g_bFirstReadImage)
    {
      pthread_create (&g_threadid_readimage, NULL,
		      MustScanner_ReadDataFromScanner, NULL);
      DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread create\n");
      g_bFirstReadImage = FALSE;
    }

  memset (lpLine, 0, wWantedTotalLines * g_SWWidth / 8);

  for (TotalXferLines = 0; TotalXferLines < wWantedTotalLines;)
    {
      if (g_dwTotalTotalXferLines >= g_SWHeight)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n");

	  *wLinesCount = TotalXferLines;
	  g_isScanning = FALSE;
	  return TRUE;
	}

      if (GetScannedLines () > g_wtheReadyLines)
	{
	  if (ST_Reflective == g_ScanType)
	    {
	      wLinePosEven = (g_wtheReadyLines) % g_wMaxScanLines;
	      wLinePosOdd =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	    }
	  else
	    {
	      wLinePosOdd = (g_wtheReadyLines) % g_wMaxScanLines;
	      wLinePosEven =
		(g_wtheReadyLines - g_wPixelDistance) % g_wMaxScanLines;
	    }



	  for (i = 0; i < g_SWWidth;)
	    {
	      if ((i + 1) != g_SWWidth)
		{
		  if (*(g_lpReadImageHead + wLinePosOdd * g_BytesPerRow + i) >
		      g_wLineartThreshold)
		    *(lpLine + i / 8) += (0x80 >> (i % 8));
		  i++;
		  if (i >= g_SWWidth)
		    {
		      break;
		    }

		  if (*(g_lpReadImageHead + wLinePosEven * g_BytesPerRow + i)
		      > g_wLineartThreshold)
		    *(lpLine + i / 8) += (0x80 >> (i % 8));
		  i++;
		}
	    }

	  TotalXferLines++;
	  g_dwTotalTotalXferLines++;
	  lpLine += g_SWBytesPerRow / 8;
	  AddReadyLines ();


	}
      if (g_isCanceled)
	{
	  pthread_cancel (g_threadid_readimage);
	  pthread_join (g_threadid_readimage, NULL);
	  DBG (DBG_FUNC, "MustScanner_GetMono1BitLine1200DPI: thread exit\n");

	  break;
	}
    }				/*end for */

  *wLinesCount = TotalXferLines;
  g_isScanning = FALSE;

  DBG (DBG_FUNC,
       "MustScanner_GetMono1BitLine1200DPI: leave MustScanner_GetMono1BitLine1200DPI\n");
  return TRUE;
}

/**********************************************************************
Author: Jack            Date: 2005/05/21
Routine Description:
	prepare calculate Max and Min value
Parameters:
	wResolution: the scan resolution
Return value:
	none
***********************************************************************/
static void
MustScanner_PrepareCalculateMaxMin (unsigned short wResolution)
{
  g_wDarkCalWidth = 52;
  if (wResolution <= 600)
    {
      g_wCalWidth = ((5120 * wResolution / 600 + 511) >> 9) << 9;
      g_wDarkCalWidth = g_wDarkCalWidth / (1200 / wResolution);

      if (wResolution < 200)
	{
	  g_nPowerNum = 3;
	  g_nSecLength = 8;	/* 2^nPowerNum */
	  g_nDarkSecLength = g_wDarkCalWidth / 2;	/* Dark has at least 2 sections */
	}
      else
	{
	  g_nPowerNum = 6;
	  g_nSecLength = 64;	/* 2^nPowerNum */
	  g_nDarkSecLength = g_wDarkCalWidth / 3;
	}
    }
  else
    {
      g_nPowerNum = 6;
      g_nSecLength = 64;	/*2^nPowerNum */
      g_wCalWidth = 10240;
      g_nDarkSecLength = g_wDarkCalWidth / 5;
    }

  if (g_nDarkSecLength <= 0)
    {
      g_nDarkSecLength = 1;
    }

  g_wStartPosition = 13 * wResolution / 1200;
  g_wCalWidth -= g_wStartPosition;


  /* start of find Max value */
  g_nSecNum = (int) (g_wCalWidth / g_nSecLength);

  /* start of fin min value */
  g_nDarkSecNum = (int) (g_wDarkCalWidth / g_nDarkSecLength);
}

/**********************************************************************
Author: Jack            Date: 2005/05/21
Routine Description:
	calculate the Max and Min value
Parameters:
	pBuffer: the image data
	lpMaxValue: the max value
	lpMinValue: the min value
	wResolution: the scan resolution
Return value:
	none
***********************************************************************/
static void
MustScanner_CalculateMaxMin (SANE_Byte * pBuffer, unsigned short * lpMaxValue,
			     unsigned short * lpMinValue, unsigned short wResolution)
{
  unsigned short *wSecData = NULL, *wDarkSecData = NULL;
  int i, j;

  (void) wResolution;

  wSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nSecNum);
  if (wSecData == NULL)
    {
      return;
    }
  else
    {
      memset (wSecData, 0, g_nSecNum * sizeof (unsigned short));
    }

  for (i = 0; i < g_nSecNum; i++)
    {

      for (j = 0; j < g_nSecLength; j++)
	wSecData[i] += *(pBuffer + g_wStartPosition + i * g_nSecLength + j);
      wSecData[i] >>= g_nPowerNum;
    }

  *lpMaxValue = wSecData[0];
  for (i = 0; i < g_nSecNum; i++)
    {
      if (*lpMaxValue < wSecData[i])
	*lpMaxValue = wSecData[i];
    }

  free (wSecData);

  wDarkSecData = (unsigned short *) malloc (sizeof (unsigned short) * g_nDarkSecNum);
  if (wDarkSecData == NULL)
    {
      return;
    }
  else
    {
      memset (wDarkSecData, 0, g_nDarkSecNum * sizeof (unsigned short));
    }

  for (i = 0; i < g_nDarkSecNum; i++)
    {
      for (j = 0; j < g_nDarkSecLength; j++)
	wDarkSecData[i] +=
	  *(pBuffer + g_wStartPosition + i * g_nDarkSecLength + j);

      wDarkSecData[i] /= g_nDarkSecLength;
    }

  *lpMinValue = wDarkSecData[0];
  for (i = 0; i < g_nDarkSecNum; i++)
    {
      if (*lpMinValue > wDarkSecData[i])
	*lpMinValue = wDarkSecData[i];
    }
  free (wDarkSecData);
}


/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Read the data from scanner
Parameters:
	none
Return value:
	if operation is success
	return TRUE
	else
	return FALSE
***********************************************************************/
static void *
MustScanner_ReadDataFromScanner (void * dummy)
{
  unsigned short wTotalReadImageLines = 0;
  unsigned short wWantedLines = g_Height;
  SANE_Byte * lpReadImage = g_lpReadImageHead;
  SANE_Bool isWaitImageLineDiff = FALSE;
  unsigned int wMaxScanLines = g_wMaxScanLines;
  unsigned short wReadImageLines = 0;
  unsigned short wScanLinesThisBlock;
  unsigned short wBufferLines = g_wLineDistance * 2 + g_wPixelDistance;

  (void) dummy;
  DBG (DBG_FUNC,
       "MustScanner_ReadDataFromScanner: call in, and in new thread\n");

  while (wTotalReadImageLines < wWantedLines && g_lpReadImageHead)
    {
      if (!isWaitImageLineDiff)
	{
	  wScanLinesThisBlock =
	    (wWantedLines - wTotalReadImageLines) <
	    g_wScanLinesPerBlock ? (wWantedLines -
				    wTotalReadImageLines) :
	    g_wScanLinesPerBlock;

	  DBG (DBG_FUNC,
	       "MustScanner_ReadDataFromScanner: wWantedLines=%d\n",
	       wWantedLines);

	  DBG (DBG_FUNC,
	       "MustScanner_ReadDataFromScanner: wScanLinesThisBlock=%d\n",
	       wScanLinesThisBlock);

	  if (SANE_STATUS_GOOD !=
	      Asic_ReadImage (&g_chip, lpReadImage, wScanLinesThisBlock))
	    {
	      DBG (DBG_FUNC,
		   "MustScanner_ReadDataFromScanner:Asic_ReadImage return error\n");
	      DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner:thread exit\n");
	      return NULL;
	    }

	  /*has read in memory Buffer */
	  wReadImageLines += wScanLinesThisBlock;

	  AddScannedLines (wScanLinesThisBlock);

	  wTotalReadImageLines += wScanLinesThisBlock;

	  lpReadImage += wScanLinesThisBlock * g_BytesPerRow;

	  /*Buffer is full */
	  if (wReadImageLines >= wMaxScanLines)
	    {
	      lpReadImage = g_lpReadImageHead;
	      wReadImageLines = 0;
	    }

	  if ((g_dwScannedTotalLines - GetReadyLines ())
	      >= (wMaxScanLines - (wBufferLines + g_wScanLinesPerBlock))
	      && g_dwScannedTotalLines > GetReadyLines ())
	    {
	      isWaitImageLineDiff = TRUE;
	    }
	}
      else if (g_dwScannedTotalLines <=
	       GetReadyLines () + wBufferLines + g_wScanLinesPerBlock)
	{
	  isWaitImageLineDiff = FALSE;
	}

      pthread_testcancel ();
    }

  DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: Read image ok\n");
  DBG (DBG_FUNC, "MustScanner_ReadDataFromScanner: thread exit\n");
  DBG (DBG_FUNC,
       "MustScanner_ReadDataFromScanner: leave MustScanner_ReadDataFromScanner\n");
  return NULL;
}

/**********************************************************************
Author: Jack            Date: 2005/05/26
Routine Description:
	get the lines of scanned
Parameters:
	none
Return value:
	the lines of scanned
***********************************************************************/
static unsigned int
GetScannedLines ()
{
  unsigned int dwScannedLines = 0;

  pthread_mutex_lock (&g_scannedLinesMutex);
  dwScannedLines = g_dwScannedTotalLines;
  pthread_mutex_unlock (&g_scannedLinesMutex);

  return dwScannedLines;
}

/**********************************************************************
Author: Jack            Date: 2005/05/26

Routine Description:
	get lines which pass to superstratum
Parameters:
	none
Return value:
	the lines which pass to superstratum
***********************************************************************/
static unsigned int
GetReadyLines ()
{
  unsigned int dwReadyLines = 0;

  pthread_mutex_lock (&g_readyLinesMutex);
  dwReadyLines = g_wtheReadyLines;
  pthread_mutex_unlock (&g_readyLinesMutex);

  return dwReadyLines;
}

/**********************************************************************
Author: Jack            Date: 2005/05/26
Routine Description:
	add the scanned total lines
Parameters:
	wAddLines: add the lines
Return value:
	none
***********************************************************************/
static void
AddScannedLines (unsigned short wAddLines)
{
  pthread_mutex_lock (&g_scannedLinesMutex);

  g_dwScannedTotalLines += wAddLines;

  pthread_mutex_unlock (&g_scannedLinesMutex);
}

/**********************************************************************
Author: Jack            Date: 2005/05/26
Routine Description:
	add the ready lines
Parameters:
	none
Return value:
	none
***********************************************************************/
static void
AddReadyLines ()
{
  pthread_mutex_lock (&g_readyLinesMutex);
  g_wtheReadyLines++;
  pthread_mutex_unlock (&g_readyLinesMutex);
}

/**********************************************************************
Author: Jack            Date: 2005/05/26
Routine Description:
	modify the point
Parameters:
	lpImageData: the data of image
  lpImageDataBefore: the data of before line image
  dwBytesPerLine: the bytes of per line
  dwLinesCount: the line count
  wPixDistance: the pixel distance
  wModPtCount: the modify point count
Return value:
	none
***********************************************************************/
static void
ModifyLinePoint (SANE_Byte * lpImageData,
		 SANE_Byte * lpImageDataBefore,
		 unsigned int dwBytesPerLine,
		 unsigned int dwLinesCount, unsigned short wPixDistance, unsigned short wModPtCount)
{
  unsigned short i = 0;
  unsigned short j = 0;
  unsigned short wLines = 0;
  unsigned int dwWidth = dwBytesPerLine / wPixDistance;
  for (i = wModPtCount; i > 0; i--)
    {
      for (j = 0; j < wPixDistance; j++)
	{
	  /*modify the first line */
	  *(lpImageData + (dwWidth - i) * wPixDistance + j) =
	    (*(lpImageData + (dwWidth - i - 1) * wPixDistance + j) +
	     *(lpImageDataBefore + (dwWidth - i) * wPixDistance + j)) / 2;
	  /*modify other lines */
	  for (wLines = 1; wLines < dwLinesCount; wLines++)
	    {
	      unsigned int dwBytesBefor = (wLines - 1) * dwBytesPerLine;
	      unsigned int dwBytes = wLines * dwBytesPerLine;
	      *(lpImageData + dwBytes + (dwWidth - i) * wPixDistance + j) =
		(*
		 (lpImageData + dwBytes + (dwWidth - i - 1) * wPixDistance +
		  j) + *(lpImageData + dwBytesBefor + (dwWidth -
						       i) * wPixDistance +
			 j)) / 2;
	    }
	}
    }
}

/**********************************************************************
Author: Jack             Date: 2005/05/15
Routine Description:
	Modify the image data
Parameters:
	A: the input the image data
	B: the input the image data
Return value:
	the modified data
***********************************************************************/
static SANE_Byte
QBET4 (SANE_Byte A, SANE_Byte B)
{
  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}
  };

  A = A & 0x0f;
  B = B & 0x0f;
  return bQBET[A][B];
}				/* end of the file MustScanner.c */
