/*.............................................................................
 * Project : SANE library for Plustek flatbed scanners.
 *.............................................................................
 */

/** @file plustek-usbimg.c
 *  @brief Image processing functions for copying and scaling image lines.
 *
 * Based on sources acquired from Plustek Inc.<br>
 * Copyright (C) 2001-2007 Gerhard Jaeger <gerhard@gjaeger.de>
 *
 * History:
 * - 0.40 - starting version of the USB support
 * - 0.41 - fixed the 14bit problem for LM9831 devices
 * - 0.42 - no changes
 * - 0.43 - no changes
 * - 0.44 - added CIS parts and dumpPic function
 * - 0.45 - added gray scaling functions for CIS devices
 *        - fixed usb_GrayScale16 function
 *        - fixed a bug in usb_ColorScale16_2 function
 *        - fixed endless loop bug
 *        - fixed a bug in usb_GrayScalePseudo16 function
 *        - fixed a bug in usb_GrayDuplicatePseudo16 function
 *        - removed the scaler stuff for CIS devices
 * - 0.46 - minor fixes
 * - 0.47 - added big-endian/little endian stuff
 * - 0.48 - fixed usb_ColorDuplicateGray16() and
 *          usb_ColorScaleGray16()
 *        - added usb_BWScaleFromColor() and usb_BWDuplicateFromColor()
 *        - cleanup
 * - 0.49 - a_bRegs is now part of the device structure
 * - 0.50 - cleanup
 * - 0.51 - added usb_ColorDuplicateGray16_2(), usb_ColorScaleGray16_2()
 *          usb_BWScaleFromColor_2() and usb_BWDuplicateFromColor_2()
 * - 0.52 - cleanup
 * .
 * <hr>
 * 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.
 * <hr>
 */

#define _SCALER  1000

static u_char   bShift, Shift;
static u_short  wSum, Mask;

/*
 */
static u_char BitTable[8] = {
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};

static u_char BitsReverseTable[256] = {
	0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
	0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
	0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
	0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
	0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
	0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
	0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
	0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
	0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
	0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
	0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
	0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
	0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
	0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
	0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
	0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
	0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
	0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
	0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
	0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
	0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
	0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
	0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
	0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
	0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
	0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
	0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
	0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
	0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
	0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
	0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
	0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

/************************ some helper functions ******************************/

static void ReverseBits( int b, u_char **pTar, int *iByte, int *iWeightSum,
                         int iSrcWeight, int iTarWeight, int cMax )
{
	int bit;

	cMax = (1 << cMax);
	if(iSrcWeight == iTarWeight) {
		for( bit = 1; bit < cMax; bit <<= 1) {
			*iByte <<= 1;
			if(b & bit)
				*iByte |= 1;
			if(*iByte >= 0x100)	{
				**pTar++ = (u_char)*iByte;
				*iByte = 1;
			}
		}
	} else {
		for( bit = 1; bit < cMax; bit <<= 1 ) {

			*iWeightSum += iTarWeight;
			while(*iWeightSum >= iSrcWeight)
			{
				*iWeightSum -= iSrcWeight;
				*iByte <<= 1;
				if(b & bit)
					*iByte |= 1;
				if(*iByte >= 0x100)
				{
					**pTar++ = (u_char)*iByte;
					*iByte = 1;
				}
			}
		}
	}
}

static void usb_ReverseBitStream( u_char *pSrc, u_char *pTar, int iPixels,
                                  int iBufSize, int iSrcWeight/* = 0*/,
                                  int iTarWeight/* = 0*/, int iPadBit/* = 1*/)
{
	int i;
	int iByte = 1;
	int cBytes = iPixels / 8;
	int cBits = iPixels % 8;
	u_char bPad = iPadBit? 0xff: 0;
	u_char *pTarget = pTar;
	int iWeightSum = 0;

	if(iSrcWeight == iTarWeight) {
		if(cBits)
		{
			int cShift = 8 - cBits;
			for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--, pTar++)
				*pTar = BitsReverseTable[(u_char)((*pSrc << cBits) | (*(pSrc+1) >> cShift))];
			ReverseBits(*(pSrc+1) >> cShift, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, cBits);
		}
		else /* byte boundary */
		{
			for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--, pTar++)
				*pTar = BitsReverseTable[*pSrc];
		}
	}
	else /* To shrink or enlarge */
	{
		if(cBits) {
			int cShift = 8 - cBits;
			for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--)
				ReverseBits((*pSrc << cBits) | (*(pSrc+1) >> cShift), &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, 8);
			ReverseBits(*(pSrc+1) >> cShift, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, cBits);
		}
		else /* byte boundary */
		{
			for(i = cBytes, pSrc = pSrc + cBytes - 1; i > 0; i--, pSrc--)
				ReverseBits(*pSrc, &pTar, &iByte, &iWeightSum, iSrcWeight, iTarWeight, 8);
		}
	}

	if(iByte != 1) {
		while(iByte < 0x100)
		{
			iByte <<= 1;
			iByte |= iPadBit;
		}
		*pTar++ = (u_char)iByte;
	}

	cBytes = (int)(pTar - pTarget);

	for(i = iBufSize - cBytes; i > 0; i--, pTar++)
		*pTar = bPad;
}

/**
 */
static void usb_AverageColorByte( Plustek_Device *dev )
{
	u_long   dw;
	ScanDef *scan = &dev->scanning;

	if((scan->sParam.bSource == SOURCE_Negative ||
	    scan->sParam.bSource == SOURCE_Transparency) &&
	    scan->sParam.PhyDpi.x > 800) {

		for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
		{
			scan->Red.pcb[dw].a_bColor[0] =
			                 (u_char)(((u_short)scan->Red.pcb[dw].a_bColor[0] +
			                 (u_short)scan->Red.pcb[dw + 1].a_bColor[0]) / 2);

			scan->Green.pcb[dw].a_bColor[0] =
			               (u_char)(((u_short)scan->Green.pcb[dw].a_bColor[0] +
			               (u_short)scan->Green.pcb[dw + 1].a_bColor[0]) / 2);

			scan->Blue.pcb[dw].a_bColor[0] =
			               (u_char)(((u_short)scan->Blue.pcb[dw].a_bColor[0] +
			               (u_short)scan->Blue.pcb[dw + 1].a_bColor[0]) / 2);
		}
	}
}

