/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   Module          = CONVEXT                                        */
/*                                                                    */
/*   Description     = Display Device Driver subroutines to convert   */
/*                     between the PM bitmap formats and the          */
/*                     device driver internal formats                 */
/*                                                                    */
/*   Function        = ConvertExtToInt                                */
/*                     TableExtToInt                                  */
/*                                                                    */
/*                                                                    */
/*   77638 - JOHNB     Added support for 16 bit external bitmaps.     */
/*                     These will only be used when we are using      */
/*                     16 bit internal bitmaps ( we are in 64K        */
/*                     color mode )                                   */
/*                                                                    */
/**********************************************************************/
#define INCL_GRE_PALETTE
#define INCL_DDIMISC
#define INCL_WINSYS
#include "eddinclt.h"

#include "eddbcone.h"
#include "eddccone.h"

#include "eddbtypt.h"
#include "edddtypt.h"

#include "eddcextf.h"
#include "eddgextf.h"

#include "eddhcone.h"
#include "eddhtype.h"
#include "eddhmacr.h"

#include "bitmaps.h"
#include "convfuns.h"
#include "twozero.h"

extern PRGB2            MemoryDeviceDefaultPalette;
extern USHORT           SizeOfHWPalette;
extern DDTType          DDT;
extern BOOL             fRealizeSupported;

/**********************************************************************/
/* Function prototypes for assembly version of conversion routines.   */
/* Assembly routines in convert.asm                                   */
/**********************************************************************/
VOID DRIVERCALL ExtToIntConversionSetUp( PBITMAPINFO2  pSourceHeader );
VOID DRIVERCALL ConvertExt8ToInt8( VOID );
VOID DRIVERCALL ConvertExt8ToInt16( VOID );
VOID DRIVERCALL ConvertExt24ToInt8( VOID );
VOID DRIVERCALL ConvertExt24ToInt16( VOID );
VOID DRIVERCALL ConvertExt24ToInt16Matrox( VOID );

/**********************************************************************/
/* EncodedPalConversionRequired                                       */
/*                                                                    */
/* Returns TRUE if copying the external (source) bitmap to the        */
/* internal (target) bitmap requires a colour conversion, FALSE if    */
/* not. Called for bitmaps with the same bitcount and the source      */
/* is a palette encoded bitmap.                                       */
/* Provided that the mapping is the identity then no conversion needs */
/* to be done.  The mapping only needs to be the identity for as many */
/* indices as are actually used in the bitmap.                        */
/*                                                                    */
/* Palette encoding can only occur with the BITMAPINFO2 header, so    */
/* we know that we must have RGB2 values in the external colour table */
/* so we point at the external table with a PULONG.                   */
/**********************************************************************/
BOOL DRIVERCALL EncodedPalConversionRequired(VOID)
{
    PULONG  pExtRGB;
    ULONG   i;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PULONG)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    /******************************************************************/
    /* Check each entry in the external color table.                  */
    /* We are looking for a perfect identity mapping to prevent us    */
    /* having to do any conversion.                                   */
    /******************************************************************/
    for (i=0; i < SPad.usExtTabSize; i++)
    {
        if ( pExtRGB[i] != i )
        {
            /**********************************************************/
            /* The external color table is not 'perfect' so return    */
            /* that conversion is required.                           */
            /**********************************************************/
            return(TRUE);
        }
    }

    /******************************************************************/
    /* We successfully checked the whole table so no conversion is    */
    /* required.                                                      */
    /******************************************************************/
    return(FALSE);

} /* EncodedPalConversionRequired */


/**********************************************************************/
/* NonEncodedPalConversionRequired                                    */
/*                                                                    */
/* Returns TRUE if the copying the external (source) bitmap to the    */
/* internal (target) bitmap requires a colour conversion, FALSE if    */
/* not. Called for bitmaps with the same bitcount, the colour format  */
/* is LCOLF_PALETTE and and the source is not a palette encoded       */
/* bitmap.                                                            */
/**********************************************************************/
BOOL DRIVERCALL NonEncodedPalConversionRequired(VOID)
{
    PRGB    pExtRGB;
    ULONG   i;
    ULONG   ulCheckSize;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    /******************************************************************/
    /* Compare the external color table with the internal palette.    */
    /* Only compare as many (as few!) values as we need to.           */
    /******************************************************************/
    ulCheckSize = min(pdc->Palette->usCountStored, SPad.usExtTabSize);

    for (i = 0;
         i < ulCheckSize;
         i++, pExtRGB = (PRGB)((PBYTE)pExtRGB + SPad.usIncrement))
    {
        if (pExtRGB->bBlue  != pdc->Palette->entries[i].rgb.bBlue  ||
            pExtRGB->bGreen != pdc->Palette->entries[i].rgb.bGreen ||
            pExtRGB->bRed   != pdc->Palette->entries[i].rgb.bRed  )
        {
            /**********************************************************/
            /* External and internal colours differ so conversion is  */
            /* needed.                                                */
            /**********************************************************/
            return(TRUE);
        }
    }

    /**********************************************************/
    /* check for black filling in the remaining ext entries   */
    /**********************************************************/
    while (i < SPad.usExtTabSize)
    {
        if (pExtRGB->bBlue  != 0x00 ||
            pExtRGB->bGreen != 0x00 ||
            pExtRGB->bRed   != 0x00 )
        {
            /**********************************************************/
            /* Padding colour is not black so this is a dubious       */
            /* external table which we would do best to convert.      */
            /**********************************************************/
            return(TRUE);
        }

        /**************************************************************/
        /* Move to the next external RGB                              */
        /**************************************************************/
        i++;
        pExtRGB = (PRGB)((PBYTE)pExtRGB + SPad.usIncrement);
    }

    /******************************************************************/
    /* We successfully checked the whole table so no conversion is    */
    /* required.                                                      */
    /******************************************************************/
    return(FALSE);

} /* NonEncodedPalConversionRequired */


/**********************************************************************/
/* PaletteConversionRequired                                          */
/*                                                                    */
/* Returns TRUE if the copying the external (source) bitmap to the    */
/* internal (target) bitmap requires a colour conversion, FALSE if    */
/* not. Called for bitmaps with the same bitcount when the colour     */
/* format is LCOLF_PALETTE.                                           */
/**********************************************************************/
BOOL DRIVERCALL PaletteConversionRequired(VOID)
{
    if ( SPad.fbFlags & CONV_BCE_PALETTE )
    {
        return(EncodedPalConversionRequired());
    }
    else
    {
        return(NonEncodedPalConversionRequired());
    }
} /* PaletteConversionRequired */


