/***********************************************************************/
/*                  Scanner Driver ToolKit                             */
/*                      C Demo Program                                 */
/***********************************************************************/

#include <dos.h>
#include <conio.h>
#include <stdio.h>
#include <alloc.h>

#include "scandrv.h"		    /* scanner interface */

#define ESC     0x1B    /* Escape */

#define VIDEO_SERVICE           0x10 	/* BIOS Video Service interrupt */
#define VS_SET_MODE             0x00 	/* set video mode */
#define VS_SET_PALETTE_REGS     0x10 	/* set palette registers */

#define BufferSize	64000

#define COLOR_SCANNER   1

BYTE  lprgbColorRegs[256*3];
BYTE  lpShowImage[1680];
WORD  lpRealImage[1680];

/* Set VGA to correct graphic mode */
BOOL dspInitial()
{
    union REGS inregs, outregs;

    inregs.h.ah = VS_SET_MODE;
    inregs.h.al = 0x13;                     /* 320 * 200 256 color mode */
    int86( VIDEO_SERVICE, &inregs, &outregs);
}

/* Set VGA palettes */
void dspSetVGAPalette(LPBYTE lprgbPalette)
{
    union REGS inregs, outregs;
    struct SREGS segregs;

    inregs.h.ah = VS_SET_PALETTE_REGS;
    inregs.h.al = 0x12;             /* set block of color registers */
    inregs.x.bx = 0;                /* starting index */
    inregs.x.cx = 256;              /* number of color registers to set */
    inregs.x.dx = FP_OFF(lprgbPalette);      /* pointer to color table */
    segregs.es  = FP_SEG(lprgbPalette);      /* values in RGB format */
    int86x(VIDEO_SERVICE, &inregs, &outregs, &segregs );
}

/* Put a Horizontal Image Line to Screen */
void dspPutHorLine(short x1, short x2, short y, LPBYTE lpImage )
{
    static  LPBYTE video_address = (LPBYTE)0xa0000000L;
    LPBYTE  lpDst;

    lpDst = video_address + (long)y * 320 + (long)x1;
    memcpy( lpDst, lpImage, x2 - x1 + 1 );
}

/* Switch VGA back to Text Mode */
void dspExit(void)
{
    union REGS inregs, outregs;

    inregs.h.ah = VS_SET_MODE;
    inregs.h.al = 0x03;               /* Set 80 * 25 Text Mode */
    int86(VIDEO_SERVICE, &inregs, &outregs);
}

/* Black and White Scan Data Transfer to VGA Display Data */
MonoToVga( LPBYTE lpDstImage, LPBYTE lpSrcImage, short Count )
{
    short i;

    for ( i = 0; i < Count; i++ )
    {
        if (*lpSrcImage & 0x80)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x40)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x20)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x10)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x08)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x04)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x02)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        if (*lpSrcImage & 0x01)
            *lpDstImage++ = 0xf;
        else
            *lpDstImage++ = 0;
        lpSrcImage++;
    }
}

/* Color Dither Scan Data Transfer to VGA Display Data */
ColorDitherToVga( LPBYTE lpDstImage, LPBYTE lpSrcImage, short Count )
{
    short j, k, count;
    short red, green, blue, ColorNo;
    short r, g, b;
    static BYTE dropout[8] = {
                             0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
                             };

      j = count = 0;
      while (j <= Count)
      {
                r =j + 2;
                g =j + 1;
                b =j ;
                     for (k=0;k<8;k++)
                      {
                       red  = lpSrcImage[r];
                       red  = (red  & dropout[k]) >> (7-k);
                       green= lpSrcImage[g];
                       green= (green & dropout[k]) >> (7-k);
                       blue = lpSrcImage[b];
                       blue = (blue & dropout[k]) >> (7-k);
                       ColorNo = red * 4 + green * 2 + blue;
                       if (ColorNo == 0)
                            lpDstImage[count++] = 0;
                       else
                            lpDstImage[count++] = ColorNo + 8;
                      }
               j += 3;
      }
}