/**
 */
static void usb_AverageColorWord( Plustek_Device *dev )
{
	u_char   ls = 2;
	u_long   dw;
	ScanDef *scan = &dev->scanning;

	if((scan->sParam.bSource == SOURCE_Negative ||
		scan->sParam.bSource == SOURCE_Transparency) &&
		scan->sParam.PhyDpi.x > 800) {

		scan->Red.pcw[0].Colors[0]   = _HILO2WORD(scan->Red.pcw[0].HiLo[0])   >> ls;
		scan->Green.pcw[0].Colors[0] = _HILO2WORD(scan->Green.pcw[0].HiLo[0]) >> ls;
		scan->Blue.pcw[0].Colors[0]  = _HILO2WORD(scan->Blue.pcw[0].HiLo[0])  >> ls;

		for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
		{
			scan->Red.pcw[dw + 1].Colors[0]   = _HILO2WORD(scan->Red.pcw[dw + 1].HiLo[0])   >> ls;
			scan->Green.pcw[dw + 1].Colors[0] = _HILO2WORD(scan->Green.pcw[dw + 1].HiLo[0]) >> ls;
			scan->Blue.pcw[dw + 1].Colors[0]  = _HILO2WORD(scan->Blue.pcw[dw + 1].HiLo[0])  >> ls;

			scan->Red.pcw[dw].Colors[0]   = (u_short)(((u_long)scan->Red.pcw[dw].Colors[0] +
			                                           (u_long)scan->Red.pcw[dw + 1].Colors[0]) / 2);
			scan->Green.pcw[dw].Colors[0] = (u_short)(((u_long)scan->Green.pcw[dw].Colors[0] +
			                                           (u_long)scan->Green.pcw[dw + 1].Colors[0]) / 2);
			scan->Blue.pcw[dw].Colors[0]  = (u_short)(((u_long)scan->Blue.pcw[dw].Colors[0] +
			                                           (u_long)scan->Blue.pcw[dw + 1].Colors[0]) / 2);

			scan->Red.pcw[dw].Colors[0]   = _HILO2WORD(scan->Red.pcw[dw].HiLo[0])   << ls;
			scan->Green.pcw[dw].Colors[0] = _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) << ls;
			scan->Blue.pcw[dw].Colors[0]  = _HILO2WORD(scan->Blue.pcw[dw].HiLo[0])  << ls;
		}

		scan->Red.pcw[dw].Colors[0]   = _HILO2WORD(scan->Red.pcw[dw].HiLo[0])   << ls;
		scan->Green.pcw[dw].Colors[0] = _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) << ls;
		scan->Blue.pcw[dw].Colors[0]  = _HILO2WORD(scan->Blue.pcw[dw].HiLo[0])  << ls;
	}
}

/**
 */
static void usb_AverageGrayByte( Plustek_Device *dev )
{
	u_long   dw;
	ScanDef *scan = &dev->scanning;

	if((scan->sParam.bSource == SOURCE_Negative ||
		scan->sParam.bSource == SOURCE_Transparency) &&
		scan->sParam.PhyDpi.x > 800)
	{
		for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
			scan->Green.pb[dw] = (u_char)(((u_short)scan->Green.pb[dw]+
			                           (u_short)scan->Green.pb[dw+1]) / 2);
	}
}

/**
 */
static void usb_AverageGrayWord( Plustek_Device *dev )
{
	u_long   dw;
	ScanDef *scan = &dev->scanning;

	if((scan->sParam.bSource == SOURCE_Negative ||
		scan->sParam.bSource == SOURCE_Transparency) &&
		scan->sParam.PhyDpi.x > 800)
	{
		scan->Green.pw[0] = _HILO2WORD(scan->Green.philo[0]) >> 2;

		for (dw = 0; dw < (scan->sParam.Size.dwPhyPixels - 1); dw++)
		{
			scan->Green.pw[dw + 1] = _HILO2WORD(scan->Green.philo[dw+1]) >> 2;
			scan->Green.pw[dw] = (u_short)(((u_long)scan->Green.pw[dw]+
			                                (u_long)scan->Green.pw[dw+1]) / 2);
			scan->Green.pw[dw] = _HILO2WORD(scan->Green.philo[dw]) << 2;
		}

		scan->Green.pw[dw] = _HILO2WORD(scan->Green.philo[dw]) << 2;
	}
}

/**
 * returns the zoom value, used for our scaling algorithm (DDA algo
 * digital differential analyzer).
 */
static int usb_GetScaler( ScanDef *scan )
{
	double ratio;

	ratio = (double)scan->sParam.UserDpi.x/
			(double)scan->sParam.PhyDpi.x;

	return (int)(1.0/ratio * _SCALER);
}

/******************************* the copy functions **************************/

/** do a simple memcopy from scan-buffer to user buffer
 */
static void usb_ColorDuplicate8( Plustek_Device *dev )
{
	int      next;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next ) {

		scan->UserBuf.pb_rgb[pixels].Red   = scan->Red.pcb[dw].a_bColor[0];
		scan->UserBuf.pb_rgb[pixels].Green = scan->Green.pcb[dw].a_bColor[0];
		scan->UserBuf.pb_rgb[pixels].Blue  = scan->Blue.pcb[dw].a_bColor[0];
	}
}

/** reorder from rgb line to rgb pixel (CIS scanner)
 */
static void usb_ColorDuplicate8_2( Plustek_Device *dev )
{
	int      next;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next  = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next ) {

		scan->UserBuf.pb_rgb[pixels].Red   = (u_char)scan->Red.pb[dw];
		scan->UserBuf.pb_rgb[pixels].Green = (u_char)scan->Green.pb[dw];
		scan->UserBuf.pb_rgb[pixels].Blue  = (u_char)scan->Blue.pb[dw];
	}
}