/**********************************************************************/
/* MonoBMConversionRequired                                           */
/*                                                                    */
/* Returns TRUE if the source and target (which are both 1 bpp)       */
/* require conversion, FALSE if no conversion needed.                 */
/* Our internal format is such that a zero will be drawn in the       */
/* current background color and a one will be drawn in the current    */
/* foreground color. If the external bitmap color table tells us that */
/* a zero represents black (the default display background color)     */
/* then no conversion needs to be done.                               */
/* If the color table has a non black first entry then we need to     */
/* perform a conversion which inverts every bit in the bitmap.        */
/**********************************************************************/
BOOL DRIVERCALL MonoBMConversionRequired(VOID)
{
    PRGB    pExtRGB;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    /******************************************************************/
    /* Look at the first external entry                               */
    /******************************************************************/
    if (pExtRGB->bBlue  != 0x00 ||
        pExtRGB->bGreen != 0x00 ||
        pExtRGB->bRed   != 0x00 )
    {
        /**************************************************************/
        /* The entry is not black so conversion is required.          */
        /**************************************************************/
        return(TRUE);
    }

    /**************************************************************/
    /* We assume the entries are black followed by white, so      */
    /* we say dont convert.                                       */
    /* This is not how the VGA/8514 etc work!!!                   */
    /**************************************************************/
#pragma message( __FILE__"(236) : Warning: algorithm differs from VGA/8514")
    return(FALSE);
} /* MonoBMConversionRequired */


/**********************************************************************/
/* RealizableCTConversionRequired                                     */
/*                                                                    */
/* Returns TRUE if the source and target (which have the same bpp)    */
/* require conversion, FALSE if no conversion needed.                 */
/* We are in Index mode and there is a realizable LCT so we need to   */
/* compare the supplied color table with the merged LCT and default   */
/* physical palette.                                                  */
/*                                                                    */
/* Note that the color table comparison is a little awkward because   */
/* the external table may be packed (i.e. all of the 24-bit RGB       */
/* values are stored consecutively whereas internally RGB values are  */
/* padded to 32-bit boundaries.                                       */
/*                                                                    */
/* If we find we have a new format bitmap, then both have RGB values  */
/* stored in ULONGS so we could simplify the comparison in this case. */
/**********************************************************************/
BOOL DRIVERCALL RealizableCTConversionRequired(VOID)
{
    PRGB    pExtRGB;
    RGB2    DefaultRGB;
    ULONG   i;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    /******************************************************************/
    /* Now loop through all the colours in the external color table   */
    /******************************************************************/
    for (i = 0;
         (i < SPad.usExtTabSize) && (i < SizeOfHWPalette);
         i++, pExtRGB = (PRGB)((PBYTE)pExtRGB + SPad.usIncrement))
    {
        if ( (i <= pdc->DCIHighIndex) &&
             (pdc->DCIColorTable[i].PhyIndex != CLR_NOPHYINDEX) )
        {
            /**************************************************/
            /* This is a valid entry in the LCT so use its    */
            /* logical RGB value.                             */
            /**************************************************/
            if (pExtRGB->bBlue  != pdc->DCIColorTable[i].LogRGB.bBlue  ||
                pExtRGB->bGreen != pdc->DCIColorTable[i].LogRGB.bGreen ||
                pExtRGB->bRed   != pdc->DCIColorTable[i].LogRGB.bRed)
            {
                /**********************************************************/
                /* External and internal colours differ so conversion is  */
                /* needed.                                                */
                /**********************************************************/
                return(TRUE);
            }
        }
        else
        {
            /**************************************************/
            /* Use the RGB value in the default physical      */
            /* palette.                                       */
            /**************************************************/
            DefaultRGB = MemoryDeviceDefaultRGB2(i);
            if (pExtRGB->bBlue  != DefaultRGB.bBlue       ||
                pExtRGB->bGreen != DefaultRGB.bGreen      ||
                pExtRGB->bRed   != DefaultRGB.bRed)
            {
                /**********************************************************/
                /* External and internal colours differ so conversion is  */
                /* needed.                                                */
                /**********************************************************/
                return(TRUE);
            }
        }
    }

    /******************************************************************/
    /* We successfully checked the whole table so no conversion is    */
    /* required.                                                      */
    /******************************************************************/
    return(FALSE);

} /* RealizableCTConversionRequired */


/**********************************************************************/
/* PhysicalPalConversionRequired                                      */
/*                                                                    */
/* Returns TRUE if the source and target (which have the same bpp)    */
/* require conversion, FALSE if no conversion needed.                 */
/* We are in RGB mode, or Index mode with an unrealizable LCT. We     */
/* therefore need to compare the supplied color table with the        */
/* default physical palette.                                          */
/*                                                                    */
/* Note that the color table comparison is a little awkward because   */
/* the external table may be packed (i.e. all of the 24-bit RGB       */
/* values are stored consecutively whereas internally RGB values are  */
/* padded to 32-bit boundaries.                                       */
/*                                                                    */
/* If we find we have a new format bitmap, then both have RGB values  */
/* stored in ULONGS so we could simplify the comparison in this case. */
/**********************************************************************/
BOOL DRIVERCALL PhysicalPalConversionRequired(VOID)
{
    PRGB    pExtRGB;
    RGB2    DefaultRGB;
    ULONG   i;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    for (i = 0;
         (i < SPad.usExtTabSize) && (i < SizeOfHWPalette);
         i++, pExtRGB = (PRGB)((PBYTE)pExtRGB + SPad.usIncrement))
    {
        DefaultRGB = MemoryDeviceDefaultRGB2(i);
        if (pExtRGB->bBlue  != DefaultRGB.bBlue       ||
            pExtRGB->bGreen != DefaultRGB.bGreen      ||
            pExtRGB->bRed   != DefaultRGB.bRed)
        {
            /**********************************************************/
            /* External and internal colours differ so conversion is  */
            /* needed.                                                */
            /**********************************************************/
            return(TRUE);
        }
    }

    /******************************************************************/
    /* We successfully checked the whole table so no conversion is    */
    /* required.                                                      */
    /******************************************************************/
    return(FALSE);

} /* PhysicalPalConversionRequired */


/**********************************************************************/
/* SameBitCountConversionRequired                                     */
/*                                                                    */
/* Returns TRUE if the source and target (which have the same         */
/* bitcount) require conversion, FALSE if no conversion needed        */
/* We can only avoid conversion if the external color table matches   */
/* the physical color table that is used by the current DC. This      */
/* physical color table will be                                       */
/*                                                                    */
/*  a) The default physical palette if the mode is RGB or             */
/*     the current mode is Index and the LCT is unrealizable.         */
/* OR...                                                              */
/*                                                                    */
/*  b) The current LCT merged with the default physical               */
/*     palette if the mode is Index and the LCT is realizable         */
/*                                                                    */
/* OR...                                                              */
/*  c) The logical palette in the DC under palette manager            */
/**********************************************************************/
BOOL DRIVERCALL SameBitCountConversionRequired(VOID)
{
    if (pdc->DCIColFormat == LCOLF_PALETTE)
    {
        return(PaletteConversionRequired());
    }
    else if (SPad.cExternalBitCount == 1)
    {
        return(MonoBMConversionRequired());
    }
    else if (fRealizeSupported &&
             (pdc->DCIColStatus & LCOL_REALIZABLE))
    {
        return(RealizableCTConversionRequired());
    }
    else
    {
        return(PhysicalPalConversionRequired());
    }
} /* SameBitCountConversionRequired */