/* Color Gray Scan Data Transfer to VGA Display Data */
ColorGrayToVga( LPBYTE lpDstImage, LPBYTE lpSrcImage, short Count )
{
	BYTE code1[3],code2[3];
    short i, j;

      i = j = 0;
      while (j <= Count)
      {
          code1[0] = lpSrcImage[j] >> 4;
          code1[1] = lpSrcImage[j] & 0x0f;
          code1[2] = lpSrcImage[j+1] >> 4;
          code2[0] = lpSrcImage[j+1] & 0x0f;
          code2[1] = lpSrcImage[j+2] >> 4;
          code2[2] = lpSrcImage[j+2] & 0x0f;
   /* lpRealImage is real ( 0,0,0,0,r3,r2,r1,r0,g3,g2,g1,g0,b3,b2,b1,b0 ) */
          lpRealImage[i]=(WORD)code1[2] << 8 | code1[1] << 4 | code1[0];
          lpRealImage[i+1]=(WORD)code2[2] << 8 | code2[1] << 4 | code2[0];
   /* lpDstImage is ( r3,r2,r1,g3,g2,g1,b3,b2 ) */
          lpDstImage[i]= ((code1[2] >> 1) << 5) | ((code1[1] >> 1) << 2)
                             | (code1[0] >> 2);
          lpDstImage[i+1]= ((code2[2] >> 1) << 5) | ((code2[1] >> 1) << 2)
                             | (code2[0] >> 2);
          j += 3;
          i += 2;
      }
}