/**
 */
static void usb_ColorDuplicate16( Plustek_Device *dev )
{
	int       next;
	u_char    ls;
	u_long    dw, pixels;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageColorWord( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next  = 1;
		pixels = 0;
	}

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {

		if( swap ) {
			scan->UserBuf.pw_rgb[pixels].Red =
			                  _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) >> ls;
			scan->UserBuf.pw_rgb[pixels].Green =
			                  _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) >> ls;
			scan->UserBuf.pw_rgb[pixels].Blue =
			                  _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) >> ls;
		} else {
			scan->UserBuf.pw_rgb[pixels].Red  = scan->Red.pw[dw]   >> ls;
			scan->UserBuf.pw_rgb[pixels].Green= scan->Green.pw[dw] >> ls;
			scan->UserBuf.pw_rgb[pixels].Blue = scan->Blue.pw[dw]  >> ls;
		}
	}
}

/**
 */
static void usb_ColorDuplicate16_2( Plustek_Device *dev )
{
	int       next;
	u_char    ls;
	HiLoDef   tmp;
	u_long    dw, pixels;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageColorWord( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	for( dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {

		if( swap ) {
			tmp = *((HiLoDef*)&scan->Red.pw[dw]);
			scan->UserBuf.pw_rgb[pixels].Red = _HILO2WORD(tmp) >> ls;

			tmp = *((HiLoDef*)&scan->Green.pw[dw]);
			scan->UserBuf.pw_rgb[pixels].Green = _HILO2WORD(tmp) >> ls;

			tmp = *((HiLoDef*)&scan->Blue.pw[dw]);
			scan->UserBuf.pw_rgb[pixels].Blue = _HILO2WORD(tmp) >> ls;

		} else {

			scan->UserBuf.pw_rgb[pixels].Red   = scan->Red.pw[dw]   >> ls;
			scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[dw] >> ls;
			scan->UserBuf.pw_rgb[pixels].Blue  = scan->Blue.pw[dw]  >> ls;
		}
	}
}

/**
 */
static void usb_ColorDuplicatePseudo16( Plustek_Device *dev )
{
	int      next;
	u_short  wR, wG, wB;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	wR = (u_short)scan->Red.pcb[0].a_bColor[0];
	wG = (u_short)scan->Green.pcb[0].a_bColor[0];
	wB = (u_short)scan->Blue.pcb[0].a_bColor[0];

	for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {

		scan->UserBuf.pw_rgb[pixels].Red   =
		                        (wR + scan->Red.pcb[dw].a_bColor[0]) << bShift;
		scan->UserBuf.pw_rgb[pixels].Green =
		                      (wG + scan->Green.pcb[dw].a_bColor[0]) << bShift;
		scan->UserBuf.pw_rgb[pixels].Blue  =
		                       (wB + scan->Blue.pcb[dw].a_bColor[0]) << bShift;

		wR = (u_short)scan->Red.pcb[dw].a_bColor[0];
		wG = (u_short)scan->Green.pcb[dw].a_bColor[0];
		wB = (u_short)scan->Blue.pcb[dw].a_bColor[0];
	}
}

/**
 */
static void usb_ColorDuplicateGray( Plustek_Device *dev )
{
	int      next;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	switch(scan->fGrayFromColor) {

	case 1:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Red.pcb[dw].a_bColor[0];
		break;
	case 2:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Green.pcb[dw].a_bColor[0];
		break;
	case 3:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Blue.pcb[dw].a_bColor[0];
		break;
	}
}

/**
 */
static void usb_ColorDuplicateGray_2( Plustek_Device *dev )
{
	int      next;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	switch(scan->fGrayFromColor)
	{
	case 1:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Red.pb[dw];
		break;
	case 3:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Blue.pb[dw];
		break;
	default:
		for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
			scan->UserBuf.pb[pixels] = scan->Green.pb[dw];
		break;
	}
}

/**
 */
static void usb_ColorDuplicateGray16( Plustek_Device *dev )
{
	int       next;
	u_char    ls;
	u_long    dw, pixels;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageColorWord( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}
	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	switch(scan->fGrayFromColor) {

	case 1:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] =
				                   _HILO2WORD(scan->Red.pcw[dw].HiLo[0]) >> ls;
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
		}
		break;
	case 2:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] =
				                 _HILO2WORD(scan->Green.pcw[dw].HiLo[0]) >> ls;
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] = scan->Green.pw[dw] >> ls;
		}
		break;
	case 3:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] =
				                   _HILO2WORD(scan->Blue.pcw[dw].HiLo[0]) >> ls;
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next)
				scan->UserBuf.pw[pixels] = scan->Blue.pw[dw] >> ls;
		}
		break;
	}
}

/**
 */
static void usb_ColorDuplicateGray16_2( Plustek_Device *dev )
{
	int       next;
	u_char    ls;
	u_long    dw, pixels;
	HiLoDef   tmp;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageColorWord( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}
	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	switch(scan->fGrayFromColor) {
	case 1:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				tmp = *((HiLoDef*)&scan->Red.pw[dw]);
				scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
			}
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
			}
		}
		break;
	case 2:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				tmp = *((HiLoDef*)&scan->Green.pw[dw]);
				scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
			}
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				scan->UserBuf.pw[pixels] = scan->Green.pw[dw] >> ls;
			}
		}
		break;
	case 3:
		if( swap ) {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				tmp = *((HiLoDef*)&scan->Blue.pw[dw]);
				scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
			}
		} else {
			for (dw = 0; dw < scan->sParam.Size.dwPixels; dw++, pixels += next) {
				scan->UserBuf.pw[pixels] = scan->Blue.pw[dw] >> ls;
			}
		}
		break;
	}
}

/**
 */