/**********************************************************************/
/* ConversionRequired                                                 */
/*                                                                    */
/* Returns TRUE if colour conversion is required, FALSE if not.       */
/**********************************************************************/
BOOL DRIVERCALL ConversionRequired(VOID)
{
    #ifdef BPP24
    if ( SPad.cInternalBitCount == 24 ) {
        return(TRUE);
    }
    #endif

    if (SPad.cExternalBitCount == SPad.cInternalBitCount )
    {
        return(SameBitCountConversionRequired());
    }
    else /* source and target bitcounts differ */
    {
        // @DMS this statement is no longer true since we have 24bpp
        /***************************************************************/
        /* 24 and 16 bpp will fall through to here since target cannot */
        /* ever be 24 bpp, source cannot ever be 16 bpp.               */
        /***************************************************************/
        return(TRUE);
    }
} /* ConversionRequired */


/**********************************************************************/
/* DirectMapping                                                      */
/*                                                                    */
/* Always returns its input value - used so that all conversions can  */
/* be handled via the same controlling routine which will generally   */
/* need to call a subroutine to perform the mapping.                  */
/*                                                                    */
/* DirectMapping is used at 8bpp with a palette, in which case the    */
/* actual index value is held in a single byte (the blue component of */
/* the RGB2 value passed).                                            */
/**********************************************************************/
ULONG DRIVERCALL  DirectMapping(RGB2 Input)
{
    return( (ULONG)Input.bBlue );
}

/**********************************************************************/
/* NearestPaletteIndexForScreen                                       */
/*                                                                    */
/* Does a NearestPaletteIndex call, then uses its return value to     */
/* to find what h/w value to use on the screen.                       */
/**********************************************************************/
ULONG DRIVERCALL  NearestPaletteIndexForScreen(RGB2 Input)
{
    ULONG   npIndex;

    npIndex = NearestPaletteIndex(Input);
    return(pdc->Palette->entries[npIndex].bCurrent);
}

/**********************************************************************/
/* RGBToIndexFunction                                                 */
/*                                                                    */
/* Returns a pointer to the function to use to convert a 24-bit RGB   */
/* to an index for the inner conversion loop later.                   */
/**********************************************************************/
PFNUS DRIVERCALL RGBToIndexFunction(VOID)
{
    if (pdc->DCIColFormat == LCOLF_PALETTE)
    {
        if ( SPad.fbFlags & CONV_BCE_PALETTE )
        {
            return( DirectMapping );
        }
        else if (pdc->DCIDCType == OD_DIRECT)
        {
            /******************************************************/
            /* The target is the screen - this occurs if we are   */
            /* calling this routine as a result of DrawBits.      */
            /* Thus we need a h/w index, rather than just a       */
            /* NearestPalatteIndex                                */
            /******************************************************/
            return(NearestPaletteIndexForScreen);
        }
        else
        {
            return( NearestPaletteIndex );
        }
    }

    if (fRealizeSupported &
        (pdc->DCIColStatus & LCOL_REALIZABLE))
    {
        return( NearestRealizableIndex );
    }

    return( NearestDefaultPhysicalIndex );
} /* RGBToIndexFunction */


/**********************************************************************/
/* ConvertExt1ToInt1                                                  */
/*                                                                    */
/* Converts a single scan line from external to internal 1bpp forms.  */
/* Since we only consider 2 possibilities for 1bpp (either straight   */
/* copy in which case we use FlipTheBitmap, or bitwise inversion)     */
/* then this routine simply does bitwise inversion, without using a   */
/* conversion table.                                                  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt1ToInt1(VOID)
{
    ULONG  j;

    /******************************************************************/
    /* Handle as many dwords (sets of 32 pels) as we can              */
    /******************************************************************/
    for (j=SPad.cx / 32; j--; )
    {
        /**************************************************************/
        /* Bitwise invert this dword                                  */
        /**************************************************************/
        *((PULONG)SPad.pbTrgPointer)++ = ~(*((PULONG)SPad.pbSrcPointer)++);
    }
    /******************************************************************/
    /* Now do any remaining bytes (sets of 8 pels)                    */
    /* (round up the number of bytes inverted, since it does not      */
    /* matter if we flip some data in the padding at the end of the   */
    /* scanline)                                                      */
    /******************************************************************/
    for (j=((SPad.cx % 32) + 7) / 8; j--; )
    {
        /**************************************************************/
        /* Bitwise invert this byte                                   */
        /**************************************************************/
        *SPad.pbTrgPointer++ = (BYTE)~(*SPad.pbSrcPointer++);
    }
} /* ConvertExt1ToInt1 */


/**********************************************************************/
/* ConvertExt1ToInt8                                                  */
/*                                                                    */
/* Converts a single scan line from external 1bpp to internal 8bpp.   */
/**********************************************************************/
VOID DRIVERCALL ConvertExt1ToInt8(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;
    ULONG   ulExtraBits;

    /**************************************************************/
    /* each source byte of eight pixels maps to 8 target bytes    */
    /**************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
        bSrcByte = *SPad.pbSrcPointer++;

        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x80)>>7];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x40)>>6];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x20)>>5];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x10)>>4];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x08)>>3];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x04)>>2];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x02)>>1];
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[bSrcByte & 0x01];

    } /* for each byte on the row */

    /******************************************************************/
    /* finish off any incomplete bytes at the end of the row          */
    /* SrcPointer already points to the correct source byte           */
    /******************************************************************/
    ulExtraBits = SPad.cx & 7;
    if ( ulExtraBits )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        while (ulExtraBits--)
        {
            *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0x80)>>7];
            bSrcByte <<= 1;
        }
    }
} /* ConvertExt1ToInt8 */


/**********************************************************************/
/* ConvertExt4ToInt4                                                  */
/*                                                                    */
/* Converts a single scan line from external 4bpp to internal 4bpp.   */
/*                                                                    */
/* NB. if optimization of setting up a byte to byte conversion table  */
/*     rather than a pel to pel table is taken, then this routine     */
/*     becomes redundant (use ConvertExt8ToInt8).                     */
/**********************************************************************/
#pragma message( __FILE__"(592) : Optimization: could convert pel/pel table to byte/byte")
VOID DRIVERCALL ConvertExt4ToInt4(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /******************************************************************/
    /* each source and target byte contains 2 pixels                  */
    /******************************************************************/
    for ( j = (SPad.cx + 1) / 2; j--; )
    {
        /**************************************************************/
        /* get the index of each pixel in the source byte and write   */
        /* to the target                                              */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;
        *SPad.pbTrgPointer++ = (BYTE)
                        ((SPad.ausConvertTable[(bSrcByte & 0xF0)>>4] << 4) |
                          SPad.ausConvertTable[(bSrcByte & 0x0F)]);
    } /* for each byte on the row */
    /******************************************************************/
    /*                                                                */
    /******************************************************************/
} /* ConvertExt4ToInt4 */