main()
{
    BUFLIST buflistScan[1];     /* buffer list (only one in our case) */
    SS ssScanBuf;               /* Scan Start buffer descriptor */
    SP  spApAnswer;             /* Scan Status buffer allocate by AP */
    LPSP lpspScanPos;           /* to monitor the scan process */
    GETPB getpbParameters;
    SETPB setpbParameters;

    LPSTR lpImage;
    WORD wTailLine = -1;
    WORD wTailBuffer = 0;
    short nScreenPos = 0;       /* position where to display the next lines
                                        on the screen */
    short i, j, r, g, b;
    short nMode, xRes, yRes;
    short nGamma, nBright, nHue;
    short scanner_type;

    printf("Which One do you install now? (1)Color (2)Gray Scanner : ");
    scanf("%d", &scanner_type);

    /* Check for the scanner driver */
    if ( !ScanOpen() )
    {
        printf("Cannot open the scanner driver\n");
        return (1);
    }

    /* Get the Scanner Parameter  */
    if ( !ScanGetParameters( &getpbParameters ) )
        printf("Cannot get the scanner parameter\n");

    printf("Current Mode is      : %d \n\n", getpbParameters.nMode );
    printf("Current Horizontal Resolution is :%u DPI\n",
                    getpbParameters.wHorizontalRes*25/10 );
    printf("Current Vertical Resolution is   :%u DPI\n\n",
                    getpbParameters.wVerticalRes*25/10 );
    printf("Interrupt Number is      : %d \n\n",
                    getpbParameters.nIRQ );
    printf("Max Width is      : %u Bytes\n\n",
                    getpbParameters.wMaxScanWidth );
    printf("Bit Per Pixel is      : %d bits\n\n",
                    getpbParameters.nBitsPerPixel );

    if( scanner_type == COLOR_SCANNER )
    {
        printf("Please Input Scan Mode (16)Mono (0)Color Dither (32)Color: ");
        scanf("%d", &nMode);
        printf("Please Input horizontal Resolution : ");
        scanf("%d", &xRes);
        printf("Please Input vertical Resolution   : ");
        scanf("%d", &yRes);
        printf("Please Input Gamma Correction (85)(101)(117)(133)(149)(165) : ");
        scanf("%d", &nGamma);
        printf("Please Input Brightness (127) : ");
        scanf("%d", &nBright);
        printf("Please Input Hue (128) : ");
        scanf("%d", &nHue);

        setpbParameters.nMode = nMode;
        setpbParameters.wHorizontalRes = xRes*10/25;
        setpbParameters.wVerticalRes = yRes*10/25;
        setpbParameters.nGammaCorrection = nGamma;
        setpbParameters.nBrightness = nBright;
        setpbParameters.nContrast = 0x80;
        setpbParameters.nHue = nHue;
        /* Set the Scanner Parameter  */
        if ( !ScanSetParameters( &setpbParameters ) )
            printf("Cannot set the scanner parameter\n");

        /* Get the Scanner Parameter  */
        if ( !ScanGetParameters( &getpbParameters ) )
            printf("Cannot get the scanner parameter\n");

        printf("Current Mode is      : %d \n\n", getpbParameters.nMode );
        printf("Current Horizontal Resolution is :%u DPI\n",
                        getpbParameters.wHorizontalRes*25/10 );
        printf("Current Vertical Resolution is   :%u DPI\n\n",
                        getpbParameters.wVerticalRes*25/10 );
        printf("Max Width is      : %u Bytes\n\n",
                        getpbParameters.wMaxScanWidth );
        printf("Bit Per Pixel is      : %d bits\n\n",
                        getpbParameters.nBitsPerPixel );

    }

    /* Allocate the scan buffer */
    if ((buflistScan[0].lpBuf = (LPSTR)farmalloc(BufferSize)) == NULL) {
        printf("Cannot allocate the scan buffer\n");
        return (1);
    }
    
    /* Set up the Scan buffer descriptor */
    buflistScan[0].wLinesOfBuffer 
                = BufferSize / getpbParameters.wMaxScanWidth;
    ssScanBuf.lpbuflistScan = (BUFLIST far *) buflistScan;
    ssScanBuf.wLineWidth = getpbParameters.wMaxScanWidth;
    ssScanBuf.wBufNumber = 1;
    ssScanBuf.wOptions = WRAPAROUND + AP_ANSWER;
    ssScanBuf.lpDisplayBuf = &wTailBuffer;
    ssScanBuf.lpDisplayLine = &wTailLine;

    lpspScanPos = &spApAnswer;          /* pass the address to driver */

    printf("Press any Key to start Scan.\n" );
    printf("After Scan Press ESC to stop Scan.\n" );
    getch();

    /* Clear the screen */
    dspInitial();
    if ( scanner_type != COLOR_SCANNER )
    {
        if ( getpbParameters.nMode != MONO_MODE )
        {
            for ( i = 0; i < 64; i++ )
            {
                for ( j = 0; j < 12; j++ )
                {
                    lprgbColorRegs[i*12+j] = i;
                }
            }
            dspSetVGAPalette( lprgbColorRegs );
        }
    }
    else if ( nMode == COLOR_GRAY_MODE )  /* Color Gray Mode */
    {
        i = 0;
        for ( r = 0; r < 8; r++ )
        {
            for ( g = 0; g < 8; g++ )
            {
                for ( b = 0; b < 4; b++ )
                {
                    lprgbColorRegs[i++] = r*8+4 ;
                    lprgbColorRegs[i++] = g*8+4 ;
                    lprgbColorRegs[i++] = b*16+8;
                }
            }
        }
        dspSetVGAPalette( lprgbColorRegs );
    }

    /* Run the scan process */
    if ( !ScanStart(&ssScanBuf, &lpspScanPos) )
        printf("Cannot start the scan operation\n");
    /* Depending on the interrupt mode of the scanner
       driver, ScanStart will either return (when IRQ!= -1) and allow
       us to display the scanned picture in realtime,
       or it will return only when the scan process is over(in "no interrupt
       mode", IRQ=-1), via a timeout or a keypress. 
       The return conditions can be checked with spScanPos->scanStatus) */

    while ( 1 )
    {
        if ( kbhit() )
        {
            if (getch() == ESC)
            {
                 break;
            }
        }
        if ( !(lpspScanPos->wScanStatus & SS_ACTIVE) )
                 break;
        if ( lpspScanPos->wCurrentLine != wTailLine )
        {
            if ( ++wTailLine == buflistScan[0].wLinesOfBuffer )
                  wTailLine = 0;
            lpImage = buflistScan[0].lpBuf 
                      + (long)getpbParameters.wMaxScanWidth*(long)wTailLine;

            if( scanner_type == COLOR_SCANNER )
            {
                if (nMode == COLOR_MONO_MODE )
                    MonoToVga( lpShowImage, (LPBYTE)lpImage, 
                                    getpbParameters.wMaxScanWidth );
                else if ( nMode == COLOR_GRAY_MODE )
                    ColorGrayToVga( lpShowImage, (LPBYTE)lpImage, 
                                        getpbParameters.wMaxScanWidth );
                else
                    ColorDitherToVga( lpShowImage, (LPBYTE)lpImage, 
                                        getpbParameters.wMaxScanWidth );
                dspPutHorLine(0, 319, nScreenPos%200, (LPBYTE)lpShowImage );
            }
            else
            {
                if ( getpbParameters.nMode == MONO_MODE )
                {
                    MonoToVga( lpShowImage, (LPBYTE)lpImage, 
                                    getpbParameters.wMaxScanWidth );
                    dspPutHorLine(0, 319, nScreenPos%200,
                                             (LPBYTE)lpShowImage );
                }
                else
                    dspPutHorLine(0, 319, nScreenPos%200, (LPBYTE)lpImage );
            }

            nScreenPos++;
        }
    }

    /* Stop the scan process */ 
    if ( !ScanStop() )
        printf("Cannot stop the scanner \n");
    
    /* close the scanner driver */
    if ( !ScanClose() )
        printf("Cannot close the scanner \n");

    farfree( buflistScan[0].lpBuf );

    dspExit();

}