static void usb_GrayDuplicate8( Plustek_Device *dev )
{
	u_char  *dest, *src;
	u_long   pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageGrayByte( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {

		pixels = scan->sParam.Size.dwPixels;
		src    = scan->Green.pb;
		dest   = scan->UserBuf.pb + pixels - 1;

		for(; pixels; pixels--, src++, dest--)
			*dest = *src;
	} else {
		memcpy( scan->UserBuf.pb, scan->Green.pb, scan->sParam.Size.dwBytes );
	}
}

/**
 */
static void usb_GrayDuplicate16( Plustek_Device *dev )
{
	int       next;
	u_char    ls;
	u_short  *dest;
	u_long    pixels;
	HiLoDef  *pwm;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageGrayWord( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next = -1;
		dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
	} else {
		next = 1;
		dest = scan->UserBuf.pw;
	}

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	pwm = scan->Green.philo;
	for( pixels=scan->sParam.Size.dwPixels; pixels--; pwm++, dest += next ) {
		if( swap )
			*dest = (_PHILO2WORD(pwm)) >> ls;
		else
			*dest = (_PLOHI2WORD(pwm)) >> ls;
	}
}

/**
 */
static void usb_GrayDuplicatePseudo16( Plustek_Device *dev )
{
	u_char  *src;
	int      next;
	u_short  g;
	u_short *dest;
	u_long   pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageGrayByte( dev );

	if (scan->sParam.bSource == SOURCE_ADF) {
		next = -1;
		dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
	} else {
		next = 1;
		dest = scan->UserBuf.pw;
	}

	src = scan->Green.pb;
	g = (u_short)*src;

	for( pixels=scan->sParam.Size.dwPixels; pixels--; src++, dest += next ) {

		*dest = (g + *src) << bShift;
		g = (u_short)*src;
	}
}

/** copy binary data to the user buffer
 */
static void usb_BWDuplicate( Plustek_Device *dev )
{
	ScanDef *scan = &dev->scanning;

	if(scan->sParam.bSource == SOURCE_ADF)
	{
		usb_ReverseBitStream( scan->Green.pb, scan->UserBuf.pb,
		                      scan->sParam.Size.dwValidPixels,
		                      scan->dwBytesLine, 0, 0, 1 );
	} else {
		memcpy( scan->UserBuf.pb, scan->Green.pb, scan->sParam.Size.dwBytes );
	}
}

/** generate binary data from one of the three color inputs according to the
 *  value in fGrayFromColor (CCD version)
 */
static void usb_BWDuplicateFromColor( Plustek_Device *dev )
{
	int           next;
	u_char        d, s, *dest;
	u_short       j;
	u_long        pixels;
	ColorByteDef *src;
	ScanDef      *scan = &dev->scanning;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
		next = -1;
	} else {
		dest = scan->UserBuf.pb;
		next = 1;
	}

	switch(scan->fGrayFromColor) {
		case 1:  src = scan->Red.pcb;   break;
		case 3:  src = scan->Blue.pcb;  break;
		default: src = scan->Green.pcb; break;
	}

	d = j = 0;
	for( pixels = scan->sParam.Size.dwPixels; pixels; pixels--, src++ ) {

		s = src->a_bColor[0];
		if( s != 0 )
			d |= BitTable[j];
		j++;
		if( j == 8 ) {
			*dest = d;
			dest += next;

			d = j = 0;
		}
	}
}

/** generate binary data from one of the three color inputs according to the
 *  value in fGrayFromColor (CIS version)
 */
static void usb_BWDuplicateFromColor_2( Plustek_Device *dev )
{
	int      next;
	u_char   d, *dest, *src;
	u_short  j;
	u_long   pixels;
	ScanDef *scan = &dev->scanning;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
		next = -1;
	} else {
		dest = scan->UserBuf.pb;
		next = 1;
	}

	switch(scan->fGrayFromColor) {
		case 1:  src = scan->Red.pb;   break;
		case 3:  src = scan->Blue.pb;  break;
		default: src = scan->Green.pb; break;
	}

	d = j = 0;
	for( pixels = scan->sParam.Size.dwPixels; pixels; pixels--, src++ ) {

		if( *src != 0 )
			d |= BitTable[j];
		j++;
		if( j == 8 ) {
			*dest = d;
			dest += next;

			d = j = 0;
		}
	}
}

/************************** the scaling functions ****************************/

/**
 */