/**********************************************************************/
/* ConvertExt4ToInt8                                                  */
/*                                                                    */
/* Converts a single scan line from external 4bpp to internal 8bpp.   */
/**********************************************************************/
VOID DRIVERCALL ConvertExt4ToInt8(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /******************************************************************/
    /* for each source byte which is to be converted - two pixels per */
    /* byte                                                           */
    /******************************************************************/
    for ( j = SPad.cx / 2; j--; )
    {
        /**************************************************************/
        /* Get the next byte of data out of the source.               */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;

        /**************************************************************/
        /* now update the target - each source index (4 bits) expands */
        /* to 8 bits in the target                                    */
        /**************************************************************/
        /* get the first pixel out the source and write to the target */
        /**************************************************************/
        *SPad.pbTrgPointer++ =
                       (BYTE)SPad.ausConvertTable[(bSrcByte & 0xf0)>>4];

        /**************************************************************/
        /* get the second pixel out the source and write to the       */
        /* target                                                     */
        /**************************************************************/
        *SPad.pbTrgPointer++ =
                          (BYTE)SPad.ausConvertTable[(bSrcByte & 0x0f)];

    } /* for each pel on the row */

    /******************************************************************/
    /* check for an odd pixel at the end of the source row            */
    /******************************************************************/
    if ( SPad.cx & 1 )
    {
        *SPad.pbTrgPointer =
           (BYTE)SPad.ausConvertTable[(*SPad.pbSrcPointer++ & 0xf0)>>4];
    }
} /* ConvertExt4ToInt8 */


/**********************************************************************/
/* ConvertExt8ToInt4                                                  */
/*                                                                    */
/* Converts a single scan line from external 8bpp to internal 4bpp.   */
/**********************************************************************/
VOID DRIVERCALL ConvertExt8ToInt4(VOID)
{
    ULONG   j;
    BYTE    bTrgIndex;

   /**************************************************************/
   /* two source bytes map to each target byte                   */
   /**************************************************************/
   for ( j = SPad.cx / 2; j--; )
   {
       /**********************************************************/
       /* get the target index corresponding to the first source */
       /* byte, shift left by 4 and OR in the target index       */
       /* corresponding to the second source byte                */
       /**********************************************************/
       bTrgIndex =
                (BYTE)(SPad.ausConvertTable[*SPad.pbSrcPointer++] << 4);
       *SPad.pbTrgPointer++ = (BYTE)
               (bTrgIndex | SPad.ausConvertTable[*SPad.pbSrcPointer++]);

   } /* for each pel on the row */

   /**************************************************************/
   /* if there's an odd byte at the end of the row, handle it now*/
   /**************************************************************/
   if ( SPad.cx & 1 )
   {
       *SPad.pbTrgPointer = (BYTE)
                     (SPad.ausConvertTable[*SPad.pbSrcPointer++] << 4);
   }

} /* ConvertExt8ToInt4 */


/**********************************************************************/
/* ConvertExt8ToInt8NoConversion                                      */
/*                                                                    */
/* Copys a single scan line of external 8bpp to internal 8bpp when    */
/* no colour conversion is required.                                  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt8ToInt8NoConversion(VOID)
{
    memcpy(SPad.pbTrgPointer, SPad.pbSrcPointer, SPad.cx);

    SPad.pbTrgPointer += SPad.cx;
    SPad.pbSrcPointer += SPad.cx;
} /* ConvertExt8ToInt8 */

/**********************************************************************/
/* ConvertExt24ToInt1                                                 */
/*                                                                    */
/* Converts a single scan line from external 24bpp to internal 1bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt24ToInt1(VOID)
{
    ULONG   j;
    ULONG   ulExtraBits;
    BYTE    bTrgIndex;
    BYTE    bMask;

    /**************************************************************/
    /* process 8 pixels at a time, ie. 1 target byte / 24 source  */
    /* bytes                                                      */
    /**************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
        /******************************************************/
        /* require three source bytes for each bit in the     */
        /* target byte. Get the source RGB, convert to a 0 or */
        /* 1 and shift into the target                        */
        /*    - first byte is BLUE                            */
        /*    - second byte is GREEN                          */
        /*    - third byte is RED                             */
        /******************************************************/

        /**************************************************************/
        /* These macros make the next bit of code far easier to read  */
        /**************************************************************/
/* Start of macros */
#define RGBSum(p) ((ULONG)*p++ + (ULONG)*p++ + (ULONG)*p++)
#define NextPixel(mask)                                               \
    if ( RGBSum(SPad.pbSrcPointer) > WHITE_THRESHOLD )                \
    {                                                                 \
        bTrgIndex |= mask;                                            \
    }
/* End of macros */

        /**************************************************************/
        /* First pixel                                                */
        /**************************************************************/
        if ( RGBSum(SPad.pbSrcPointer) > WHITE_THRESHOLD )
        {
            bTrgIndex = 0x80;
        }
        else
        {
            bTrgIndex = 0;
        }

        NextPixel(0x40);        /* Second pixel  */
        NextPixel(0x20);        /* Third pixel   */
        NextPixel(0x10);        /* Fourth pixel  */
        NextPixel(0x08);        /* Fifth pixel   */
        NextPixel(0x04);        /* Sixth pixel   */
        NextPixel(0x02);        /* Seventh pixel */

        /******************************************************/
        /* eighth pixel                                       */
        /******************************************************/
        if ( RGBSum(SPad.pbSrcPointer) > WHITE_THRESHOLD )
        {
            *SPad.pbTrgPointer++ = (BYTE)(bTrgIndex | 0x01);
        }
        else
        {
            *SPad.pbTrgPointer++ = bTrgIndex;
        }
    } /* for each pel on the row */

    /******************************************************************/
    /* if there are extra bits at the end of the row, handle them now */
    /******************************************************************/
    ulExtraBits = SPad.cx & 7;
    if ( ulExtraBits )
    {
        bTrgIndex = 0;
        bMask = 0x80;
        while (ulExtraBits--)
        {
            if ( RGBSum(SPad.pbSrcPointer) > WHITE_THRESHOLD )
            {
                bTrgIndex |= bMask;
            }

            bMask >>= 1;
        }
        *SPad.pbTrgPointer = bTrgIndex;
    }
} /* ConvertExt24ToInt1 */

/**********************************************************************/
/* ConvertExt16ToInt1             @77638 added new function           */
/*                                                                    */
/* Converts a single scan line from external 16bpp to internal 1bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt16ToInt1(VOID)
{
    ULONG   j;
    ULONG   ulExtraBits;
    BYTE    bTrgIndex;
    BYTE    bMask;

    /**************************************************************/
    /* process 8 pixels at a time, ie. 1 target byte / 16 source  */
    /* bytes                                                      */
    /**************************************************************/
    for ( j = SPad.cx / 8; j--; )
    {
        /******************************************************/
        /* require two source bytes for each bit in the       */
        /* target byte. Get the source RGB, convert to a 0 or */
        /* 1 and shift into the target                        */
        /*    - first byte is BLUE                            */
        /*    - second byte is GREEN                          */
        /*    - third byte is RED                             */
        /******************************************************/

        /**************************************************************/
        /* These macros make the next bit of code far easier to read  */
        /**************************************************************/
/* Start of macros */
#define Next16bppPixel(mask)                                          \
    if ( ULONGFromRGB16(*((PUSHORT)(SPad.pbSrcPointer))) > WHITE_THRESHOLD )        \
    {                                                                 \
        bTrgIndex |= mask;                                            \
    }                                                                 \
    SPad.pbSrcPointer+=2;

/* End of macros */

        /**************************************************************/
        /* First pixel                                                */
        /**************************************************************/
        if ( ULONGFromRGB16( *((PUSHORT)(SPad.pbSrcPointer)) ) > WHITE_THRESHOLD )
        {
            bTrgIndex = 0x80;
        }
        else
        {
            bTrgIndex = 0;
        }
        SPad.pbSrcPointer+=2;

        Next16bppPixel(0x40);        /* Second pixel  */
        Next16bppPixel(0x20);        /* Third pixel   */
        Next16bppPixel(0x10);        /* Fourth pixel  */
        Next16bppPixel(0x08);        /* Fifth pixel   */
        Next16bppPixel(0x04);        /* Sixth pixel   */
        Next16bppPixel(0x02);        /* Seventh pixel */

        /******************************************************/
        /* eighth pixel                                       */
        /******************************************************/
        if ( ULONGFromRGB16(*((PUSHORT)(SPad.pbSrcPointer))) > WHITE_THRESHOLD )
        {
            *SPad.pbTrgPointer++ = (BYTE)(bTrgIndex | 0x01);
        }
        else
        {
            *SPad.pbTrgPointer++ = bTrgIndex;
        }
        SPad.pbSrcPointer+=2;

    } /* for each pel on the row */

    /******************************************************************/
    /* if there are extra bits at the end of the row, handle them now */
    /******************************************************************/
    ulExtraBits = SPad.cx & 7;
    if ( ulExtraBits )
    {
        bTrgIndex = 0;
        bMask = 0x80;
        while (ulExtraBits--)
        {
            if ( ULONGFromRGB16(*((PUSHORT)(SPad.pbSrcPointer))) > WHITE_THRESHOLD )
            {
                bTrgIndex |= bMask;
            }
            SPad.pbSrcPointer+=2;

            bMask >>= 1;
        }
        *SPad.pbTrgPointer = bTrgIndex;
    }
} /* ConvertExt16ToInt1 */


/**********************************************************************/
/* ConvertExt24ToInt4                                                 */
/*                                                                    */
/* Converts a single scan line from external 24bpp to internal 4bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt24ToInt4(VOID)
{
    ULONG   j;
    ULONG   ulTrgIndex;
    BYTE    bTrgByte;
    RGB2    RGBColour;

    /******************************************************************/
    /* We are not interested in the options byte of the RGB2 we use.  */
    /******************************************************************/
    RGBColour.fcOptions = 0;

    /**************************************************************/
    /* process 2 pixels at a time, ie. 1 target byte / 6 source   */
    /* bytes                                                      */
    /**************************************************************/
    for ( j = SPad.cx / 2; j--; )
    {
        /**********************************************************/
        /* require three source bytes for each 4 bits in the      */
        /* target byte. Get the source RGB, convert to a 4        */
        /* bit index and make the target byte                     */
        /*    - first byte is BLUE                                */
        /*    - second byte is GREEN                              */
        /*    - third byte is RED                                 */
        /**********************************************************/
        RGBColour.bBlue  = *SPad.pbSrcPointer++;
        RGBColour.bGreen = *SPad.pbSrcPointer++;
        RGBColour.bRed   = *SPad.pbSrcPointer++;

        ulTrgIndex = SPad.pfnRGBToIndex(RGBColour);

        bTrgByte = (BYTE)(ulTrgIndex << 4);

        /******************************************************/
        /* second pixel                                       */
        /******************************************************/
        RGBColour.bBlue  = *SPad.pbSrcPointer++;
        RGBColour.bGreen = *SPad.pbSrcPointer++;
        RGBColour.bRed   = *SPad.pbSrcPointer++;

        ulTrgIndex = SPad.pfnRGBToIndex(RGBColour);

        *SPad.pbTrgPointer++ = (bTrgByte | (BYTE)ulTrgIndex);
    } /* for each pel on the row */

    /******************************************************************/
    /* if there is an odd byte at the end of the row, handle it now   */
    /******************************************************************/
    if ( SPad.cx & 1 )
    {
        RGBColour.bBlue  = *SPad.pbSrcPointer++;
        RGBColour.bGreen = *SPad.pbSrcPointer++;
        RGBColour.bRed   = *SPad.pbSrcPointer++;

        ulTrgIndex = SPad.pfnRGBToIndex(RGBColour);

        *SPad.pbTrgPointer = (BYTE)(ulTrgIndex << 4);
    }
} /* ConvertExt24ToInt4 */


/**********************************************************************/
/* ConvertExt1ToInt16                                                 */
/*                                                                    */
/* Converts a single scan line from external 1bpp to internal 16bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt1ToInt16(VOID)
{
    ULONG   j;
    ULONG   ulExtraBits;
    BYTE    bSrcByte;

    /******************************************************************/
    /* Enter a loop for the whole external bytes.                     */
    /******************************************************************/
    for ( j = SPad.cx/8; j--; )
    {
        /**************************************************************/
        /* Get the external byte (8 pels)                             */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;

        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x80) >> 7]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x40) >> 6]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x20) >> 5]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x10) >> 4]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x08) >> 3]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x04) >> 2]);
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x02) >> 1]);
        SetInternal16BppPel(SPad.ausConvertTable[ bSrcByte & 0x01      ]);
   }

    /******************************************************************/
    /* if there are extra bits at the end of the row, handle them now */
    /******************************************************************/
    ulExtraBits = SPad.cx & 7;
    if ( ulExtraBits )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        while (ulExtraBits--)
        {
            SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0x80) >> 7]);
            /**********************************************************/
            /* Move the next pel up into bit 7 of the src byte        */
            /**********************************************************/
            bSrcByte <<= 1;
        }
    }
} /* ConvertExt1ToInt16 */


/**********************************************************************/
/* ConvertExt4ToInt16                                                 */
/*                                                                    */
/* Converts a single scan line from external 1bpp to internal 16bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt4ToInt16(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /******************************************************************/
    /* Enter a loop for the whole external bytes.                     */
    /* (There are 2 pels per external byte).                          */
    /******************************************************************/
    for ( j = SPad.cx/2; j--; )
    {
        /**************************************************************/
        /* Get the external byte (2 pels)                             */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;

        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0xF0) >> 4]);
        SetInternal16BppPel(SPad.ausConvertTable[ bSrcByte & 0x0F      ]);
   }

    /******************************************************************/
    /* if there is an extra pel at the end of the row, handle it now  */
    /******************************************************************/
    if ( SPad.cx & 1 )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        SetInternal16BppPel(SPad.ausConvertTable[(bSrcByte & 0xF0) >> 4]);
    }
} /* ConvertExt4ToInt16 */

#ifdef   BPP24
/**********************************************************************/
/* ConvertExt1ToInt24                                                 */
/*                                                                    */
/* Converts a single scan line from external 1bpp to internal 16bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt1ToInt24(VOID)
{
    ULONG   j;
    ULONG   ulExtraBits;
    BYTE    bSrcByte;

    /******************************************************************/
    /* Enter a loop for the whole external bytes.                     */
    /******************************************************************/
    for ( j = SPad.cx/8; j--; )
    {
        /**************************************************************/
        /* Get the external byte (8 pels)                             */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;

        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x80) >> 7]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x40) >> 6]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x20) >> 5]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x10) >> 4]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x08) >> 3]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x04) >> 2]);
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x02) >> 1]);
        SetInternal24BppPel(SPad.ausConvertTable[ bSrcByte & 0x01      ]);
   }

    /******************************************************************/
    /* if there are extra bits at the end of the row, handle them now */
    /******************************************************************/
    ulExtraBits = SPad.cx & 7;
    if ( ulExtraBits )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        while (ulExtraBits--)
        {
            SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0x80) >> 7]);
            /**********************************************************/
            /* Move the next pel up into bit 7 of the src byte        */
            /**********************************************************/
            bSrcByte <<= 1;
        }
    }
} /* ConvertExt1ToInt24 */