static void usb_ColorScaleGray( Plustek_Device *dev )
{
	int           izoom, ddax, next;
	u_long        dw, pixels;
	ColorByteDef *src;
	ScanDef      *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	switch(scan->fGrayFromColor) {
		case 1:  src = scan->Red.pcb;   break;
		case 3:  src = scan->Blue.pcb;  break;
		default: src = scan->Green.pcb; break;
	}

	izoom = usb_GetScaler( scan );

	for( ddax = 0; dw; src++ ) {

		ddax -= _SCALER;
		while((ddax < 0) && (dw > 0)) {

			scan->UserBuf.pb[pixels] = src->a_bColor[0];

			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

/**
 */
static void usb_ColorScaleGray_2( Plustek_Device *dev )
{
	u_char  *src;
	int      izoom, ddax, next;
	u_long   dw, pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	switch(scan->fGrayFromColor) {
		case 1:  src = scan->Red.pb;   break;
		case 3:  src = scan->Blue.pb;  break;
		default: src = scan->Green.pb; break;
	}

	izoom = usb_GetScaler( scan );

	for( ddax = 0; dw; src++ ) {

		ddax -= _SCALER;
		while((ddax < 0) && (dw > 0)) {

			scan->UserBuf.pb[pixels] = *src;

			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

/**
 */
static void usb_ColorScaleGray16( Plustek_Device *dev )
{
	u_char    ls;
	int       izoom, ddax, next;
	u_long    dw, pixels, bitsput;
	SANE_Bool swap = usb_HostSwap();
	ScanDef  *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	switch( scan->fGrayFromColor ) {

	case 1:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					scan->UserBuf.pw[pixels] =
					        _HILO2WORD(scan->Red.pcw[bitsput].HiLo[0]) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Red.pw[bitsput] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;

	case 2:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					scan->UserBuf.pw[pixels] =
					      _HILO2WORD(scan->Green.pcw[bitsput].HiLo[0]) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Green.pw[bitsput] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;

	case 3:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					scan->UserBuf.pw[pixels] =
					       _HILO2WORD(scan->Blue.pcw[bitsput].HiLo[0]) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Blue.pw[bitsput] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;
	}
}

/**
 */
static void usb_ColorScaleGray16_2( Plustek_Device *dev )
{
	u_char    ls;
	int       izoom, ddax, next;
	u_long    dw, pixels, bitsput;
	HiLoDef   tmp;
	SANE_Bool swap = usb_HostSwap();
	ScanDef  *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	switch( scan->fGrayFromColor ) {

	case 1:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					tmp = *((HiLoDef*)&scan->Red.pw[bitsput]);
					scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Red.pw[dw] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;

	case 2:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					tmp = *((HiLoDef*)&scan->Green.pw[bitsput]);
					scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Green.pw[bitsput] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;

	case 3:
		for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

			ddax -= _SCALER;

			while((ddax < 0) && (dw > 0)) {
				if( swap ) {
					tmp = *((HiLoDef*)&scan->Blue.pw[bitsput]);
					scan->UserBuf.pw[pixels] = _HILO2WORD(tmp) >> ls;
				} else {
					scan->UserBuf.pw[pixels] = scan->Blue.pw[bitsput] >> ls;
				}
				pixels += next;
				ddax   += izoom;
				dw--;
			}
		}
		break;
	}
}

/** here we copy and scale from scanner world to user world...
 */
static void usb_ColorScale8( Plustek_Device *dev )
{
	int      izoom, ddax, next;
	u_long   dw, pixels, bitsput;
    ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (dw > 0)) {

			scan->UserBuf.pb_rgb[pixels].Red =
			                            scan->Red.pcb[bitsput].a_bColor[0];
			scan->UserBuf.pb_rgb[pixels].Green =
			                            scan->Green.pcb[bitsput].a_bColor[0];
			scan->UserBuf.pb_rgb[pixels].Blue =
			                            scan->Blue.pcb[bitsput].a_bColor[0];
			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

static void usb_ColorScale8_2( Plustek_Device *dev )
{
	int      izoom, ddax, next;
	u_long   dw, pixels, bitsput;
	ScanDef *scan = &dev->scanning;

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (dw > 0)) {

			scan->UserBuf.pb_rgb[pixels].Red   = scan->Red.pb[bitsput];
			scan->UserBuf.pb_rgb[pixels].Green = scan->Green.pb[bitsput];
			scan->UserBuf.pb_rgb[pixels].Blue  = scan->Blue.pb[bitsput];

			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

/**
 */
static void usb_ColorScale16( Plustek_Device *dev )
{
	u_char    ls;
	int       izoom, ddax, next;
	u_long    dw, pixels, bitsput;
	SANE_Bool swap = usb_HostSwap();
	ScanDef  *scan = &dev->scanning;

	usb_AverageColorWord( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (dw > 0)) {

			if( swap ) {

				scan->UserBuf.pw_rgb[pixels].Red =
				          _HILO2WORD(scan->Red.pcw[bitsput].HiLo[0]) >> ls;

				scan->UserBuf.pw_rgb[pixels].Green =
					      _HILO2WORD(scan->Green.pcw[bitsput].HiLo[0]) >> ls;

				scan->UserBuf.pw_rgb[pixels].Blue =
					      _HILO2WORD(scan->Blue.pcw[bitsput].HiLo[0]) >> ls;

			} else {

				scan->UserBuf.pw_rgb[pixels].Red   = scan->Red.pw[bitsput]>>ls;
				scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[bitsput] >> ls;
				scan->UserBuf.pw_rgb[pixels].Blue  = scan->Blue.pw[bitsput] >> ls;
			}
			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

/**
 */
static void usb_ColorScale16_2( Plustek_Device *dev )
{
	u_char     ls;
	HiLoDef    tmp;
	int        izoom, ddax, next;
	u_long     dw, pixels, bitsput;
	SANE_Bool  swap = usb_HostSwap();
	ScanDef   *scan = &dev->scanning;

	usb_AverageColorWord( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (dw > 0)) {

			if( swap ) {

				tmp = *((HiLoDef*)&scan->Red.pw[bitsput]);
				scan->UserBuf.pw_rgb[pixels].Red = _HILO2WORD(tmp) >> ls;

				tmp = *((HiLoDef*)&scan->Green.pw[bitsput]);
				scan->UserBuf.pw_rgb[pixels].Green = _HILO2WORD(tmp) >> ls;

				tmp = *((HiLoDef*)&scan->Blue.pw[bitsput]);
				scan->UserBuf.pw_rgb[pixels].Blue = _HILO2WORD(tmp) >> ls;

			} else {

				scan->UserBuf.pw_rgb[pixels].Red   = scan->Red.pw[bitsput] >> ls;
				scan->UserBuf.pw_rgb[pixels].Green = scan->Green.pw[bitsput] >> ls;
				scan->UserBuf.pw_rgb[pixels].Blue  = scan->Blue.pw[bitsput] >> ls;
			}
			pixels += next;
			ddax   += izoom;
			dw--;
		}
	}
}

/**
 */
static void usb_ColorScalePseudo16( Plustek_Device *dev )
{
	int      izoom, ddax, next;
	u_short  wR, wG, wB;
	u_long   dw, pixels, bitsput;
	ScanDef *scan = &dev->scanning;

	usb_AverageColorByte( dev );

	dw = scan->sParam.Size.dwPixels;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next   = -1;
		pixels = scan->sParam.Size.dwPixels - 1;
	} else {
		next   = 1;
		pixels = 0;
	}

	izoom = usb_GetScaler( scan );

	wR = (u_short)scan->Red.pcb[0].a_bColor[0];
	wG = (u_short)scan->Green.pcb[0].a_bColor[1];
	wB = (u_short)scan->Blue.pcb[0].a_bColor[2];

	for( bitsput = 0, ddax = 0; dw; bitsput++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (dw > 0)) {

			scan->UserBuf.pw_rgb[pixels].Red =
				(wR + scan->Red.pcb[bitsput].a_bColor[0]) << bShift;

			scan->UserBuf.pw_rgb[pixels].Green =
				(wG + scan->Green.pcb[bitsput].a_bColor[0]) << bShift;

			scan->UserBuf.pw_rgb[pixels].Blue =
				(wB + scan->Blue.pcb[bitsput].a_bColor[0]) << bShift;

			pixels += next;
			ddax   += izoom;
			dw--;
		}

		wR = (u_short)scan->Red.pcb[bitsput].a_bColor[0];
		wG = (u_short)scan->Green.pcb[bitsput].a_bColor[0];
		wB = (u_short)scan->Blue.pcb[bitsput].a_bColor[0];
	}
}

/**
 */
static void usb_BWScale( Plustek_Device *dev )
{
	u_char   tmp, *dest, *src;
	int      izoom, ddax;
	u_long   i, dw;
	ScanDef *scan = &dev->scanning;

	src = scan->Green.pb;
	if( scan->sParam.bSource == SOURCE_ADF ) {
		int iSum = wSum;
		usb_ReverseBitStream(scan->Green.pb, scan->UserBuf.pb,
		                     scan->sParam.Size.dwValidPixels,
		                     scan->dwBytesLine, scan->sParam.PhyDpi.x,
		                     scan->sParam.UserDpi.x, 1 );
		wSum = iSum;
		return;
	} else {
		dest = scan->UserBuf.pb;
	}

	izoom = usb_GetScaler( scan );

	memset( dest, 0, scan->dwBytesLine );
	ddax = 0;
	dw   = 0;

	for( i = 0; i < scan->sParam.Size.dwValidPixels; i++ ) {

		ddax -= _SCALER;

		while( ddax < 0 ) {

			tmp = src[(i>>3)];

			if((dw>>3) < scan->sParam.Size.dwValidPixels ) {

				if( 0 != (tmp &= (1 << ((~(i & 0x7))&0x7))))
					dest[dw>>3] |= (1 << ((~(dw & 0x7))&0x7));
			}
			dw++;
			ddax += izoom;
		}
	}
}

/**
 */
static void usb_BWScaleFromColor( Plustek_Device *dev )
{
	u_char        d, s, *dest;
	u_short       j;
	u_long        pixels;
	int           izoom, ddax, next;
	ColorByteDef *src;
	ScanDef      *scan = &dev->scanning;

	if (scan->sParam.bSource == SOURCE_ADF) {
		dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
		next = -1;
	} else {
		dest = scan->UserBuf.pb;
		next = 1;
	}

	/* setup the source buffer */
	switch(scan->fGrayFromColor) {
	case 1:  src = scan->Red.pcb;   break;
	case 3:  src = scan->Blue.pcb;  break;
	default: src = scan->Green.pcb; break;
	}

	izoom = usb_GetScaler( scan );
	ddax  = 0;

	d = j = 0;
	for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (pixels > 0)) {

			s = src->a_bColor[0];
			if( s != 0 )
				d |= BitTable[j];
			j++;
			if( j == 8 ) {
				*dest = d;
				dest += next;
				d = j = 0;
			}
			ddax   += izoom;
			pixels--;
		}
	}
}

/**
 */
static void usb_BWScaleFromColor_2( Plustek_Device *dev )
{
	u_char        d, *dest, *src;
	u_short       j;
	u_long        pixels;
	int           izoom, ddax, next;
	ScanDef      *scan = &dev->scanning;

	if (scan->sParam.bSource == SOURCE_ADF) {
		dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
		next = -1;
	} else {
		dest = scan->UserBuf.pb;
		next = 1;
	}

	/* setup the source buffer */
	switch(scan->fGrayFromColor) {
	case 1:  src = scan->Red.pb;   break;
	case 3:  src = scan->Blue.pb;  break;
	default: src = scan->Green.pb; break;
	}

	izoom = usb_GetScaler( scan );
	ddax  = 0;

	d = j = 0;
	for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (pixels > 0)) {

			if( *src != 0 )
				d |= BitTable[j];
			j++;
			if( j == 8 ) {
				*dest = d;
				dest += next;
				d = j = 0;
			}
			ddax   += izoom;
			pixels--;
		}
	}
}

/**
 */
static void usb_GrayScale8( Plustek_Device *dev )
{
	u_char  *dest, *src;
	int      izoom, ddax, next;
	u_long   pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageGrayByte( dev );

	src = scan->Green.pb;
	if( scan->sParam.bSource == SOURCE_ADF ) {
		dest = scan->UserBuf.pb + scan->sParam.Size.dwPixels - 1;
		next = -1;
	} else {
		dest = scan->UserBuf.pb;
		next = 1;
	}

	izoom = usb_GetScaler( scan );
	ddax  = 0;

	for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (pixels > 0)) {

			*dest = *src;
			dest += next;
			ddax   += izoom;
			pixels--;
		}
	}
}