/**********************************************************************/
/* ConvertExt4ToInt24                                                 */
/*                                                                    */
/* Converts a single scan line from external 4bpp to internal 24bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt4ToInt24(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /******************************************************************/
    /* Enter a loop for the whole external bytes.                     */
    /* (There are 2 pels per external byte).                          */
    /******************************************************************/
    for ( j = SPad.cx/2; j--; )
    {
        /**************************************************************/
        /* Get the external byte (2 pels)                             */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;

        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0xF0) >> 4]);
        SetInternal24BppPel(SPad.ausConvertTable[ bSrcByte & 0x0F      ]);
    }

    /******************************************************************/
    /* if there is an extra pel at the end of the row, handle it now  */
    /******************************************************************/
    if ( SPad.cx & 1 )
    {
        bSrcByte = *SPad.pbSrcPointer++;
        SetInternal24BppPel(SPad.ausConvertTable[(bSrcByte & 0xF0) >> 4]);
    }
} /* ConvertExt4ToInt24 */

/**********************************************************************/
/* ConvertExt8ToInt24                                                 */
/*                                                                    */
/* Converts a single scan line from external 8bpp to internal 24bpp.  */
/**********************************************************************/
VOID DRIVERCALL ConvertExt8ToInt24(VOID)
{
    ULONG   j;
    BYTE    bSrcByte;

    /******************************************************************/
    /* Enter a loop for the whole external bytes.                     */
    /* (There is 1 pel per external byte).                            */
    /******************************************************************/
    for ( j = SPad.cx; j--; )
    {
        /**************************************************************/
        /* Get the external byte (1 pel)                              */
        /**************************************************************/
        bSrcByte = *SPad.pbSrcPointer++;
        SetInternal24BppPel(SPad.ausConvertTable[ bSrcByte ]);
    }

} /* ConvertExt8ToInt24 */

/**********************************************************************/
/* ConvertExt24ToInt24                                                */
/*                                                                    */
/* Converts a single scan line from external 24bpp to internal 24bpp. */
/* (No convert table is used at 24 bpp).                              */
/**********************************************************************/
VOID ConvertExt24ToInt24(VOID)
{
    ULONG            j;
    RGB              TrgPel;

    for ( j = SPad.cx; j--; )
    {
        TrgPel = *((PRGB)SPad.pbSrcPointer);
        if ( !(DDT.fScreenFlags & USE_ATTDAC) ) {
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bRed;
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bGreen;
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bBlue;
        }
        else
        {
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bBlue;
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bGreen;
           *((PBYTE)SPad.pbTrgPointer)++ = TrgPel.bRed;
        }
        SPad.pbSrcPointer += sizeof(RGB);
    }
} /* ConvertExt24ToInt24 */
#endif  //bpp24

/**********************************************************************/
/* From24BppConvertFn                                                 */
/*                                                                    */
/* Returns a pointer to the function to be used to convert from a     */
/* 24bpp external bitmap to the internal format.                      */
/**********************************************************************/
PFNV DRIVERCALL From24BppConvertFn(VOID)
{
    switch (SPad.cInternalBitCount)
    {
        case 1:
            return( ConvertExt24ToInt1 );

        case 4:
            return( ConvertExt24ToInt4 );

        case 8:
            return( ConvertExt24ToInt8 );

#ifdef MATROX
        /**************************************************************/
        /* Matrox card MAY be present, see which version of this      */
        /* routine we need.                                           */
        /**************************************************************/
        case 16:
            if (RunningOnMatrox())
            {
                return( ConvertExt24ToInt16Matrox );
            }
            else
            {
                return( ConvertExt24ToInt16 );
            }
#else /* MATROX */
        /**************************************************************/
        /* No matrox card possible, use XGA version of this routine   */
        /**************************************************************/
        case 16:
            return( ConvertExt24ToInt16 );
#endif /* MATROX */
        #ifdef   BPP24
        case 24:
            return( ConvertExt24ToInt24 );
        #endif
   }
} /* From24BppConvertFn */

/**********************************************************************/
/* From16BppExternalFn     @77638  added new function                 */
/*                                                                    */
/* Returns a pointer to the function to be used to convert from a     */
/* 16bpp external bitmap to the internal format.                      */
/**********************************************************************/
PFNV DRIVERCALL From16BppExternalFn(VOID)
{
    switch (SPad.cInternalBitCount)
    {
        case 1:
            return( ConvertExt16ToInt1 );

   }
} /* From16BppExternalFn */


/**********************************************************************/
/* From8BppExternalFn                                                 */
/*                                                                    */
/* Returns a pointer to the function to be used to convert from an    */
/* 8bpp external bitmap to the internal format.                       */
/**********************************************************************/
PFNV DRIVERCALL From8BppExternalFn(VOID)
{
    switch (SPad.cInternalBitCount)
    {
        case 1:
            return( Convert8To1 );

        case 4:
            return( ConvertExt8ToInt4 );

        case 8:
            return( ConvertExt8ToInt8 );

        case 16:
            return( ConvertExt8ToInt16 );

        #ifdef   BPP24
        case 24:
            return( ConvertExt8ToInt24 );
        #endif
    }
} /* From8BppExternalFn */


/**********************************************************************/
/* From4BppExternalFn                                                 */
/*                                                                    */
/* Returns a pointer to the function to be used to convert from a     */
/* 4bpp External bitmap to the internal format.                       */
/* Note that we dont support palette manager at 4bpp so don't have    */
/* int4ToExt routines which support palettes                          */
/**********************************************************************/
PFNV DRIVERCALL From4BppExternalFn(VOID)
{
    switch (SPad.cInternalBitCount)
    {
        case 1:
            return( Convert4To1 );

        case 4:
            return( ConvertExt4ToInt4 );

        case 8:
            return( ConvertExt4ToInt8 );

        case 16:
            return( ConvertExt4ToInt16 );

        #ifdef   BPP24
        case 24:
            return( ConvertExt4ToInt24 );
        #endif
    }
} /* From4BppExternalFn */


/**********************************************************************/
/* From1BppExternalFn                                                 */
/*                                                                    */
/* Returns a pointer to the function to be used to convert from a     */
/* 1bpp external bitmap to the internal format.                       */
/**********************************************************************/
PFNV DRIVERCALL From1BppExternalFn(VOID)
{
    switch (SPad.cInternalBitCount)
    {
        case 1:
            return( ConvertExt1ToInt1 );

        case 4:
            return( Convert1To4 );

        case 8:
            return( ConvertExt1ToInt8 );

        case 16:
            return( ConvertExt1ToInt16 );

        #ifdef   BPP24
        case 24:
            return( ConvertExt1ToInt24 );
        #endif
    }
} /* From1BppExternalFn */