/**
 */
static void usb_GrayScale16( Plustek_Device *dev )
{
	u_char    ls;
	int       izoom, ddax, next;
	u_short  *dest;
	u_long    pixels;
	HiLoDef  *pwm;
	ScanDef  *scan = &dev->scanning;
	SANE_Bool swap = usb_HostSwap();

	usb_AverageGrayWord( dev);

	pwm  = scan->Green.philo;
	wSum = scan->sParam.PhyDpi.x;

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next = -1;
		dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
	} else {
		next = 1;
		dest = scan->UserBuf.pw;
	}

	izoom = usb_GetScaler( scan );
	ddax  = 0;

	if( scan->dwFlag & SCANFLAG_RightAlign )
		ls = Shift;
	else
		ls = 0;

	for( pixels = scan->sParam.Size.dwPixels; pixels; pwm++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (pixels > 0)) {

			if( swap )
				*dest = _PHILO2WORD(pwm) >> ls;
			else
				*dest = _PLOHI2WORD(pwm) >> ls;

			dest += next;
			ddax += izoom;
			pixels--;
		}
	}
}

/**
 */
static void usb_GrayScalePseudo16( Plustek_Device *dev )
{
	u_char  *src;
	int      izoom, ddax, next;
	u_short *dest, g;
	u_long   pixels;
	ScanDef *scan = &dev->scanning;

	usb_AverageGrayByte( dev );

	if( scan->sParam.bSource == SOURCE_ADF ) {
		next = -1;
		dest = scan->UserBuf.pw + scan->sParam.Size.dwPixels - 1;
	} else {
		next = 1;
		dest = scan->UserBuf.pw;
	}

	src = scan->Green.pb;
	g   = (u_short)*src;

	izoom = usb_GetScaler( scan );
	ddax  = 0;

	for( pixels = scan->sParam.Size.dwPixels; pixels; src++ ) {

		ddax -= _SCALER;

		while((ddax < 0) && (pixels > 0)) {

			*dest = (g + *src) << bShift;
			dest += next;
			ddax += izoom;
			pixels--;
		}
		g = (u_short)*src;
	}
}

/** function to select the appropriate pixel copy function
 */
static void usb_GetImageProc( Plustek_Device *dev )
{
    ScanDef  *scan = &dev->scanning;
	DCapsDef *sc   = &dev->usbDev.Caps;
	HWDef    *hw   = &dev->usbDev.HwSetting;

	bShift = 0;

	if( scan->sParam.UserDpi.x != scan->sParam.PhyDpi.x ) {

		/* Pixel scaling... */
		switch( scan->sParam.bDataType ) {

			case SCANDATATYPE_Color:
				if (scan->sParam.bBitDepth > 8) {

					if( usb_IsCISDevice(dev)){
						scan->pfnProcess = usb_ColorScale16_2;
						DBG( _DBG_INFO, "ImageProc is: ColorScale16_2\n" );
					} else {
						scan->pfnProcess = usb_ColorScale16;
						DBG( _DBG_INFO, "ImageProc is: ColorScale16\n" );
					}
					if (scan->fGrayFromColor) {
						if( usb_IsCISDevice(dev)){
							scan->pfnProcess = usb_ColorScaleGray16_2;
							DBG( _DBG_INFO, "ImageProc is: ColorScaleGray16_2\n" );
						} else {
							scan->pfnProcess = usb_ColorScaleGray16;
							DBG( _DBG_INFO, "ImageProc is: ColorScaleGray16\n" );
						}
					}
				} else if (scan->dwFlag & SCANFLAG_Pseudo48) {
					scan->pfnProcess = usb_ColorScalePseudo16;
					DBG( _DBG_INFO, "ImageProc is: ColorScalePseudo16\n" );

				} else if (scan->fGrayFromColor) {

					if( usb_IsCISDevice(dev)){
						if (scan->fGrayFromColor > 7 ) {
							scan->pfnProcess = usb_BWScaleFromColor_2;
							DBG( _DBG_INFO, "ImageProc is: BWScaleFromColor_2\n" );
						} else {
							scan->pfnProcess = usb_ColorScaleGray_2;
							DBG( _DBG_INFO, "ImageProc is: ColorScaleGray_2\n" );
						}
					} else {
						if (scan->fGrayFromColor > 7 ) {
							scan->pfnProcess = usb_BWScaleFromColor;
							DBG( _DBG_INFO, "ImageProc is: BWScaleFromColor\n" );
						} else {
							scan->pfnProcess = usb_ColorScaleGray;
							DBG( _DBG_INFO, "ImageProc is: ColorScaleGray\n" );
						}
					}
				} else {

					if( usb_IsCISDevice(dev)){
						scan->pfnProcess = usb_ColorScale8_2;
						DBG( _DBG_INFO, "ImageProc is: ColorScale8_2\n" );
					} else {
						scan->pfnProcess = usb_ColorScale8;
						DBG( _DBG_INFO, "ImageProc is: ColorScale8\n" );
					}
				}
				break;

			case SCANDATATYPE_Gray:
				if (scan->sParam.bBitDepth > 8) {
					scan->pfnProcess = usb_GrayScale16;
					DBG( _DBG_INFO, "ImageProc is: GrayScale16\n" );
				} else {

					if (scan->dwFlag & SCANFLAG_Pseudo48) {
						scan->pfnProcess = usb_GrayScalePseudo16;
						DBG( _DBG_INFO, "ImageProc is: GrayScalePseudo16\n" );
					} else {
						scan->pfnProcess = usb_GrayScale8;
						DBG( _DBG_INFO, "ImageProc is: GrayScale8\n" );
					}
				}
				break;

			default:
				scan->pfnProcess = usb_BWScale;
				DBG( _DBG_INFO, "ImageProc is: BWScale\n" );
				break;
		}

	} else {

		/* Pixel copy */
		switch( scan->sParam.bDataType ) {

			case SCANDATATYPE_Color:
				if (scan->sParam.bBitDepth > 8) {
					if( usb_IsCISDevice(dev)){
						scan->pfnProcess = usb_ColorDuplicate16_2;
						DBG( _DBG_INFO, "ImageProc is: ColorDuplicate16_2\n" );
					} else {
						scan->pfnProcess = usb_ColorDuplicate16;
						DBG( _DBG_INFO, "ImageProc is: ColorDuplicate16\n" );
					}
					if (scan->fGrayFromColor) {
						if( usb_IsCISDevice(dev)){
							scan->pfnProcess = usb_ColorDuplicateGray16_2;
							DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray16_2\n" );
						} else {
							scan->pfnProcess = usb_ColorDuplicateGray16;
							DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray16\n" );
						}
					}
				} else if (scan->dwFlag & SCANFLAG_Pseudo48) {
					scan->pfnProcess = usb_ColorDuplicatePseudo16;
					DBG( _DBG_INFO, "ImageProc is: ColorDuplicatePseudo16\n" );
				} else if (scan->fGrayFromColor) {
					if( usb_IsCISDevice(dev)){
						if (scan->fGrayFromColor > 7 ) {
							scan->pfnProcess = usb_BWDuplicateFromColor_2;
							DBG( _DBG_INFO, "ImageProc is: BWDuplicateFromColor_2\n" );
						} else {
							scan->pfnProcess = usb_ColorDuplicateGray_2;
							DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray_2\n" );
						}
					} else {
						if (scan->fGrayFromColor > 7 ) {
							scan->pfnProcess = usb_BWDuplicateFromColor;
							DBG( _DBG_INFO, "ImageProc is: BWDuplicateFromColor\n" );
						} else {
							scan->pfnProcess = usb_ColorDuplicateGray;
							DBG( _DBG_INFO, "ImageProc is: ColorDuplicateGray\n" );
						}
					}
				} else {
					if( usb_IsCISDevice(dev)){
						scan->pfnProcess = usb_ColorDuplicate8_2;
						DBG( _DBG_INFO, "ImageProc is: ColorDuplicate8_2\n" );
					} else {
						scan->pfnProcess = usb_ColorDuplicate8;
						DBG( _DBG_INFO, "ImageProc is: ColorDuplicate8\n" );
					}
				}
				break;

			case SCANDATATYPE_Gray:
				if (scan->sParam.bBitDepth > 8) {
					scan->pfnProcess = usb_GrayDuplicate16;
					DBG( _DBG_INFO, "ImageProc is: GrayDuplicate16\n" );
				} else {
					if (scan->dwFlag & SCANFLAG_Pseudo48) {
						scan->pfnProcess = usb_GrayDuplicatePseudo16;
						DBG( _DBG_INFO, "ImageProc is: GrayDuplicatePseudo16\n" );
					} else {
						scan->pfnProcess = usb_GrayDuplicate8;
						DBG( _DBG_INFO, "ImageProc is: GrayDuplicate8\n" );
					}
				}
				break;

			default:
				scan->pfnProcess = usb_BWDuplicate;
				DBG( _DBG_INFO, "ImageProc is: BWDuplicate\n" );
				break;
		}
	}

	if( scan->sParam.bBitDepth == 8 ) {

		if( scan->dwFlag & SCANFLAG_Pseudo48 ) {
			if( scan->dwFlag & SCANFLAG_RightAlign ) {
				bShift = 5;
			} else {

				/* this should fix the Bearpaw/U12 discrepancy
				 * in general the fix is needed, but not for the U12
				 * why? - no idea!
				 */
				if(_WAF_BSHIFT7_BUG == (_WAF_BSHIFT7_BUG & sc->workaroundFlag))
					bShift = 0; /* Holger Bischof 16.12.2001 */
				else
					bShift = 7;
			}
			DBG( _DBG_INFO, "bShift adjusted: %u\n", bShift );
		}
	}

	if( _LM9833 == hw->chip ) {
		Shift = 0;
		Mask  = 0xFFFF;
	} else {
		Shift = 2;
		Mask  = 0xFFFC;
	}
}