/**********************************************************************/
/* GetExternalConvertFn                                               */
/*                                                                    */
/* Returns a pointer to the bpp specific function to be used to do    */
/* the work of converting from external to internal format.           */
/* Returns NULL if unrecognised format (shouldn't occur).             */
/**********************************************************************/
PFNV DRIVERCALL GetExternalConvertFn(VOID)
{
    switch (SPad.cExternalBitCount)
    {
        case 1:
            return( From1BppExternalFn() );

        case 4:
            return( From4BppExternalFn() );

        case 8:
            return( From8BppExternalFn() );

        case 16:                                          /* @77638 */
            return( From16bppExternalFn() );              /* @77638 */

        case 24:
            return( From24BppConvertFn() );
    }
} /* GetExternalConvertFn */


/**********************************************************************/
/* GetConvertTableForPalette                                          */
/*                                                                    */
/* Gets the colour conversion table when the colour format is         */
/* LCOLF_PALETTE. Maps the exteral indices or colours to indices into */
/* the current palette.                                               */
/**********************************************************************/
VOID DRIVERCALL GetConvertTableForPalette(VOID)
{
    ULONG   i;
    PRGB2   pExtRGB;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB2)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    if ( SPad.fbFlags & CONV_BCE_PALETTE )
    {
        for (i = 0; i < SPad.usExtTabSize; i++)
        {
            /**********************************************************/
            /* We pick up just the blue byte from the external table  */
            /* which we know is the one holding the index.            */
            /**********************************************************/
            SPad.ausConvertTable[i] = pExtRGB->bBlue;

            /**********************************************************/
            /* To be using BCE_PALETTE then we must have the new      */
            /* format header, and so be using RGB2s in the external   */
            /* color table                                            */
            /**********************************************************/
            pExtRGB++;
        }
    }
    else /* external table is not already the mapping we need */
    {
        for (i = 0; i < SPad.usExtTabSize; i++)
        {
            SPad.ausConvertTable[i] =
            #ifndef   BPP24
                                  (USHORT)NearestPaletteIndex(*pExtRGB);
            #else
                                  NearestPaletteIndex(*pExtRGB);
            #endif

            pExtRGB = (PRGB2)((PBYTE)pExtRGB + SPad.usIncrement);
        }
    }
} /* GetConvertTableForPalette */


/**********************************************************************/
/* GetMonoConvertTable                                                */
/*                                                                    */
/* Creates a conversion table for a mono bitmap                       */
/**********************************************************************/
VOID DRIVERCALL GetMonoConvertTable(VOID)
{
#pragma message( __FILE__"(1271) : Warning: algorithm differs from VGA/8514")
    SPad.ausConvertTable[0] = 1;
    SPad.ausConvertTable[1] = 0;
} /* GetMonoConvertTable */

/**********************************************************************/
/* GetMonoToColourConvertTable                                        */
/*                                                                    */
/* Creates a conversion table from a mono bitmap to a colour bitmap   */
/*                                                                    */
/* For various historical reasons (ie VGA did it) the colour table    */
/* associated with an external bitmap is ignored.                     */
/* Colour 0 is converted to the current background colour             */
/* Colour 1 is converted to the current foreground colour             */
/**********************************************************************/
VOID DRIVERCALL GetMonoToColourConvertTable(VOID)
{
    SPad.ausConvertTable[0] = LogToPhyIndex(pdc->DCICurImgAts.ibnd.lBackColor);
    SPad.ausConvertTable[1] = LogToPhyIndex(pdc->DCICurImgAts.ibnd.lColor);
} /* GetMonoToColourConvertTable */

/**********************************************************************/
/* GetColourToMonoConvertTable                                        */
/*                                                                    */
/* Creates a conversion table for translating from colour to mono.    */
/* Each entry in the table is 0 or 1 depending on whether the source  */
/* colour is closer to black or white.                                */
/**********************************************************************/
VOID DRIVERCALL GetColourToMonoConvertTable(VOID)
{
    PRGB2   pExtRGB;
    ULONG   i;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB2)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    for (i = 0;
         i < SPad.usExtTabSize;
         i++, pExtRGB = (PRGB2)((PBYTE)pExtRGB + SPad.usIncrement) )
    {
        SPad.ausConvertTable[i] = MapRGBToMono(pExtRGB);
    }
} /* GetColourToMonoConvertTable */


/**********************************************************************/
/* GetColourToColourTable                                             */
/*                                                                    */
/* Creates a conversion table for translating from colour to colour.  */
/* Each entry in the table is mapped to the nearest colour in either  */
/* the physical palette or the (realizable) logical colour table.     */
/**********************************************************************/
VOID DRIVERCALL GetColourToColourConvertTable(VOID)
{
    PRGB2   pExtRGB;
    ULONG   i;

    /******************************************************************/
    /* Work out where the external colour table starts.               */
    /******************************************************************/
    pExtRGB = (PRGB2)
            ((PBYTE)SPad.pExternalHeader + SPad.pExternalHeader->cbFix);

    for (i = 0;
         i < SPad.usExtTabSize;
         i++, pExtRGB = (PRGB2)((PBYTE)pExtRGB + SPad.usIncrement) )
    {
        if (fRealizeSupported &&
            (pdc->DCIColStatus & LCOL_REALIZABLE))
        {
            SPad.ausConvertTable[i] =
            #ifndef   BPP24
                              (USHORT)NearestRealizableIndex(*pExtRGB);
            #else
                              NearestRealizableIndex(*pExtRGB);
            #endif
        }

        else /* colour table is not realizable */
        {
            SPad.ausConvertTable[i] =
            #ifndef   BPP24
                          (USHORT)NearestDefaultPhysicalIndex(*pExtRGB);
            #else
                          NearestDefaultPhysicalIndex(*pExtRGB);
            #endif
        }
    }

    /******************************************************************/
    /* If internal and external bitcounts are both 4 then we will have*/
    /* set up a pel for pel conversion table with 16 enties in it.    */
    /* We will be able to process data far quicker if we now convert  */
    /* this into a byte to byte table with 256 entries in it.         */
    /******************************************************************/
#pragma message( __FILE__"(1345) : Optimization: could convert pel/pel table to byte/byte")

} /* GetColourToColourConvertTable */


/**********************************************************************/
/* TableExtToInt                                                      */
/*                                                                    */
/* Routine to create a Colour Conversion table between the colour     */
/* table supplied with an external bitmap and the Logical Colour Table*/
/* or the logical palette under palette manager.                      */
/**********************************************************************/
VOID DRIVERCALL TableExtToInt(VOID)
{
    if (pdc->DCIColFormat == LCOLF_PALETTE)
    {
        GetConvertTableForPalette();
    }
    else if (SPad.cExternalBitCount == 1)
    {
        if (SPad.cInternalBitCount == 1)
        {
            GetMonoConvertTable();
        }
        else
        {
            GetMonoToColourConvertTable();
        }
    }
    else if (SPad.cInternalBitCount == 1)
    {
        GetColourToMonoConvertTable();
    }
    else
    {
        GetColourToColourConvertTable();
    }
} /* TableExtToInt */