/**
 * here we read the image data into our intermediate buffer (in the NT version
 * the function was implemented as thread)
 */
static SANE_Int usb_ReadData( Plustek_Device *dev )
{
	u_long   dw, dwRet, dwBytes, pl;
	ScanDef *scan = &dev->scanning;
	HWDef   *hw   = &dev->usbDev.HwSetting;

	DBG( _DBG_READ, "usb_ReadData()\n" );

	pl = dev->usbDev.a_bRegs[0x4e] * hw->wDRAMSize/128;

	while( scan->sParam.Size.dwTotalBytes ) {

		if( usb_IsEscPressed()) {
			DBG( _DBG_INFO, "usb_ReadData() - Cancel detected...\n" );
			return 0;
		}

		if( scan->sParam.Size.dwTotalBytes > scan->dwBytesScanBuf )
			dw = scan->dwBytesScanBuf;
		else
			dw = scan->sParam.Size.dwTotalBytes;

		scan->sParam.Size.dwTotalBytes -= dw;

		if(!scan->sParam.Size.dwTotalBytes && dw < (pl * 1024))
		{
			if(!(dev->usbDev.a_bRegs[0x4e] = (u_char)ceil((double)dw /
			                                         (4.0 * hw->wDRAMSize)))) {
				dev->usbDev.a_bRegs[0x4e] = 1;
			}
			dev->usbDev.a_bRegs[0x4f] = 0;

			sanei_lm983x_write( dev->fd, 0x4e, &dev->usbDev.a_bRegs[0x4e], 2, SANE_TRUE );
		}

		while( scan->bLinesToSkip ) {

			DBG( _DBG_READ, "Skipping %u lines\n", scan->bLinesToSkip );

			dwBytes = scan->bLinesToSkip * scan->sParam.Size.dwPhyBytes;

			if (dwBytes > scan->dwBytesScanBuf) {

				dwBytes = scan->dwBytesScanBuf;
				scan->bLinesToSkip -= scan->dwLinesScanBuf;
			} else {
				scan->bLinesToSkip = 0;
			}

			if( !usb_ScanReadImage( dev, scan->pbGetDataBuf, dwBytes ))
				return 0;
		}

		if( usb_ScanReadImage( dev, scan->pbGetDataBuf, dw )) {

			dumpPic("plustek-pic.raw", scan->pbGetDataBuf, dw, 0);

			if( scan->dwLinesDiscard ) {

				DBG(_DBG_READ, "Discarding %lu lines\n", scan->dwLinesDiscard);

				dwRet = dw / scan->sParam.Size.dwPhyBytes;

				if (scan->dwLinesDiscard > dwRet) {
					scan->dwLinesDiscard -= dwRet;
					dwRet = 0;
				} else	{
					dwRet -= scan->dwLinesDiscard;
					scan->dwLinesDiscard = 0;
				}
			} else {

				dwRet = dw / scan->sParam.Size.dwPhyBytes;
			}

			scan->pbGetDataBuf += scan->dwBytesScanBuf;
			if( scan->pbGetDataBuf >= scan->pbScanBufEnd ) {
				scan->pbGetDataBuf = scan->pbScanBufBegin;
			}

			if( dwRet )
				return dwRet;
		}
	}
	return 0;
}

/* END PLUSTEK-USBIMG.C .....................................................*/