/**********************************************************************/
/* ExtToIntConversionSetUp                                            */
/*                                                                    */
/* Called to set up the scrath pad ready for the conversion routines. */
/* Sets up the external bitcount, table size, element size and the    */
/* options flags.                                                     */
/*                                                                    */
/* Note the conversion tbale and the conversion function are not set  */
/* up.                                                                */
/**********************************************************************/
VOID DRIVERCALL ExtToIntConversionSetUp( PBITMAPINFO2  pSourceHeader )
{
    if ( OldFormatBitmap(pSourceHeader) )
    {
        SPad.fbFlags           = 0;
        SPad.cExternalBitCount = ((PBITMAPINFO)pSourceHeader)->cBitCount;
        SPad.usExtTabSize      = (USHORT)(1 << SPad.cExternalBitCount);
        SPad.usIncrement       = sizeof(RGB);
    }
    else
    {
        /**************************************************************/
        /* New BITMAPINFO2 bitmap is in use                           */
        /**************************************************************/
        SPad.cExternalBitCount = pSourceHeader->cBitCount;
        SPad.usIncrement       = sizeof(RGB2);

        /**************************************************************/
        /* If the cclrUsed field is zero we assume they just forgot   */
        /* to fill it in.                                             */
        /**************************************************************/
        if ( (pSourceHeader->cbFix >= SIZE_BM2_TO_USED(pSourceHeader)) &&
             (pSourceHeader->cclrUsed != 0) )
        {
            /**********************************************************/
            /* cclrUsed field is filled in so use the info it contains*/
            /**********************************************************/
            SPad.usExtTabSize = (USHORT)
                   min(1UL << SPad.cExternalBitCount,
                       SPad.pExternalHeader->cclrUsed);
        }
        else
        {
            /**********************************************************/
            /* we use the normal size of an external bitmap           */
            /**********************************************************/
            SPad.usExtTabSize = (USHORT)(1 << SPad.cExternalBitCount);
        }

        /**************************************************************/
        /* Check to see if we have been passed the special BCE_PALETTE*/
        /* format.                                                    */
        /* This only applies if there is a palette in the current DC  */
        /* (Higher level code will have raised an error if they asked */
        /* for the special format without having a palette)           */
        /**************************************************************/
        if ((pdc->DCIColFormat == LCOLF_PALETTE) &&
            (pSourceHeader->cbFix >= SIZE_ALL_BM2_FIELDS(pSourceHeader)) &&
            (SPad.pExternalHeader->ulColorEncoding == BCE_PALETTE))
        {
            SPad.fbFlags = CONV_BCE_PALETTE;
        }
        else
        {
            SPad.fbFlags = 0;
        }
    }
}


/**********************************************************************/
/* DoTheConversion                                                    */
/*                                                                    */
/* Called once we have established that we need to do a conversion    */
/* from external to internal format rather than just a copy.          */
/* Creates a conversion table and kicks of the conversion.            */
/* For a non-24 bpp source we need a conversion table to use in the   */
/* inner conversion loop. For 24 bpp we need the address of a         */
/* function to do translation from 24-bit RGB to an index.            */
/* If the target is 16 bpp then no table is needed since the 16-bit   */
/* RGB is made as the 16 bpp bitmap is constructed.                   */
/**********************************************************************/
VOID DRIVERCALL DoTheConversion(VOID)
{
    if ( SPad.cExternalBitCount == 24 )
    {
        /**************************************************************/
        /* 24bpp needs an inner function rather than a table.         */
        /**************************************************************/
        SPad.pfnRGBToIndex = RGBToIndexFunction();
    }
    else
    {
        /**************************************************************/
        /* Create the conversion table.                               */
        /**************************************************************/
        TableExtToInt();
    }

    /******************************************************************/
    /* Now do the conversion.                                         */
    /******************************************************************/
    ConvertPMInt(GetExternalConvertFn(),
                 &SPad.pbTrgPointer,
                 &SPad.pbSrcPointer);
} /* DoTheConversion */


/**********************************************************************/
/* ConvertExtToInt                                                    */
/*                                                                    */
/* pbSourceBitmap     pointer to the source (external) bitmap data    */
/* pSourceHeader      pointer to the source bitmap header             */
/* pbhTargetBitmap,   pointer to the target (internal) bitmap header  */
/* pptlScanStart      pointer to the scan starting coordinate         */
/* lScanLines         number of scanlines to process                  */
/*                                                                    */
/* Converts a source bitmap in external (PM) format to a target       */
/* bitmap in internal format.                                         */
/**********************************************************************/
VOID DRIVERCALL ConvertExtToInt( PBYTE         pbSourceBitmap,
                                 PBITMAPINFO2  pSourceHeader,
                                 pBitmapHeader pbhTargetBitmap,
                                 PPOINTL       pptlScanStart,
                                 LONG          lScanLines)
{
    /******************************************************************/
    /* set up our global parameter block. Note that this memory is a  */
    /* general purpose scratch pad in the data segment. It is not     */
    /* used by any function currently calling ConvertExtToInt. If     */
    /* this changes the whole thing will come crashing down           */
    /******************************************************************/
    SPad.pbExternalBitmap  = pbSourceBitmap;
    SPad.pExternalHeader   = pSourceHeader;
    SPad.pbhInternalBitmap = pbhTargetBitmap;
    SPad.pptlScanStart     = pptlScanStart;
    SPad.lScanLines        = lScanLines;
    SPad.cx                = SPad.pbhInternalBitmap->Info.Width;
    SPad.cInternalBitCount = SPad.pbhInternalBitmap->Info.BitCount;

#ifdef XFIREWALLS
    /******************************************************************/
    /* We are passed calls where the dimensions of the internal       */
    /* bitmap does not agree with the dimensions given in the         */
    /* external bitmap info.  In this case we will use our internal   */
    /* sizes.                                                         */
    /******************************************************************/
    if ( OldFormatBitmap(pSourceHeader) )
    {
        if ((((PBITMAPINFO)pSourceHeader)->cx
                      != SPad.pbhInternalBitmap->Info.Width ) ||
            (((PBITMAPINFO)pSourceHeader)->cy
                      != SPad.pbhInternalBitmap->Info.Height ))
        {
            DebugOutput("Bitmap conversion : Ext and Int dimensions differ\n\r");
            (PBITMAPINFO)pSourceHeader->cx =
                                      SPad.pbhInternalBitmap->Info.Width;
            (PBITMAPINFO)pSourceHeader->cy =
                                      SPad.pbhInternalBitmap->Info.Height;
        }
    }
    else
    {
        if ((pSourceHeader->cx != SPad.pbhInternalBitmap->Info.Width ) ||
            (pSourceHeader->cy != SPad.pbhInternalBitmap->Info.Height ))
        {
            DebugOutput("Bitmap conversion : Ext and Int dimensions differ\n\r");
            pSourceHeader->cx = SPad.pbhInternalBitmap->Info.Width;
            pSourceHeader->cy = SPad.pbhInternalBitmap->Info.Height;
        }
    }
#endif /* FIREWALLS */

    ExtToIntConversionSetUp( pSourceHeader );

    /******************************************************************/
    /* If colour conversion is required                               */
    /******************************************************************/
    if (ConversionRequired())
    {
        DoTheConversion();
    }
    else {
           /**************************************************************/
           /* no colour conversion is required and so call subroutine to */
           /* perform the flipping of the order of the rows              */
           /**************************************************************/
           FlipTheBitmap(EXT_TO_INT_COPY);
    }

} /* ConvertExtToInt */
