/*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          = DRAWBITS                                       */
/*                                                                    */
/*   Description     = Display Device Driver functions                */
/*                     Drawbits support                               */
/*                                                                    */
/*   Function        = DrawBits                                       */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design Specification     */
/*                                                                    */
/*                                                                    */
/*   Change History:                                                  */
/*                                                                    */
/*   08/25/93 - Defect 72854 (APAR PJ09526) (TAV)  Changed logic      */
/*              in GetConvertTableForBBO_PAL_COLORS() for the         */
/*              condition where the destination was the screen.       */
/*              Must check that the index found is actually less than */
/*              the number of stored palette entries, otherwise       */
/*              return an error. Also necessitated changing function  */
/*              from VOID to ULONG.                                   */
/*                                                                    */
/*   10/05/93 - Defect 71428. Set PixelOp for later use by            */
/*              eddh_SrcDestBlt for 8514 and S3 when doing a          */
/*              DrawBits to a memory DC.                              */
/*                                                                    */
/**********************************************************************/

#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#define INCL_GRE_BITMAPS
#define INCL_GRE_PALETTE
#include <eddinclt.h>

#include <eddfcone.h>
#include <eddhcone.h>
#include <eddmcone.h>
#include <edddtypt.h>
#include <eddftype.h>
#include <eddhtype.h>
#include <eddbextf.h>
#include <eddgextf.h>
#include <eddmextf.h>
#ifdef DCAF                                                               //DCAF
#include <dcafextf.h>                                                     //DCAF
#endif                                                                    //DCAF
#include <eddhmacr.h>

#include <cursor.h>
#include <bitmaps.h>
#include <twozero.h>
#ifdef VRAMPTR
#include <eddncach.h>
#endif /* VRAMPTR */

/**********************************************************************/
/* This includes the SPad as a CONVERTCOMMON scratch pad. This        */
/* contains both the values used by pixblt and those used by          */
/* the conversion routines. Thus avoiding pixblt and the conversion   */
/* routines from interfering with each other.                         */
/**********************************************************************/
#include <convfuns.h>

#define RLE_END_OF_LINE     0
#define RLE_END_OF_BITMAP   1
#define RLE_DELTA           2

/**********************************************************************/
/* The RLE decoding state                                             */
/**********************************************************************/
#define RLE_IN_CHUNK                    1
#define RLE_LEFT_CHUNK                  2
#define RLE_RIGHT_OR_BELOW_CHUNK        3
#define RLE_ABOVE_CHUNK                 4

/**********************************************************************/
/* The following are needed for the SetDrawMode macros.               */
/**********************************************************************/
extern SHORT               softDrawInUse;
extern ULONG               LinePatternCur;
extern ULONG               LinePatternPhys;
extern ULONG               LinePatternSys;
extern ULONG               MarkerCur;
extern ULONG               MarkerPhys;
extern ULONG               MarkerSys;
extern ULONG               pCurCacheBasePhy;
extern ULONG               pSysCacheStartPhy;
extern ULONG               pPhunkPhys;
#ifndef   _8514
extern MMReg               ShadowXGARegs;
extern pMMReg              pXGARegs;
extern pMMReg              pRealXGARegs;
#else
#include <8514.h>
extern MM8514Reg           Shadow8514Regs;
extern pMM8514Reg          p8514Regs;
extern ULONG               PixelOp;
#endif
extern pDrawFunctionsTable pDrawFunctions;
extern drawFunctionsTable  softDrawTable;
extern drawFunctionsTable  hardDrawTable;
extern SHORT               foregroundSession;


extern PPFNL               EnginesDispatchTable;
extern BitmapHeader        DirectListEntry;
extern BITBLTPB            AIxfer;
extern CURSORDATA          cursor_data;
extern BYTE                RopToHWMix[];
extern BitmapHeader        PhunkBMHeader;

BitmapHeader    DrawBitsBMHeader;

/**********************************************************************/
/* Declare this function here as it is local to this module.          */
/**********************************************************************/
BOOL    NotWithTheseFormats(PBITMAPINFO2,
                            pBitmapHeader,
                            USHORT,
                            ULONG);
VOID    DrawBitsRLESetup( VOID );
VOID    ConvertExternalRLE ( VOID );
VOID    GetConvertTableForScreenPalette(VOID);
ULONG   GetConvertTableForBBO_PAL_COLORS(VOID);            /*72854 TAV*/
USHORT  PalIndirectionUsed(VOID);
VOID    DecodeRLERunLength( VOID );
VOID    DecodeRLEUnencodedRun( VOID );
VOID    ConvColour24    ( VOID );
VOID    ConvColour8to8  ( VOID );
VOID    CopyColour8to8  ( VOID );
VOID    ConvColour8to16 ( VOID );

#ifdef XGADEBUG

    void  debug_long(CHAR * string, ULONG number)
    {
        ULONG i;
        ULONG    j, k;
        CHAR    num[10] = "         \0";


        DebugOutput(string);
        DebugOutput(" : 0x");
        j = number;
        for (i=8; i ; i--)
        {
            k = j % 16;
            j = j / 16;
            switch (k)
            {
                case 0:     num[i] = '0'; break;
                case 1:     num[i] = '1'; break;
                case 2:     num[i] = '2'; break;
                case 3:     num[i] = '3'; break;
                case 4:     num[i] = '4'; break;
                case 5:     num[i] = '5'; break;
                case 6:     num[i] = '6'; break;
                case 7:     num[i] = '7'; break;
                case 8:     num[i] = '8'; break;
                case 9:     num[i] = '9'; break;
                case 10:    num[i] = 'A'; break;
                case 11:    num[i] = 'B'; break;
                case 12:    num[i] = 'C'; break;
                case 13:    num[i] = 'D'; break;
                case 14:    num[i] = 'E'; break;
                case 15:    num[i] = 'F'; break;
                default:    num[i] = '?'; break;
            }
        }
        DebugOutput(num);
        DebugOutput("\r\n");
    }
    /******************************************************************/
    /* Print out a message and a long                                 */
    /******************************************************************/
    #define DEBUG(A, B)     debug_long(A, (LONG)B)



#else
    #define DEBUG(A, B)
#endif

#ifdef FIREWALLS
    /******************************************************************/
    /* Check that two pointers delimit an area within the target      */
    /* bitmap.                                                        */
    /******************************************************************/
    #define TARGET_CHECK(A, B)                                         \
    if ( ((A) < SPad.pbhInternalBitmap->Bitmap) ||                     \
         ((B) > SPad.pbhInternalBitmap->Bitmap +                       \
                              SPad.pbhInternalBitmap->BMSize))         \
    {                                                                  \
        DEBUG("ERROR Invalid target pointer, from", (A));              \
        DEBUG("      to", (B));                                        \
        DEBUG("    bitmap", SPad.pbhInternalBitmap->Bitmap);           \
        DEBUG("    size", SPad.pbhInternalBitmap->BMSize);             \
        DEBUG("    bytes per line", SPad.ulInternalBytesPerLine);      \
        haltproc();                                                    \
    }
#else
    #define TARGET_CHECK(A, B)
#endif


/**********************************************************************/
/* The DrawBits is effectively the equivalent to the bitblt function  */
/* but the source is an external bitmap instead of an internal one.   */
/* For most cases we simply return the call to the engine to be       */
/* simulated.                                                         */
/*                                                                    */
/* For a few of the more common cases where fast performance is       */
/* likely to be needed we handle the call ourselves.  These cases are */
/* for source-destination only ROPs where the conversion required is  */
/* one of the following:                                              */
/*                                                                    */
/*      - 8  bpp ext to 8  bpp int                                    */
/*      - 8  bpp ext to 16 bpp int                                    */
/*      - 24 bpp ext to 8  bpp int                                    */
/*      - 24 bpp ext to 16 bpp int                                    */
/*      - RLE equivalents of the above for source->dest copies        */
/*        only wihtout BM_SRCTRANSPARENT.                             */
/*                                                                    */
/* The architecture of (non-simulated) DrawBits requires use of the   */
/* PixBlt routines and external->internal conversion routines.  These */
/* both use the scratch pad for holding data.  To prevent them        */
/* interfering with each other, the conversion routines scratch pad   */
/* definition has had the BitBlt scratch pad definition added as a    */
/* sub-structure as the first element.                                */
/*                                                                    */
/* The non-simulated DrawBits code works as follows:                  */
/*                                                                    */
/* DrawBits sets up the global variables required to call off to      */
/* PixBltThroughClipsViaPHUNK in a similar way to eddb_BitBlt.  These */
/* include:                                                           */
/*      - SPad.BltSPad.BltFunction                                    */
/*      - softDrawInUse                                               */
/*      - AIxfer.rcsSrc                                               */
/*      - AIxfer.rcsDest                                              */
/*      - AIxfer.pbmhSrc                                              */
/*      - AIxfer.pbmhDest                                             */
/*                                                                    */
/* The AIxfer.pbmhSrc is set to point at DrawBitsBMHeader. This is    */
/* set up to describe a bitmap of size and dimensions that would be   */
/* created if the external bitmap where copied/converted into an      */
/* internal bitmap of the same format as the destination bitmap. The  */
/* bitcount and format of the external bitmap are stores in the       */
/* scratch pad for later use (including RLE if used).                 */
/*                                                                    */
/* The SPad.BltSPad.BltFunction is set up to be the                   */
/* SimpleSourceDestPixBlt function which will do the blt to the       */
/* destination after the source has been converted from external      */
/* format and copied into the PHUNK.                                  */
/*                                                                    */
/* The scratch pad values required for the conversion routines (which */
/* will be used to convert the data from the external bitmap to       */
/* internal form) are then setup. This envolves mimicking the setup   */
/* done by ConvertExtToInt and calling ExtToIntConversionSetUp. RLE   */
/* external bitmaps require some further initialisation of scratch    */
/* pad values. This is done by DrawBitsRLESetUp.                      */
/*                                                                    */
/* PixBltThroughClipsViaPHUNK is then called to handle the clipping   */
/* and the potentially huge/giant external source. The pixblt         */
/* function does all its processing as normal and calls off to        */
/* CopyChunkToPhunkForPixBltSource in the usual way.                  */
/*                                                                    */
/* CopyChunkToPhunkForPixBltSource checks to see if the source bitmap */
/* header is the DrawBitsBMHeader. If it is then instead of doing the */
/* copy itself it calls CopyExternalChunkToPhunkForPixBltSource.      */
/*                                                                    */
/* CopyExternalChunkToPhunkForPixBltSource needs to copy one chunk of */
/* the external bitmap into the phunk, converting the colours into    */
/* those in the current destination palette.                          */
/*                                                                    */
/* For non-RLE bitmaps this envolves calling the external to internal */
/* bitmap conversion routines once for each line in the chunk.        */
/*                                                                    */
/* RLE-bitmaps are supported for Src->Dest copies only, and only if a */
/* BM_SRCTRANSPARENT mix is not being used. They are copied into      */
/* the PHUNK by decoding the RLE bitmap into the PHUNK. The details   */
/* of how this is done can be found in the explanation given with     */
/* CopyExternalChunkToPhunkForPixbltSource.                           */
/*                                                                    */
/* DrawBits is also required to implement the two special blt mixes   */
/* BM_SRCTRANSPARENT and BM_DESTTRANSPARENT.  These both fit into the */
/* architecture without any problems.  The only point to note is that */
/* in the case of a BM_SRCTRANSPARENT mix being used with a VRAM      */
/* destination, the PixBltThroughClipsViaPHUNK will call the          */
/* simulation function SimulateSrcTransparentPixBlt instead of the    */
/* usual CopyChunkToPhunkForPixBltSource function because the XGA     */
/* hardware is not capable of doing the required color compare        */
/* function directly.  In this case the source will be in memory and  */
/* the simulation will call CopyChunkToPhunkForPixBltSource anyway    */
/* and this will call CopyExternalChunkToPhunkForPixBltSource in the  */
/* same way as for the standard DrawBits path.                        */
/*                                                                    */
/* "It's all really rather cunning!"                                  */
/**********************************************************************/
DDIENTRY DrawBits(HDC          hdc,
                  PBYTE        pbBits,
                  PBITMAPINFO2 pbmiInfoTable,
                  LONG         lCount,
                  PPOINTL      aptlPoints,
                  LONG         lRop,
                  ULONG        flOptions,
                  PDC          pdcArg,
                  ULONG        FunN)
{
    ULONG               ulError;           /* error code to be logged */
    ULONG               ulCorrelationResult;
    ULONG               i;
    USHORT              usTemp;

    /******************************************************************/
    /* Get driver semaphore and perform entry checks                  */
    /******************************************************************/
    EnterDriver(pdcArg, FunN, EDF_STANDARD);

    /******************************************************************/
    /* Return the call to the default handler if:                     */
    /*      - the conversion of the target rectangle from world       */
    /*        coordinates to AI coordinates is not simple (ie. the    */
    /*        conversion is from device coords)                       */
    /*      - the rop is not a src-dest only rop                      */
    /*      - the blt will require an expansion or compression        */
    /*      - the external/internal format combinations are not       */
    /*        supported as fast paths.                                */
    /*      - An RLE bitmap is being passed and the rop is not a      */
    /*        source->dest copy, or the background mix                */
    /*        BM_SRCTRANSPARENT is being used.                        */
    /*      - the BBO_PAL_COLORS flags is ON and the DC does not      */
    /*        have a palette loaded. (Added for defect 62887)         */
    /*                                                                */
    /* Notes:                                                         */
    /*                                                                */
    /* If COM_DEVICE is set we cannot return the call to the          */
    /* engine for simulation and we must just do the best we can.     */
    /*                                                                */
    /* A source is required for this rop if bits 0,1 and 4,5 are      */
    /* different to bits 2,3 and 6,7.  A pattern is required (3-way   */
    /* blt) if the high and low nibbles of the rop are not equal.     */
    /*                                                                */
    /* The target rectangle supplied is inclusive but the source      */
    /* rectangle supplied is non-inclusive on the top right edge.     */
    /*                                                                */
    /******************************************************************/
    if (!FunNTest(COM_DEVICE))
    {
        if (  ((FunNTest(COM_TRANSFORM)) && !pdc->DCIXFrmSimple)
           || (aptlPoints[1].x-aptlPoints[0].x != aptlPoints[3].x-aptlPoints[2].x - 1)
           || (aptlPoints[1].y-aptlPoints[0].y != aptlPoints[3].y-aptlPoints[2].y - 1)
           || (((BYTE)lRop & 0x33) << 2 == ((BYTE)lRop & 0xcc))
           || ((((BYTE)lRop >> 4) ^ (BYTE)lRop) & 0x0f)
           || NotWithTheseFormats(pbmiInfoTable,
                                  pdcArg->DCISelListEntry,
                                  pdcArg->DCICurImgAts.ibnd.usBackMixMode,
                                  lRop)
           || ((flOptions & BBO_PAL_COLORS) &&
               (pdc->DCIColFormat != LCOLF_PALETTE))
           )
        {
            ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_IGNORE_TIME);
            return( EnginesDispatchTable[NGreDrawBits & 0xff](hdc,
                                                              pbBits,
                                                              pbmiInfoTable,
                                                              lCount,
                                                              aptlPoints,
                                                              lRop,
                                                              flOptions,
                                                              pdcArg,
                                                              FunN));
        }
    }

/**********************************************************************/
/* !!! What happens with COM_DEVICE set - we will drop through here   */
/**********************************************************************/
    /******************************************************************/
    /* If the Bimap header passed was of the old format, set up a     */
    /* copy of it in the static new format header. This is the header */
    /* that will then be passed to the lower level routines.          */
    /* The lower level routines will not then have to worry about the */
    /* format of the header.                                          */
    /******************************************************************/
    if (pbmiInfoTable->cbFix == sizeof(BITMAPINFOHEADER))
    {
        SPad.ulExternalCx      = ((PBITMAPINFOHEADER)pbmiInfoTable)->cx;
        SPad.ulExternalCy      = ((PBITMAPINFOHEADER)pbmiInfoTable)->cy;
        SPad.cExternalBitCount = ((PBITMAPINFOHEADER)pbmiInfoTable)->cBitCount;
        SPad.ulExternalBytesPerLine = 4 *
                                 (((SPad.ulExternalCx * SPad.cExternalBitCount) + 31) / 32);
#ifdef FIREWALLS
        SPad.pbExternalEnd     = pbBits +
                                 (SPad.ulExternalCy  *
                                  SPad.ulExternalBytesPerLine);
#endif
    }
    else
    {
        SPad.ulExternalCx      = pbmiInfoTable->cx;
        SPad.ulExternalCy      = pbmiInfoTable->cy;
        SPad.cExternalBitCount = pbmiInfoTable->cBitCount;
        SPad.ulExternalBytesPerLine = 4 *
                                 (((SPad.ulExternalCx * SPad.cExternalBitCount) + 31) / 32);
#ifdef FIREWALLS
        /**************************************************************/
        /* We can only use cbImage if it is defined in the header     */
        /**************************************************************/
        if ( (pbmiInfoTable->cbFix >= SIZE_BM2_TO_IMAGE(pbmiInfoTable)) &&
             (pbmiInfoTable->cbImage != 0) )
        {
            SPad.pbExternalEnd     = pbBits + pbmiInfoTable->cbImage;
        }
        else
        {
            SPad.pbExternalEnd     = pbBits +
                                     (SPad.ulExternalCy  *
                                      SPad.ulExternalBytesPerLine);
        }
#endif

    }


    DEBUG("external width", SPad.ulExternalCx);
    DEBUG("external height", SPad.ulExternalCy);
    DEBUG("external bitcount", SPad.cExternalBitCount);
    DEBUG("external bytes per line", SPad.ulExternalBytesPerLine);
    DEBUG("External Start", pbBits);
    DEBUG("External end", SPad.pbExternalEnd);


    /******************************************************************/
    /* DrawBits is illegal within a path or area: if it occurs then   */
    /* log an error and exit.                                         */
    /******************************************************************/
    if ( FunNTest(COM_AREA | COM_PATH) )
    {
        if (FunNTest(COM_AREA))
        {
            ulError = PMERR_INV_IN_AREA;
        }
        else
        {
            ulError = PMERR_INV_IN_PATH;
        }
        goto DRAWBITS_LOGERR_EXIT;
    }

    /******************************************************************/
    /* DrawBits should always be called with four points passed to it.*/
    /******************************************************************/
    if ( lCount != 4 )
    {
        ulError = PMERR_INV_LENGTH_OR_COUNT;
        goto DRAWBITS_LOGERR_EXIT;
    }

    /******************************************************************/
    /* Check the source points are within the bitmap boundaries       */
    /******************************************************************/
    for (i = 2; i < 3 ; i++)
    {
        if ( (aptlPoints[i].x > (LONG)SPad.ulExternalCx) ||
             (aptlPoints[i].y > (LONG)SPad.ulExternalCy))
        {
            ulError = PMERR_INV_COORDINATE;
            goto DRAWBITS_LOGERR_EXIT;
        }
    }

    /******************************************************************/
    /* Check that the info table supplied is valid.                   */
    /******************************************************************/
    if (!ValidHeader(pbmiInfoTable))
    {
        ulError = PMERR_INV_INFO_TABLE;
        goto DRAWBITS_LOGERR_EXIT;
    }

    /******************************************************************/
    /* Set the correlation result to default to the successful return */
    /* code with no hits.                                             */
    /******************************************************************/
    ulCorrelationResult = OK;

    /******************************************************************/
    /* Apply the DC command mask to the command bits to turn off the  */
    /* draw bit if we are dead.                                       */
    /******************************************************************/
    COMMANDBITS(FunN) &= pdc->DCICommandMask;

    /******************************************************************/
    /* If we are being asked to draw, the target device context must  */
    /* have a selected bitmap.                                        */
    /******************************************************************/
    if ( (FunNTest(COM_DRAW)) &&
         (pdc->DCIBitmapType == BITMAP_NOT_SELECTED) )
    {
        ulError = PMERR_BITMAP_NOT_SELECTED;
        goto DRAWBITS_LOGERR_EXIT;
    }

    if ((FunNTest(COM_CORRELATE)) && (pdc->DCICorrInvalid))
    {
        /**************************************************************/
        /* We will be doing correlation and the correlation           */
        /* rectangles are not up to date so call eddg_ClipPickWindow  */
        /* to get them up to date.                                    */
        /**************************************************************/
        if (! eddg_ClipPickWindow())
        {
            goto DRAWBITS_ERR_EXIT;
        }
    }

    /******************************************************************/
    /* Set up the destination bitmap header.                          */
    /******************************************************************/
    AIxfer.pbmhDest = pdc->DCISelListEntry;

    /******************************************************************/
    /* Set up the target rectangle in AIxfer converting from the      */
    /* supplied world co-ordinates into AI coordinates.               */
    /******************************************************************/
    eddg_Convert( (PULONG)aptlPoints,
                  (PULONG)&AIxfer.rcsTrg,
                  COORD_WORLD,
                  COORD_AI,
                  4,
                  FunN);

    /******************************************************************/
    /* The coordinates have been converted from WORLD to AI. As this  */
    /* means they are numbered from the top instead of the bottom,    */
    /* we need to swap over the Y coordinates. This ensures that the  */
    /* first coordinate is less than the second.                      */
    /******************************************************************/

    usTemp                  = AIxfer.rcsTrg.pts1.y;
    AIxfer.rcsTrg.pts1.y    = AIxfer.rcsTrg.pts2.y;
    AIxfer.rcsTrg.pts2.y    = usTemp;

    /******************************************************************/
    /* If the bounds bit is set then update bounds to target          */
    /* rectangle.                                                     */
    /******************************************************************/
    if ( FunNTest(COM_ALT_BOUND | COM_BOUND) )
    {
        eddg_AddBounds ((pDevRect)&AIxfer.rcsTrg, FunN, COORD_AI);
    }

#ifdef DCAF                                                               //DCAF
    /******************************************************************/  //DCAF
    /* Accumulate DCAF screen bounds if required                      */  //DCAF
    /******************************************************************/  //DCAF
    if (DCAFBoundsRequired(FunN))                                         //DCAF
    {                                                                     //DCAF
        AccumulateScreenBoundsThroughClips( (pDevRect)&AIxfer.rcsTrg,     //DCAF
                                            COORD_AI );                   //DCAF
    }                                                                     //DCAF
#endif                                                                    //DCAF

    /******************************************************************/
    /* If correlate the bit is set and we have some correlation       */
    /* rectangles then do the correlation.                            */
    /******************************************************************/
    if ((FunNTest(COM_CORRELATE)) && (pdc->DCICorrNum))
    {
        ulCorrelationResult =
                  eddg_CheckRectCorrelation((pDevRect)&AIxfer.rcsTrg);
    }

    /******************************************************************/
    /* Defect 62605 - If a window issued the drawbits API when it     */
    /* was totally overlapped by another window, it would cause       */
    /* corruption on the overlapping window. In this scenario there   */
    /* are no clip rectangles for the destination DC.  The COM_DRAW   */
    /* flag was being ignored.                                        */
    /******************************************************************/
    if ( !(FunNTest(COM_DRAW)) )
    {
       goto DRAWBITS_PASS_EXIT;
    }

    /******************************************************************/
    /* Set up the source rectangle in AIxfer converting from the      */
    /* supplied device coordinates into AI coordinates. The right     */
    /* and upper boundaries are supplied as non-inclusive so we must  */
    /* adjust them here to make them inclusive.                       */
    /******************************************************************/
    AIxfer.rcsSrc.pts1.x = (SHORT)aptlPoints[2].x;
    AIxfer.rcsSrc.pts1.y = (SHORT)(SPad.ulExternalCy - aptlPoints[3].y + 1);
    AIxfer.rcsSrc.pts2.x = (SHORT)aptlPoints[3].x - (SHORT)1;
    AIxfer.rcsSrc.pts2.y = (SHORT)(SPad.ulExternalCy - aptlPoints[2].y);

    /******************************************************************/
    /* Set up the source bitmap header (DrawBitsBMHeader) and the     */
    /* AIxfer.pbmhSrc to point to it.                                 */
    /*                                                                */
    /* Note CopyChunkToPhunkForPixBltSource will be called later from */
    /* PixBltViaPHUNK and will detect that the source is an external  */
    /* bitmap from DrawBits by checking to see if the AIxfer.pbmhSrc  */
    /* parameter is the DrawBitsBMHeader.                             */
    /******************************************************************/
    AIxfer.pbmhSrc = &DrawBitsBMHeader;

    DrawBitsBMHeader.Bitmap = pbBits;
    DrawBitsBMHeader.BMPhys = NULL;
#ifdef VRAMPTR
    DrawBitsBMHeader.bm_cache_slot = BITMAP_NOT_CACHED;
#endif /* VRAMPTR */


    DrawBitsBMHeader.Info.Width =  (SHORT)SPad.ulExternalCx;
    DrawBitsBMHeader.Info.Height = (SHORT)SPad.ulExternalCy;
    /******************************************************************/
    /* Note : Bitcount value is set here to the external bitcount.    */
    /* This is to allow the conversion routines to set up the         */
    /* correct conversion function. Before PixBltThoughClipViaPhunk   */
    /* is called the bitcount is set to the target bitcount to enable */
    /* the phunk bitmap header to be set for the bitmap in the        */
    /* state it needs to be after the conversion has been done.       */
    /******************************************************************/
    DrawBitsBMHeader.Info.BitCount = (SHORT)SPad.cExternalBitCount;

    /******************************************************************/
    /* We must be carefull to check whether we are being passed a     */
    /* BITMAPINFO or a BITMAPINFO2 info table.                        */
    /* We will pass a format that reflects the compression used on    */
    /* the external bitmap.  This will be either NULL, BCA_RLE8, or   */
    /* BCA_RLE24.                                                     */
    /******************************************************************/
    if (pbmiInfoTable->cbFix == sizeof(BITMAPINFOHEADER))
    {
        DrawBitsBMHeader.Info.HWFormat = NULL;
    }
    else
    {
        DrawBitsBMHeader.Info.HWFormat = (SHORT)pbmiInfoTable->ulCompression;
    }

    /******************************************************************/
    /* We will convert the external bitmap to either 8 or 16 bits per */
    /* pel so there is no need to do any padding calculations here.   */
    /******************************************************************/
    DrawBitsBMHeader.Info.HWWidth = DrawBitsBMHeader.Info.Width-(SHORT)1;
    DrawBitsBMHeader.Info.HWHeight = DrawBitsBMHeader.Info.Height-(SHORT)1;

    if (AIxfer.pbmhDest->Info.BitCount == 8)
    {
        DrawBitsBMHeader.BytesPerLine = DrawBitsBMHeader.Info.Width;
    }
    else
    {
        DrawBitsBMHeader.BytesPerLine = DrawBitsBMHeader.Info.Width*(SHORT)2;
    }

    DrawBitsBMHeader.BMSize = DrawBitsBMHeader.BytesPerLine
                              * DrawBitsBMHeader.Info.Height;

    /******************************************************************/
    /* We will pass SimpleSourceDestPixBlt as the function for        */
    /* PixBltThroughClipsViaPHUNK to call.                            */
    /******************************************************************/
#ifdef _8514
    if (AIxfer.pbmhDest->Bitmap == NULL)
    {
      SetDrawModeHard;
    }
    else
    {
      SetDrawModeSoft;
      /****************************************************************/
      /* Defect 71428  Set PixelOp for eddf_Mess                      */
      /****************************************************************/
      PixelOp = BACK_SRC_SRC_PIX_MAP |
                FORE_SRC_SRC_PIX_MAP |
                STEP_PXBLT |
                SRC_PIX_MAP_B |
                DST_PIX_MAP_A |
                PAT_PIX_MAP_FORE |
                MASK_PIX_MAP_OFF |
                DRAW_MODE_DONTCARE |
                DIR_OCTANT_LRTB;

    }
    SPad.BltSpad.BltFunction = (*pDrawFunctions)[index_SrcDestBlt];
#else /* ~_8514 */
    SPad.BltSpad.BltFunction = SimpleSourceDestPixBlt;
#endif /* ~_8514 */
    /******************************************************************/
    /* Set the foreground and background mixes ready for the blt to   */
    /* be done according to the ROP requested.                        */
    /******************************************************************/
    #ifndef   _8514
    ShadowXGARegs.FgMix = RopToHWMix[(BYTE)lRop >> 4];
    ShadowXGARegs.BgMix = RopToHWMix[(BYTE)lRop & 0x0f];
    #else
    Shadow8514Regs.Function_1.Mix = RopToHWMix[(BYTE)lRop >> 4];
    Shadow8514Regs.Function_1.SrcSelect = FUNC_2OP_COPY;
    Shadow8514Regs.Function_0.Mix = RopToHWMix[(BYTE)lRop & 0x0f];
    Shadow8514Regs.Function_0.SrcSelect = FUNC_2OP_COPY;
    #endif

    /******************************************************************/
    /* Set up colour compare registers if required.                   */
    /******************************************************************/
    if (UseColorCompare(pdc->DCICurImgAts.ibnd.usBackMixMode))
    {
        /**************************************************************/
        /* set up the colour compare registers to allow for the       */
        /* background mix. Note BM_SRCTRANSPARENT mixes should have   */
        /* been passed back to the engine.                            */
        /**************************************************************/
        convert_BMix_to_ColComp(0, NULL);

    }   /* Not BM_SRCTRANSPARENT or BM_DESTTRANSPARENT */
    else
    {
       #ifndef   _8514
            ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
       #else
          Shadow8514Regs.Mode.PatternSrc = MD_PS_ONES;
          Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;
          Shadow8514Regs.Mode.PlaneMode  = MD_AF_W_NORMAL;
       #endif
    }

    /******************************************************************/
    /* If the destination bitmap is in VRAM then we must use the      */
    /* hardware to do the blt from the converted data in the PHUNK,   */
    /* otherwise we must use the software drawing code (MESS).        */
    /******************************************************************/

#ifndef _8514
#ifdef VRAMPTR
    if (AIxfer.pbmhDest->Bitmap == NULL)
#else /* ndef VRAMPTR */
    if (AIxfer.pbmhDest->BMPhys != NULL)
#endif /* ndef VRAMPTR */
    {
        SetDrawModeHard;
    }
    else
    {
        SetDrawModeSoft;
    }
#endif /* ~_8514 */

    /******************************************************************/
    /* Set up values required for setting up the conversion routines  */
    /******************************************************************/
    SPad.cInternalBitCount  = AIxfer.pbmhDest->Info.BitCount;
    SPad.pbhInternalBitmap  = AIxfer.pbmhDest;
    SPad.cExternalBitCount  = AIxfer.pbmhSrc->Info.BitCount;
    SPad.pbExternalBitmap   = AIxfer.pbmhSrc->Bitmap;
    SPad.pExternalHeader    = pbmiInfoTable;

    /******************************************************************/
    /* Set up variables used by the conversion related functions.     */
    /******************************************************************/
    ExtToIntConversionSetUp( pbmiInfoTable );

    /******************************************************************/
    /* Defect 62532 - Determine if a conversion is required.          */
    /*                                                                */
    /* There are certain circumstances effecting this decision that   */
    /* ConversionRequired does not know about.  Therefore we check    */
    /* for the following two conditions before letting                */
    /* ConversionRequired make the final decision.                    */
    /*                                                                */
    /* Both of these conditions only occur when the DC has a palette. */
    /*                                                                */
    /* - If the target is the screen.                                 */
    /*                                                                */
    /*   A conversion will be required since an application cannot    */
    /*   predict which HW palette slots its logical palette will be   */
    /*   mapped to.                                                   */
    /*                                                                */
    /* - If the BBO_PAL_COLORS flag is ON.                            */
    /*                                                                */
    /*   This flag tells us that the values in the external color     */
    /*   table are indices into the logical palette, so a translation */
    /*   is manditory.                                                */
    /*                                                                */
    /*   NOTE: If the destination DC is a memory DC, we check the     */
    /*         external color table to see if it requires indirection.*/
    /*         If not, we don't need to translate the bits.  This     */
    /*         gives us a great performance boost.                    */
    /*                                                                */
    /******************************************************************/

    if ( (pdc->DCIColFormat == LCOLF_PALETTE) &&
         (pdc->DCISelListEntry == &DirectListEntry) ) {

       SPad.fConvertPal = TRUE;
    }
    else if ( (pdc->DCIColFormat == LCOLF_PALETTE) &&
              (flOptions & BBO_PAL_COLORS) ) {

       SPad.fConvertPal = PalIndirectionUsed();
    }
    else if (ConversionRequired())
    {
        SPad.fConvertPal = TRUE;
    }
    else
    {
        SPad.fConvertPal = FALSE;
    }

    /******************************************************************/
    /* Set up line conversion function.                               */
    /******************************************************************/
    if (SPad.fConvertPal)
    {
        SPad.pfnvConvertLine = GetExternalConvertFn();
    }
    else
    {
        /**************************************************************/
        /* No conversion required so it must be an 8bpp to 8bpp       */
        /* coversion. (Only 24/8bpp -> 16/8bpp supported).            */
        /**************************************************************/
        SPad.pfnvConvertLine = ConvertExt8ToInt8NoConversion;
    }

    if (DrawBitsBMHeader.Info.HWFormat != NULL)
    {
        /**************************************************************/
        /* Set up the variables for the RLE conversion.               */
        /**************************************************************/
        DrawBitsRLESetup();
    }

    /******************************************************************/
    /* Set up palette mapping table (or RGB function if 24bpp),       */
    /* for the conversion routines.                                   */
    /******************************************************************/
    if (SPad.fConvertPal)
    {
       /**************************************************************/
       /* Create conversion table (or decide on 24bpp conversion     */
       /* function)                                                  */
       /**************************************************************/
        if ( SPad.cExternalBitCount == 24 )
        {
            /*********************************************************/
            /* 24bpp needs an inner function rather than a table.    */
            /*********************************************************/
            SPad.pfnRGBToIndex = RGBToIndexFunction();
        }
        else
        {
            /**********************************************************/
            /* We can not use the normal conversion table set up      */
            /* function if a palette is being used because it would   */
            /* set up the table as if the destination were a bitmap   */
            /* (where the physical indexes are indexes into the       */
            /* logical palette) instead of as if the destination were */
            /* the screen (where the physical indexes are indexes     */
            /* into the HW palette) as is always the case with the    */
            /* DrawBits function.                                     */
            /**********************************************************/
            if (pdc->DCIColFormat == LCOLF_PALETTE)
            {
                if (flOptions & BBO_PAL_COLORS)
                {
                    /**************************************************/
                    /* Create the conversion table using the mappings */
                    /* between the logical palette and the external   */
                    /* color table.                                   */
                    /**************************************************/
                    ulError = GetConvertTableForBBO_PAL_COLORS();  /*72854 TAV*/
                    if (ulError)                                   /*72854 TAV*/
                        goto DRAWBITS_LOGERR_EXIT;                 /*72854 TAV*/
                }
                /******************************************************/
                /* Defect 53775 - We need a bit more checking before  */
                /* we use this conversion routine. We must make sure  */
                /* that we are drawing to the screen before using     */
                /* GetConvertTableForScreenPalette(). If we are       */
                /* drawing to memory then we must use TableExtToInt.  */
                /*                                                    */
                /******************************************************/
                else if (pdc->DCISelListEntry == &DirectListEntry)
                {
                    /**************************************************/
                    /* Create the conversion table using the mappings */
                    /* in the palette itself and a nearest color      */
                    /* search using the colors in the external color  */
                    /* table bitmap info.                             */
                    /**************************************************/
                    GetConvertTableForScreenPalette();
                }
                else
                {
                   /***************************************************/
                   /* Create the conversion table in the normal way.  */
                   /***************************************************/
                   TableExtToInt();
                }

            }
            else
            {
                /******************************************************/
                /* Create the conversion table in the normal way.     */
                /******************************************************/
                TableExtToInt();
            }
        }
    }

    /******************************************************************/
    /* Now that the conversion routines are set up we can set the     */
    /* DrawBits bitmap header bitcount to be that required by         */
    /* PixbltThroughClipsViaPhunk. ie the format the bitmap will be   */
    /* in after the conversion.                                       */
    /******************************************************************/
    DrawBitsBMHeader.Info.BitCount = (SHORT)AIxfer.pbmhDest->Info.BitCount;

    /******************************************************************/
    /* If we are using a software cursor and we are in hardware       */
    /* drawing mode then we must exclude the cursor and disable       */
    /* further cursor drawing until we have finished with the         */
    /* hardware.                                                      */
    /******************************************************************/
    if ( (cursor_data.cursor_status & CURSOR_SOFTWARE) &&
         !softDrawInUse )
    {
        /**************************************************************/
        /* Exclude the cursor from the target rectangle.              */
        /**************************************************************/
        eddm_ExcludeCursor((pDevPoint)&AIxfer.rcsTrg, COORD_AI);
    }


    /******************************************************************/
    /* We have now done all the necessary set up. Now we will call    */
    /* PixBltThroughClipsViaPHUNK to handle the clip rectangles and   */
    /* the potentially huge/giant external source.                    */
    /******************************************************************/
    PixBltThroughClipsViaPHUNK();

    /******************************************************************/
    /* Reset the colour compare condition to the default case as      */
    /* all other code expects it to be unchanged.                     */
    /******************************************************************/
    #ifndef   _8514
    ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
    #else
    Shadow8514Regs.Mode.PatternSrc = MD_PS_ONES;
    Shadow8514Regs.Mode.UnderPaint = MD_UP_FALSE;
    Shadow8514Regs.Mode.PlaneMode  = MD_AF_W_NORMAL;
    #endif

    /******************************************************************/
    /* Release driver semaphore and return with the correlation       */
    /* result. (This will be "OK" if we are not doing correlation).   */
    /******************************************************************/
DRAWBITS_PASS_EXIT:
    reenable_cursor();
    ExitDriver(pdcArg, FunN, EDF_STANDARD);
    return(ulCorrelationResult);

    /******************************************************************/
    /* Release driver semaphore and return with error code.           */
    /******************************************************************/
DRAWBITS_LOGERR_EXIT:
    LOGERR("DrawBits", "DrawBits error", FNULL, 0, ulError);
DRAWBITS_ERR_EXIT:
    reenable_cursor();
    ExitDriver(pdcArg, FunN, EDF_STANDARD);
    return(ERROR_ZERO);
}



/**********************************************************************/
/* This function looks at the formats of the extarnal and internal    */
/* bitmaps information passed to it and returns TRUE if DrawBits will */
/* have to return the call for simulation.  The formats conversions   */
/* supported by the DrawBits function are:                            */
/*      - 8  bpp ext to 8  bpp int                                    */
/*      - 8  bpp ext to 16 bpp int                                    */
/*      - 24 bpp ext to 8  bpp int                                    */
/*      - 24 bpp ext to 16 bpp int                                    */
/*      - RLE equivalents of the above as long as the rop is a source */
/*        to destination copy and the background mix BM_SRCTRANSPARENT*/
/*        is not being used.                                          */
/**********************************************************************/
BOOL    NotWithTheseFormats(PBITMAPINFO2   pbmiInfoTable,
                            pBitmapHeader  pbmhBMHeader,
                            USHORT         BackgroundMix,
                            ULONG          lRop)
{
    /******************************************************************/
    /* First check the internal format is either 8 or 16 bits per pel.*/
    /******************************************************************/
    if (  (pbmhBMHeader->Info.BitCount != 16)
       && (pbmhBMHeader->Info.BitCount !=  8))
    {
        return(TRUE);
    }

    /******************************************************************/
    /* Now we must check the external format. We must distinguish     */
    /* between whether we have been passed a BITMAPINFO or a          */
    /* BITMAPINFO2 structure.                                         */
    /******************************************************************/

    if (pbmiInfoTable->cbFix == sizeof(BITMAPINFOHEADER))
    {
        /**************************************************************/
        /* We have been passed a BITMAPINFO structure.                */
        /**************************************************************/

        /**************************************************************/
        /* Check the external format is either 8 or 24 bits per pel.  */
        /**************************************************************/
        if (  ( ((PBITMAPINFO)pbmiInfoTable)->cBitCount != 24)
           && ( ((PBITMAPINFO)pbmiInfoTable)->cBitCount !=  8))
        {
            return(TRUE);
        }

        /**************************************************************/
        /* Check that the number of planes of the external bitmap is  */
        /* correct (ie.  that it is one).                             */
        /**************************************************************/
        if ( ((PBITMAPINFO)pbmiInfoTable)->cPlanes != 1)
        {
            return(TRUE);
        }
    }
    else
    {
        /**************************************************************/
        /* We have been passed a BITMAPINFO2 structure.               */
        /**************************************************************/

        /**************************************************************/
        /* Check the external format is either 8 or 24 bits per pel.  */
        /**************************************************************/
        if (  (pbmiInfoTable->cBitCount != 24)
           && (pbmiInfoTable->cBitCount !=  8))
        {
            return(TRUE);
        }

        /**************************************************************/
        /* Check that the number of planes of the external bitmap is  */
        /* correct (ie.  that it is one).                             */
        /**************************************************************/
        if (pbmiInfoTable->cPlanes != 1)
        {
            return(TRUE);
        }

        /**************************************************************/
        /* Check that if compression is used it is either BCA_RLE8 or */
        /* BCA_RLE24, and that the ROP and background mix are         */
        /* acceptable for RLE decoding routines.                      */
        /**************************************************************/
        if (pbmiInfoTable->ulCompression != NULL)
        {
            if ((pbmiInfoTable->ulCompression != BCA_RLE8) &&
                (pbmiInfoTable->ulCompression != BCA_RLE24))
            {
                return(TRUE);
            }
            if ( (lRop != ROP_SRCCOPY) ||
                 UseSoftwareMix(BackgroundMix) )
            {
                return(TRUE);
            }
        }
    }

    /******************************************************************/
    /* We can handle this format conversion in the DrawBits direct    */
    /* path without the need for simulation so return FALSE.          */
    /******************************************************************/
    return(FALSE);

}


void CopyExternalChunkToPhunkForPixBltSource( POINTS ptsChunkPos )
{
    PBYTE   pbInternalRowStart;
    PBYTE   pbExternalRowStart;

    /******************************************************************/
    /* The calling function has set up the source rectangle in        */
    /* AIxfer.rcsSrc and the PHUNK bitmap header.  AIxfer.pbmhSrc now */
    /* points to the Phunks bitmap header so that the low level blt   */
    /* function which will be called off to later will treat the      */
    /* phunk as its source.  The real (external) source bitmap header */
    /* is always DrawBitsBMHeader for this function to have been      */
    /* called.  We must load the PHUNK with the chunk of the external */
    /* source that the source rect corresponds to.  The source rect   */
    /* position is passed to us in the ptsChunkPos parameter.  If the */
    /* external source is not in RLE format then we will use the same */
    /* functions used by Get/SetBitmapBits to do the conversions of   */
    /* each line of data as we need it.                               */
    /******************************************************************/

    /******************************************************************/
    /* RLE formats are copied into the PHUNK in the following manner: */
    /*                                                                */
    /* When an RLE bitmap is not 'complete' (ie it contains some      */
    /* delta, end-of-line or end-of-bitmap records) the destination   */
    /* needs to be copied into the phunk before decoding the external */
    /* bitmap so that the non-specified areas will contain the        */
    /* original contents of the destination.                          */
    /*                                                                */
    /* Initially the code assumes that the bitmap will be 'complete', */
    /* and the destination is not copied to the Phunk.  If a          */
    /* delta-type record is encountered the current Chunk is          */
    /* re-started (ie the destination is copied into the PHUNK and    */
    /* the external bitmap is decoded into the phunk from the start   */
    /* of the chunk).  All chunks remaining in the current bitmap     */
    /* then have the destination copied to the PHUNK before decoding. */
    /*                                                                */
    /* This scheme means that bitmaps that do not require the         */
    /* destination loaded into the phunk, will not waste time doing   */
    /* so.  Bitmaps that do require this will decode some data twice. */
    /* Assuming that the first delta record will occur early in the   */
    /* definition means that this should not be many records          */
    /*                                                                */
    /* This scheme was chosen so that fully specified RLE bitmaps     */
    /* do not waste time copying the destination into the PHUNK. For  */
    /* non fully specified RLE bitmaps we expect there to be a high   */
    /* proportion of delta-type records. This means that we will      */
    /* probably not have decoded much of the bitmap before we hit the */
    /* first delta-type record and have to start the chunk again.     */
    /*                                                                */
    /* The RLE bitmap has to be decoded for every chunk.  To save     */
    /* some decoding time, the last position decoded is maintained in */
    /* the SPad.  If this position is not past the beginning of the   */
    /* current chunk then we can continue decoding from it, otherwise */
    /* we will have to start decoding again from the beginning of the */
    /* bitmap.                                                        */
    /*                                                                */
    /* The actual decoding works as follows:                          */
    /*                                                                */
    /* SPad.pbSrcPointer is used to point to the current position     */
    /* within the RLE bitmap records.  SPad.pbTrgPointer points to    */
    /* the current position within the internal bitmap (the Phunk).   */
    /* SPad.ulState indicates the current state of the decoding. It   */
    /* indicates where we are in relation to the current chunk.       */
    /* (above, below, left, right or inside).                         */
    /*                                                                */
    /* An encoding of a run-length is handled by:                     */
    /*  -   converting the colour to the internal format              */
    /*  -   calculating the end position which doesnot change the     */
    /*      current state.                                            */
    /*  -   setting all colours from the current position to the      */
    /*      end position to the converted colour if we are in the     */
    /*      chunk                                                     */
    /*  -   skipping over the pels if we are outside the chunk.       */
    /*  -   updating the state, and repeating for remaining pels      */
    /*                                                                */
    /* An Unenconded run is handled by:                               */
    /*  -   calculating the end position which doesnot change the     */
    /*      current state.                                            */
    /* -    using the conversion routines to convert the pels if we   */
    /*      are inside the chunk.                                     */
    /* -    skipping over the pels if we are outside the chunk        */
    /* -    updating the state and repeating for remaining pels       */
    /*                                                                */
    /* A delta record is handled by:                                  */
    /*  -   restarting this chunk if it is the first delta-type       */
    /*      record of the bitmap (see explanation above).             */
    /*  -   calculating the new target position.                      */
    /*                                                                */
    /* An end-of-line record is handled by:                           */
    /*  -   restarting this chunk if it is the first delta-type       */
    /*      record of the bitmap (see explanation above).             */
    /*  -   moving the traget position to the start of the next line. */
    /*                                                                */
    /* An end-of-bitmap record is handled by:                         */
    /*  -   restarting this chunk if it is the first delta-type       */
    /*      record of the bitmap (see explanation above).             */
    /*  -   finishing the chunk.                                      */
    /******************************************************************/
#ifdef FIREWALLS
     /**************************************************************/
     /* This routine is only designed to work with 8bpp or 16bpp   */
     /* internal, and 8bpp or 24bpp external formats.  Other       */
     /* formats could require starting part-way through a byte and */
     /* should have been passed back to the engine earlier.        */
     /**************************************************************/
     if ( (  (SPad.pbhInternalBitmap->Info.BitCount != 8) &&
             (SPad.pbhInternalBitmap->Info.BitCount != 16) ) ||
             (SPad.cExternalBitCount != 8) &&
             (SPad.cExternalBitCount != 24) )
     {
         DebugOutput("Bitcounts not handled by drawbits!\r\n");
         haltproc();
     }
#endif

    /******************************************************************/
    /* calculate the number of pels wide and lines high the chunk is. */
    /******************************************************************/
    SPad.ulLinesInChunk    = (ULONG)(AIxfer.rcsSrc.pts2.y -
                                     AIxfer.rcsSrc.pts1.y) + 1;
    SPad.ulPelsOnChunkLine = (ULONG)(AIxfer.rcsSrc.pts2.x -
                                     AIxfer.rcsSrc.pts1.x) + 1;

    /******************************************************************/
    /* Set the internal bitmap to be the PHUNK.                       */
    /******************************************************************/
    SPad.pbhInternalBitmap = AIxfer.pbmhSrc;

    DEBUG("SPAD Lines in chunk", SPad.ulLinesInChunk);
    DEBUG("SPAD Pels on chunk line", SPad.ulPelsOnChunkLine);

    /******************************************************************/
    /* Store the values needed in the scratch pad later.              */
    /* Note : ulChunkPos points to the bottom left hand corner.       */
    /*        ptsChunkPos points to the top right.                    */
    /******************************************************************/
    SPad.ulInternalBytesPerLine = SPad.pbhInternalBitmap->BytesPerLine;
    SPad.ulChunkPosX            = (ULONG)ptsChunkPos.x;
    SPad.ulChunkPosY            = SPad.ulExternalCy -
                                  (ULONG)ptsChunkPos.y -
                                    SPad.ulLinesInChunk + 1;

    DEBUG("Original chunk pos Y", ptsChunkPos.y);
    DEBUG("SPAD chunk pos X", SPad.ulChunkPosX);
    DEBUG("SPAD chunk pos Y", SPad.ulChunkPosY);
    DEBUG("SPAD ulInternalBytesPerLine", SPad.ulInternalBytesPerLine);

    /******************************************************************/
    /* At this point we must set the PhunkBMHeader HWFormat correclty.*/
    /* It will currently contain the specail value we stored in the   */
    /* DrawBitBMHeader to indicate what format the external bitmap    */
    /* was in.                                                        */
    /******************************************************************/
#ifdef _8514
#ifndef  S3
        PhunkBMHeader.Info.HWFormat = EIGHT_BPP;
#else
    if (PhunkBMHeader.Info.BitCount == 8)
    {
        PhunkBMHeader.Info.HWFormat = EIGHT_BPP | MOTOROLA;
    }
    else if (PhunkBMHeader.Info.BitCount == 16)
    {
        PhunkBMHeader.Info.HWFormat = SIXTEEN_BPP | MOTOROLA;
    }
    else if (PhunkBMHeader.Info.BitCount == 24)
    {
        PhunkBMHeader.Info.HWFormat = TWENTYFOUR_BPP | MOTOROLA;
    }
#endif
#else /* ~_8514 */
    if (PhunkBMHeader.Info.BitCount == 8)
    {
        PhunkBMHeader.Info.HWFormat = EIGHT_BPP | MOTOROLA;
    }
    else
    {
        PhunkBMHeader.Info.HWFormat = SIXTEEN_BPP | MOTOROLA;
    }
#endif /* ~_8514 */
    /******************************************************************/
    /* Check the DrawBitBMHeader to see if the external bitmap is     */
    /* in RLE format. The format should be either null, BCA_RLE8, or  */
    /* BCA_RLE24.                                                     */
    /******************************************************************/
    if (DrawBitsBMHeader.Info.HWFormat ==  NULL)
    {
        /**************************************************************/
        /* The external bitmap is not in a compressed format.         */
        /* Copy the required area of the external source into the     */
        /* Phunk, using the conversion routine to convert to internal */
        /* format.                                                    */
        /**************************************************************/

        /**************************************************************/
        /* Initialize the external pointer to the start of the        */
        /* external bitmap bits for the first line within this chunk. */
        /* (external bitmaps stored with bottom row first, so we      */
        /* have to calculate our position from the last row)          */
        /**************************************************************/
        pbExternalRowStart  = SPad.pbExternalBitmap +
                              SPad.ulExternalBytesPerLine *
                                SPad.ulChunkPosY;

        /**************************************************************/
        /* Initialise the internal pointer to the start of the first  */
        /* line required in the internal bitmap bits.                 */
        /**************************************************************/
        pbInternalRowStart = SPad.pbhInternalBitmap->Bitmap +
                             SPad.ulInternalBytesPerLine *
                                 (SPad.ulLinesInChunk - 1);

        /**************************************************************/
        /* Adjust RowStart position to start at the left hand edge of */
        /* the chunk being copied.  (assuming bicount is a multiple   */
        /* of 8)                                                      */
        /**************************************************************/
        pbExternalRowStart += (SPad.ulChunkPosX + AIxfer.rcsSrc.pts1.x) *
                              (SPad.cExternalBitCount >> 3);

        /**************************************************************/
        /* Set up the width of the line according to size of the      */
        /* chunk.                                                     */
        /**************************************************************/
        SPad.cx = SPad.ulPelsOnChunkLine;

        /**************************************************************/
        /* for each of the rows to be converted                       */
        /**************************************************************/
        while(SPad.ulLinesInChunk--)
        {
            /**********************************************************/
            /* Initialise internal and external pointers to the       */
            /* required position within each row                      */
            /**********************************************************/
            SPad.pbSrcPointer = pbExternalRowStart;
            SPad.pbTrgPointer = pbInternalRowStart;

#ifdef FIREWALLS

            /**********************************************************/
            /* Check that both pointers are within the bitmaps.       */
            /**********************************************************/
            TARGET_CHECK(SPad.pbTrgPointer,
                         SPad.pbTrgPointer + SPad.ulInternalBytesPerLine);

            if ( (SPad.pbSrcPointer < SPad.pbExternalBitmap)           ||
                 (SPad.pbSrcPointer > SPad.pbExternalEnd) )
            {
                DEBUG("ERROR Invalid source pointer", SPad.pbSrcPointer);
                DEBUG("  SPAD Source pointer", SPad.pbSrcPointer);
                DEBUG("  AIXFER source pointer", AIxfer.pbmhSrc->Bitmap);
                DEBUG("  Source end", SPad.pbExternalEnd);
                DEBUG("  Line number (within chunk)", SPad.ulLinesInChunk);
                haltproc();
                break;
            }
#endif
            /**********************************************************/
            /* Call the specialist routine to process this scanline.  */
            /**********************************************************/
            SPad.pfnvConvertLine();

            /**********************************************************/
            /* Move on a row in each bitmap.                          */
            /**********************************************************/
            pbExternalRowStart += SPad.ulExternalBytesPerLine;
            pbInternalRowStart -= SPad.ulInternalBytesPerLine;

        } /* for each row of the bitmap */

    }
    else
    {
        /**************************************************************/
        /* The external bitmap header is in RLE format.               */
        /**************************************************************/
        ConvertExternalRLE();
    }


    /******************************************************************/
    /* Set the phunk bitmap header fields for the source dest copy    */
    /******************************************************************/
    AIxfer.pbmhSrc->Info.HWFormat = AIxfer.pbmhDest->Info.HWFormat;

}

/**********************************************************************/
/* DrawBitsRLESetup()                                                 */
/*                                                                    */
/* This routine is called by the DrawBits set-up code when an RLE     */
/* conversion is required. The following variables are initialised:   */
/*                                                                    */
/*      -   SPad.pbSrcPointer                                         */
/*              Points to the start of the last accessed RLE record.  */
/*      -   SPad.ulRLE_last_x                                         */
/*              Stores the x-coordinate for the start of the last     */
/*              accessed RLE record.                                  */
/*      -   SPad.ulRLE_last_y                                         */
/*              Stores the y-coordinate for the start of the last     */
/*              accessed RLE record.                                  */
/*      -   SPad.fRLE_delta                                           */
/*              Indicates if a delta record has been encountered      */
/*              yet.                                                  */
/*      -   SPad.pfnvConvertColour                                    */
/*              Points to function for reading a source pel and       */
/*              converting to the required internal format.           */
/*      -   SPad.cExternalBytesPerPel                              */
/*              The number of bytes for the colour information in     */
/*              the external bitmap (ie 3 for 24bpp, 1 for 8bpp)      */
/**********************************************************************/
void DrawBitsRLESetUp( void )
{
    /******************************************************************/
    /* Set initial values                                             */
    /******************************************************************/
    SPad.pbSrcPointer   = SPad.pbExternalBitmap;
    SPad.ulRLE_last_x   = 0;
    SPad.ulRLE_last_y   = 0xffffffff;
    SPad.fRLE_delta     = FALSE;

    /******************************************************************/
    /* Set up function to convert external colour value to internal   */
    /* colour value.                                                  */
    /******************************************************************/
    if (SPad.fConvertPal)
    {
        if (SPad.cExternalBitCount == 24)
        {
            /**************************************************************/
            /* 24bpp external colour to either 8pp or 16bpp internal.     */
            /**************************************************************/
            SPad.pfnvConvertColour = ConvColour24;
            SPad.cExternalBytesPerPel = 3;
        }
        else
        {
            if (SPad.cInternalBitCount == 8)
            {
                /**********************************************************/
                /* 8bpp external colour to 8bpp internal colour.          */
                /**********************************************************/
                SPad.pfnvConvertColour = ConvColour8to8;
            }
            else
            {
                /**********************************************************/
                /* 8bpp external colour to 16bpp internal colour.         */
                /**********************************************************/
                SPad.pfnvConvertColour = ConvColour8to16;
            }
            SPad.cExternalBytesPerPel = 1;
        }
    }
    else
    {
        /**************************************************************/
        /* No colour conversion required - must be 8bpp to 8bpp with  */
        /* BBO_PAL_CONVERT option.                                    */
        /**************************************************************/
        SPad.pfnvConvertColour = CopyColour8to8;
        SPad.cExternalBytesPerPel = 1;
    }
}


/**********************************************************************/
/* ConvertExternalRLE()                                               */
/*                                                                    */
/* Converts a chunk of the external RLE bitmap into the phunk.        */
/**********************************************************************/
void  ConvertExternalRLE ( void )
{
    pBitmapHeader   pbmhTemp;
    RECTS           rcsTemp;

    BYTE            SavedFgMix, SavedBgMix;
    BYTE            SavedColCompCond;

#ifdef XGADEBUG
    PBYTE   tempTrg;
#endif


    /******************************************************************/
    /* Calculate end of chunk as used several times later.            */
    /******************************************************************/
    SPad.ulChunkEndX = SPad.ulChunkPosX + SPad.ulPelsOnChunkLine - 1;
    SPad.ulChunkEndY = SPad.ulChunkPosY + SPad.ulLinesInChunk - 1;

    DEBUG("SPad.ulChunkEndX", SPad.ulChunkEndX);
    DEBUG("SPad.ulChunkEndY", SPad.ulChunkEndY);

    /******************************************************************/
    /* If we have previously encountered a delta record then we need  */
    /* to copy the destination to the phunk first.  This is because   */
    /* the RLE encoded bitmap is not complete and the destination in  */
    /* the unchanged parts needs to be preserved.                     */
    /******************************************************************/
restart_chunk:
    if (SPad.fRLE_delta)
    {
        /**************************************************************/
        /* Copy Destination to Phunk.                                 */
        /**************************************************************/
#ifdef XGADEBUG
        DebugOutput("About to copy destination to PHUNK!!\r\n");
        haltproc();
#endif

        /**************************************************************/
        /* Set up XGA registers (save copies to restore later)        */
        /**************************************************************/
   // @DMS use utility !!!

   #ifndef   _8514
        SavedFgMix       = ShadowXGARegs.FgMix;
        SavedBgMix       = ShadowXGARegs.BgMix;
        SavedColCompCond = ShadowXGARegs.ColCompCond;

        ShadowXGARegs.FgMix = HWMIX_SOURCE;
        ShadowXGARegs.BgMix = HWMIX_SOURCE;
        ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;
   #else
        SavedFgMix       = Shadow8514Regs.Function_1.Mix;
        SavedBgMix       = Shadow8514Regs.Function_0.Mix;
        SavedColCompCond = Shadow8514Regs.Color_Comp;

        Shadow8514Regs.Function_1.Mix = FUNC_S;
        Shadow8514Regs.Function_0.Mix = FUNC_S;
        Shadow8514Regs.Color_Comp = COLCOMP_ALWAYS;
   #endif

        /**************************************************************/
        /* Set up blt from destination to PHUNK. (ie swap over source */
        /* and destination bitmaps and rectangles).                   */
        /**************************************************************/
        pbmhTemp        = AIxfer.pbmhSrc;
        AIxfer.pbmhSrc  = AIxfer.pbmhDest;
        AIxfer.pbmhDest = pbmhTemp;

        rcsTemp       = AIxfer.rcsSrc;
        AIxfer.rcsSrc = AIxfer.rcsTrg;
        AIxfer.rcsTrg = rcsTemp;

        SimpleSourceDestPixBlt();

        /**************************************************************/
        /* Restore registers and bitmaps.                             */
        /**************************************************************/

        AIxfer.pbmhDest = AIxfer.pbmhSrc;
        AIxfer.pbmhSrc  = pbmhTemp;

        AIxfer.rcsTrg  = AIxfer.rcsSrc;
        AIxfer.rcsSrc  = rcsTemp;

   #ifndef   _8514
        ShadowXGARegs.FgMix         = SavedFgMix;
        ShadowXGARegs.BgMix         = SavedBgMix;
        ShadowXGARegs.ColCompCond   = SavedColCompCond;
   #else
        Shadow8514Regs.Function_1.Mix       = SavedFgMix;
        Shadow8514Regs.Function_0.Mix       = SavedBgMix;
        Shadow8514Regs.Color_Comp         = SavedColCompCond;
   #endif
    }

    /******************************************************************/
    /* Work out where to start in the RLE bitmap.  If the last known  */
    /* position has not yet reached this chunk, we can start there,   */
    /* otherwise we will have to start at the beginning again.        */
    /******************************************************************/
    if ( (SPad.ulRLE_last_y  > SPad.ulChunkPosY) ||
         ( (SPad.ulRLE_last_y == SPad.ulChunkPosY) &&
           (SPad.ulRLE_last_x > SPad.ulChunkPosX) ) )
    {
        /**************************************************************/
        /* Start at beginning of RLE bitmap.                          */
        /**************************************************************/
        DebugOutput("Starting at beginning...\r\n");
        SPad.pbRLE_last_pos = SPad.pbExternalBitmap;
        SPad.ulRLE_last_x   = 0;
        SPad.ulRLE_last_y   = 0;
    }

    SPad.ulCurrentX      = SPad.ulRLE_last_x;
    SPad.ulCurrentY      = SPad.ulRLE_last_y;
    SPad.pbSrcPointer    = SPad.pbRLE_last_pos;

    DEBUG("Starting from SPad.ulCurrentX", SPad.ulCurrentX);
    DEBUG("              SPad.ulCurrentY", SPad.ulCurrentY);
    DEBUG("            SPad.pbRLE_last_POS", SPad.pbRLE_last_pos);

    /******************************************************************/
    /* Set the current state - where are we in relation to the chunk  */
    /******************************************************************/
    if (SPad.ulCurrentY < SPad.ulChunkPosY)
        SPad.ulState = RLE_RIGHT_OR_BELOW_CHUNK;
    else if (SPad.ulCurrentY > SPad.ulChunkEndY)
        SPad.ulState = RLE_ABOVE_CHUNK;
    else if (SPad.ulCurrentX < SPad.ulChunkPosX)
        SPad.ulState = RLE_LEFT_CHUNK;
    else if (SPad.ulCurrentX > SPad.ulChunkEndX)
        SPad.ulState = RLE_RIGHT_OR_BELOW_CHUNK;
    else
        SPad.ulState = RLE_IN_CHUNK;

    /******************************************************************/
    /* Set target pointer to start of the last line in the Phunk.     */
    /* Note that the target pointer is always adjusted so that it     */
    /* lies within the chunk.  So when for example the current        */
    /* position we are decoding is to the left of the chunk, the      */
    /* target pointer will be pointing to the next line in the chunk  */
    /* ready to recieve the decoded data as soon as we enter the      */
    /* chunk area again.                                              */
    /******************************************************************/
    SPad.pbTrgPointer = AIxfer.pbmhSrc->Bitmap +
                        (SPad.ulInternalBytesPerLine *
                            (SPad.ulLinesInChunk - 1));

    /******************************************************************/
    /* Decode the RLE-bitmap into the PHUNK until a position above    */
    /* the current chunk is reached.  If we encounter a delta-type    */
    /* record, and we have not previously encountered one, we will    */
    /* have to start this chunk again so we can load the destination  */
    /* into the phunk.                                                */
    /******************************************************************/
    while (SPad.ulState != RLE_ABOVE_CHUNK)
    {
        /**************************************************************/
        /* Update the last known position.                            */
        /**************************************************************/
        SPad.ulRLE_last_x   = SPad.ulCurrentX;
        SPad.ulRLE_last_y   = SPad.ulCurrentY;
        SPad.pbRLE_last_pos = SPad.pbSrcPointer;

#ifdef XGADEBUG
        if (SPad.ulState == RLE_IN_CHUNK)
        {
            /**************************************************************/
            /* Check that the target pointer and the position match       */
            /**************************************************************/
            if ( (SPad.ulCurrentY < SPad.ulChunkPosY) ||
                 ( (SPad.ulCurrentY == SPad.ulChunkPosY) &&
                   (SPad.ulCurrentX < SPad.ulChunkPosX) ) )
            {
                /**********************************************************/
                /* Before chunk, set to last line in chunk                */
                /**********************************************************/
                tempTrg = SPad.pbhInternalBitmap->Bitmap +
                      (SPad.ulInternalBytesPerLine * (SPad.ulLinesInChunk - 1));
            }
            else if (SPad.ulCurrentX > SPad.ulChunkEndX)
            {
                /**********************************************************/
                /* To the right of chunk, pointer should be at beggining  */
                /* of next line                                           */
                /**********************************************************/
                tempTrg = SPad.pbhInternalBitmap->Bitmap +
                            (SPad.ulInternalBytesPerLine *
                                (SPad.ulChunkEndY - SPad.ulCurrentY - 1));
            }
            else if (SPad.ulCurrentX < SPad.ulChunkPosX)
            {
                /**********************************************************/
                /* To the left of chunk, pointer should be at beggining   */
                /* of this line                                           */
                /**********************************************************/
                tempTrg = SPad.pbhInternalBitmap->Bitmap +
                            (SPad.ulInternalBytesPerLine *
                                (SPad.ulChunkEndY - SPad.ulCurrentY));
            }
            else
            {
                tempTrg = SPad.pbhInternalBitmap->Bitmap +
                            (SPad.ulInternalBytesPerLine *
                                (SPad.ulChunkEndY - SPad.ulCurrentY)) +
                            ((SPad.ulCurrentX - SPad.ulChunkPosX) *
                                (SPad.cInternalBitCount >> 3));
            }

            if (tempTrg != SPad.pbTrgPointer)
            {
                /**********************************************************/
                /* Not in correct position                                */
                /**********************************************************/
                DEBUG("expected target", tempTrg);
                DEBUG("known target", SPad.pbTrgPointer);
                DEBUG("SPad.ulCurrentX", SPad.ulCurrentX);
                DEBUG("Spad.ulCurrentY", SPad.ulCurrentY);
                haltproc();
                SPad.pbTrgPointer = tempTrg;
            }
        }
#endif

#ifdef XGADEBUG
        if ( (SPad.pbSrcPointer < SPad.pbExternalBitmap)  ||
             (SPad.pbSrcPointer >= SPad.pbExternalEnd) )
        {
            /**********************************************************/
            /* The source pointer is outside the bitmap.              */
            /**********************************************************/
            DEBUG("ERROR - source pointer", SPad.pbSrcPointer);
            haltproc();
            SPad.ulState = RLE_ABOVE_CHUNK;
            break;
        }
#endif

        /**************************************************************/
        /* Decode the current RLE record.                             */
        /**************************************************************/
        if (*(SPad.pbSrcPointer) != (BYTE)0)
        {
            /**********************************************************/
            /* Encoding of a runlength                                */
            /**********************************************************/
            DecodeRLERunLength();
        }
        else if ( *(++SPad.pbSrcPointer) > (BYTE)2)
        {
            /**********************************************************/
            /* Unencoded run                                          */
            /**********************************************************/
            DecodeRLEUnencodedRun();
        }
        else
        {
            /**********************************************************/
            /* Delta, end-of-line or end-of-RLE record                */
            /**********************************************************/


            /**********************************************************/
            /* Ignore end-of-line records that are actually at the    */
            /* end of the line - we don't need to redo the chunk and  */
            /* the positions will already have wrapped to the next    */
            /* line.                                                  */
            /**********************************************************/
            if ( (*SPad.pbSrcPointer == RLE_END_OF_LINE) &&
                 ( (SPad.ulCurrentX == 0) ||
                   (SPad.ulCurrentX == SPad.ulExternalCx) ) )
            {
                /**************************************************/
                /* ignore the record                              */
                /**************************************************/
                SPad.pbSrcPointer++;
                continue;
            }

            /**********************************************************/
            /* Ignore end-of-bitmap record if we are actually at the  */
            /* end of the bitmap!                                     */
            /**********************************************************/
            if ( (*SPad.pbSrcPointer == RLE_END_OF_BITMAP) &&
                 ( (SPad.ulCurrentX == SPad.ulExternalCx) &&
                   (SPad.ulCurrentY == SPad.ulExternalCy - 1) ) ||
                 ( (SPad.ulCurrentX == 0) &&
                   (SPad.ulCurrentY == SPad.ulExternalCy) ) )
            {
                /******************************************************/
                /* Finished!                                          */
                /******************************************************/
                SPad.ulState = RLE_ABOVE_CHUNK;
                continue;
            }

            DebugOutput("DELTA TYPE RECORD ....");
            if (!SPad.fRLE_delta)
            {
                /******************************************************/
                /* found first delta record of this bitmap - need to  */
                /* re-do this chunk.                                  */
                /******************************************************/
                SPad.fRLE_delta = TRUE;
                DebugOutput("Restarting chunk\r\n");
                goto restart_chunk;
            }

            /**********************************************************/
            /* Which sort of record do we have?                       */
            /**********************************************************/
            switch (*(SPad.pbSrcPointer++))
            {
                case RLE_DELTA:
                    /**************************************************/
                    /* Delta record.                                  */
                    /* Add increments onto current position.          */
                    /**************************************************/
                    DebugOutput("DELTA\r\n");
                    SPad.ulCurrentX += (ULONG)(*(UCHAR*)(SPad.pbSrcPointer++));
                    SPad.ulCurrentY += (ULONG)(*(UCHAR*)(SPad.pbSrcPointer++));
                    break;

                case RLE_END_OF_LINE:
                    /**************************************************/
                    /* End of line record                             */
                    /**************************************************/
                    DebugOutput("END-OF-LINE\r\n");
                    SPad.ulCurrentX = SPad.ulChunkPosX;
                    SPad.ulCurrentY = SPad.ulCurrentY + 1;
                    break;

                case RLE_END_OF_BITMAP:
                    /**************************************************/
                    /* End of bitmap record                           */
                    /**************************************************/
                    DebugOutput("END-OF-BITMAP\r\n");
                    SPad.ulState = RLE_ABOVE_CHUNK;
                    break;

#ifdef FIREWALLS
                default:
                    /**************************************************/
                    /* record was an unencoded run, should not have   */
                    /* made it to here.                               */
                    /**************************************************/
                    haltproc();
                    break;
#endif
            }

            /**********************************************************/
            /* Adjust position                                        */
            /**********************************************************/
            if (SPad.ulState != RLE_ABOVE_CHUNK)
            {
                /******************************************************/
                /* Update current position state.                     */
                /******************************************************/
                if (SPad.ulCurrentY < SPad.ulChunkPosY)
                    SPad.ulState = RLE_RIGHT_OR_BELOW_CHUNK;
                else if (SPad.ulCurrentY > SPad.ulChunkEndY)
                    SPad.ulState = RLE_ABOVE_CHUNK;
                else if (SPad.ulCurrentX < SPad.ulChunkPosX)
                {
                    /**************************************************/
                    /* We must adjust the target pointer to point to  */
                    /* the beggining of the next target line ready    */
                    /* for when we start decoding within the chunk    */
                    /* again. (Remember we may just have moved up     */
                    /* while we were outside the chunk so we must do  */
                    /* this calculation even if we were previously    */
                    /* outside the chunk).                            */
                    /**************************************************/
                    SPad.pbTrgPointer = SPad.pbhInternalBitmap->Bitmap +
                                     (SPad.ulInternalBytesPerLine *
                                     (SPad.ulChunkEndY - SPad.ulCurrentY));
                    SPad.ulState = RLE_LEFT_CHUNK;
                }
                else if (SPad.ulCurrentX > SPad.ulChunkEndX)
                {
                    /**************************************************/
                    /* We must adjust the target pointer to point to  */
                    /* the beggining of the next target line ready    */
                    /* for when we start decoding within the chunk    */
                    /* again. (Remember we may just have moved up     */
                    /* while we were outside the chunk so we must do  */
                    /* this calculation even if we were previously    */
                    /* outside the chunk).                            */
                    /**************************************************/
                    SPad.pbTrgPointer = SPad.pbhInternalBitmap->Bitmap +
                                     (SPad.ulInternalBytesPerLine *
                                     (SPad.ulChunkEndY - SPad.ulCurrentY - 1));
                    SPad.ulState = RLE_RIGHT_OR_BELOW_CHUNK;
                }
                else
                {
                    SPad.ulState = RLE_IN_CHUNK;
                    SPad.pbTrgPointer =
                                SPad.pbhInternalBitmap->Bitmap +
                                (SPad.ulInternalBytesPerLine *
                                    (SPad.ulChunkEndY - SPad.ulCurrentY)) +
                                ((SPad.ulCurrentX - SPad.ulChunkPosX) *
                                    (SPad.cInternalBitCount >> 3));
                }
            }
        } /* DELTA type record */
    }  /* while within chunk */
} /* ConvertExternalRLE */

/**********************************************************************/
/* DecodeRLERunLength                                                 */
/*                                                                    */
/* Decodes a run length from an external RLE bitmap into the internal */
/* bitmap (PHUNK).                                                    */
/*                                                                    */
/**********************************************************************/
void   DecodeRLERunLength( void )
{
    ULONG   ulPels;
    ULONG   ulCount;
    PUSHORT pusTrg;
    ULONG   i;

    /******************************************************************/
    /* Read number of pels in runlength                               */
    /******************************************************************/
    ulCount = (ULONG)(*SPad.pbSrcPointer++);

    /******************************************************************/
    /* Convert colour (stores in SPad.usColour).                      */
    /******************************************************************/
    SPad.pfnvConvertColour();

    while (ulCount)
    {
        /**************************************************************/
        /* Skip over pels if outside chunk, copy in the colour if we  */
        /* are inside.                                                */
        /**************************************************************/
        switch (SPad.ulState)
        {
            case RLE_IN_CHUNK:
                /******************************************************/
                /* Within chunk, copy pels for this line.             */
                /******************************************************/

                /******************************************************/
                /* Note: The code path for runs that end outside the  */
                /* chunk should be taken when the run ends exactly on */
                /* the right hand edge of the chunk.  The other path  */
                /* does not contain code for wrapping the target      */
                /* pointer round.                                     */
                /******************************************************/

                if (SPad.ulChunkEndX - SPad.ulCurrentX  + 1 > ulCount)
                {
                    /**************************************************/
                    /* All pels within chunk, set them all to         */
                    /* required colour.                               */
                    /**************************************************/
                    if (SPad.cInternalBitCount == 8)
                    {
    #ifdef XGADEBUG
                        /**************************************************/
                        /* Check pointer.                                 */
                        /**************************************************/
                        if (ulCount == 0)
                        {
                            DEBUG("COUNT", ulCount);
                            haltproc();
                        }

                        TARGET_CHECK(SPad.pbTrgPointer,
                                     SPad.pbTrgPointer + ulCount);
    #endif
                        /**********************************************/
                        /* Set all pels and update target pointer     */
                        /**********************************************/
                        memset(SPad.pbTrgPointer,
                               (BYTE)SPad.usColour,
                               ulCount);

                        SPad.pbTrgPointer += ulCount;
                    }
                    else
                    {
    #ifdef XGADEBUG
                        /**********************************************/
                        /* Check pointer.                             */
                        /**********************************************/
                        TARGET_CHECK(SPad.pbTrgPointer,
                                     SPad.pbTrgPointer + ulCount * 2);
    #endif
                        /**********************************************/
                        /* Set all pels and update target pointer     */
                        /**********************************************/
                        pusTrg = (PUSHORT)SPad.pbTrgPointer;
                        for (i = 0; i < ulCount; i++)
                            *(pusTrg++) = SPad.usColour;

                        SPad.pbTrgPointer += ulCount * 2;
                    }

                    /**************************************************/
                    /* Adjust Position                                */
                    /**************************************************/
                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;

    #ifdef XGADEBUG
                    if (SPad.ulCurrentX > SPad.ulChunkEndX)
                    {
                        /**********************************************/
                        /* Shouldn't happen in this branch            */
                        /**********************************************/
                        DEBUG("X went past end of chunk", SPad.ulCurrentX);
                        haltproc();
                    }
    #endif
                }
                else
                {
                    /**************************************************/
                    /* Pels end on or beyond chunk, set those inside  */
                    /* to required value.                             */
                    /**************************************************/

                    /**************************************************/
                    /* Calculate number of pels from here to edge of  */
                    /* chunk.                                         */
                    /**************************************************/
                    ulPels = SPad.ulChunkEndX - SPad.ulCurrentX + 1;

                    if (SPad.cInternalBitCount == 8)
                    {
    #ifdef XGADEBUG
                        if (ulPels == 0)
                        {
                            DEBUG("PELS", ulPels);
                            haltproc();
                        }
                        TARGET_CHECK(SPad.pbTrgPointer,
                                     SPad.pbTrgPointer + ulPels);
    #endif
                        /**********************************************/
                        /* Set all pels from here to edge of chunk    */
                        /**********************************************/

                        memset(SPad.pbTrgPointer,
                               (BYTE)SPad.usColour,
                               ulPels);
                    }
                    else
                    {

    #ifdef XGADEBUG
                        TARGET_CHECK(SPad.pbTrgPointer,
                                     SPad.pbTrgPointer + ulPels * 2);
    #endif
                        /**********************************************/
                        /* Set all pels from here to edge of chunk    */
                        /**********************************************/
                        pusTrg = (PUSHORT)SPad.pbTrgPointer;
                        for (i = 0; i < ulPels; i++)
                            *(pusTrg++) = SPad.usColour;
                    }

                    /**************************************************/
                    /* Reset target pointer to beginning of next line */
                    /**************************************************/
                    SPad.pbTrgPointer -= (SPad.ulCurrentX - SPad.ulChunkPosX) *
                                         (SPad.cInternalBitCount >> 3);
                    SPad.pbTrgPointer -= SPad.ulInternalBytesPerLine;

                    /**************************************************/
                    /* Update position                                */
                    /**************************************************/
    #ifdef XGADEBUG
                    if (ulPels > ulCount)
                    {
                        DEBUG("COUNT", ulCount);
                        DEBUG("PELS", ulPels);
                        haltproc();
                        ulPels = ulCount;
                    }
    #endif
                    SPad.ulCurrentX += ulPels;
                    ulCount         -= ulPels;
                    SPad.ulState     = RLE_RIGHT_OR_BELOW_CHUNK;

    #ifdef XGADEBUG
                    /**************************************************/
                    /* This path always wraps target pointer!!!!      */
                    /**************************************************/
                    if (SPad.ulCurrentX <= SPad.ulChunkEndX)
                    {
                        DEBUG("X not far enough to wrap!!", SPad.ulCurrentX);
                        haltproc();
                    }
    #endif

                    /**************************************************/
                    /* Do we need to move onto the next line          */
                    /**************************************************/
                    if (SPad.ulCurrentX > SPad.ulExternalCx)
                    {
                        SPad.ulCurrentX -= SPad.ulExternalCx;
                        /**********************************************/
                        /* Is the start of the line in or out of the  */
                        /* chunk.                                     */
                        /**********************************************/
                        if (SPad.ulCurrentX < SPad.ulChunkPosX)
                        {
                            SPad.ulState = RLE_LEFT_CHUNK;
                        }
                        else
                        {
                            SPad.ulState = RLE_IN_CHUNK;
                        }
                        /**********************************************/
                        /* Have we now gone above the current chunk   */
                        /**********************************************/
                        if (++SPad.ulCurrentY > SPad.ulChunkEndY)
                        {
                            /******************************************/
                            /* Finished this chunk.                   */
                            /******************************************/
                            DEBUG("finished with", ulCount);
                            SPad.ulState = RLE_ABOVE_CHUNK;
                        }
                    }
                }
                break;


            case RLE_LEFT_CHUNK:

                /******************************************************/
                /* Skip over pels on line to the left of chunk.       */
                /******************************************************/
                ulPels = SPad.ulChunkPosX - SPad.ulCurrentX;
                if (ulPels > ulCount)
                {
                    /**************************************************/
                    /* Skip over the pels, they are all to the left   */
                    /* of the chunk.                                  */
                    /**************************************************/
                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;
                }
                else
                {
                    /**************************************************/
                    /* Skip over the pels in this run that are to the */
                    /* left of the chunk.                             */
                    /**************************************************/
                    SPad.ulCurrentX += ulPels;
                    ulCount -= ulPels;
                    SPad.ulState = RLE_IN_CHUNK;
                }
                break;

            case RLE_RIGHT_OR_BELOW_CHUNK:
                /******************************************************/
                /* skip over pels on line to the right of chunk, or   */
                /* those below it.                                    */
                /******************************************************/
                if (SPad.ulExternalCx - SPad.ulCurrentX > ulCount)
                {
                    /**************************************************/
                    /* Skip the remaining pels in this run - they do  */
                    /* not reach the end of the line.                 */
                    /**************************************************/
                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;
                }
                else
                {
                    /**************************************************/
                    /* Skip pels to end of the line.                  */
                    /**************************************************/
                    ulPels = SPad.ulExternalCx - SPad.ulCurrentX;
                    ulCount -= ulPels;

                    /**************************************************/
                    /* Move to next line.                             */
                    /**************************************************/
                    SPad.ulCurrentX = 0;
                    SPad.ulCurrentY++;

                    /**************************************************/
                    /* See if state has changed.                      */
                    /**************************************************/
                    if (SPad.ulCurrentY >= SPad.ulChunkPosY)
                    {
                        /**********************************************/
                        /* No Longer below chunk.                     */
                        /**********************************************/
                        if (SPad.ulCurrentY > SPad.ulChunkEndY)
                        {
                            /******************************************/
                            /* Finished - we're above chunk           */
                            /******************************************/
                            SPad.ulState = RLE_ABOVE_CHUNK;
                        }
                        else if (SPad.ulCurrentX >= SPad.ulChunkPosX)
                        {
                            /******************************************/
                            /* Moved inside chunk                     */
                            /******************************************/
                            SPad.ulState = RLE_IN_CHUNK;
                        }
                        else
                        {
                            /******************************************/
                            /* Moved onto line to left of chunk       */
                            /******************************************/
                            SPad.ulState = RLE_LEFT_CHUNK;
                        }
                    }
                }
                break;

            case RLE_ABOVE_CHUNK:
                /******************************************************/
                /* finished chunk.                                    */
                /******************************************************/
                ulCount = 0;
                break;

#ifdef FIREWALLS
            default:
                DEBUG("Bad RLE state", SPad.ulState);
                haltproc();
                SPad.ulState = RLE_ABOVE_CHUNK;
                break;
#endif
        }
    }
}

/**********************************************************************/
/* DecodeRLEunencodedRun()                                            */
/*                                                                    */
/* Decodes an unencoded run from an external RLE bitmap into the      */
/* internal bitmap (PHUNK).                                           */
/*                                                                    */
/**********************************************************************/
void DecodeRLEUnencodedRun( void )
{
    ULONG   ulCount;
    ULONG   ulAllignAdjust;
    ULONG   ulPels;

    /******************************************************************/
    /* read the number of bytes in the run.                           */
    /******************************************************************/
    ulCount = (ULONG)*(SPad.pbSrcPointer++);

    /******************************************************************/
    /* The run is padded to a word boundary - remember if there was   */
    /* an odd number of pels.                                         */
    /******************************************************************/
//  ulAllignAdjust = ulCount % 2;
    ulAllignAdjust = ulCount & 0x00000001;

    /******************************************************************/
    /* count is the number of bytes. This must be divided by 3 to get */
    /* the number of pels for 24bpp bitmaps.                          */
    /******************************************************************/
    if (SPad.cExternalBitCount == 24)
    {
#ifdef FIREWALLS
        if (ulCount % 3 != 0)
        {
            /**********************************************************/
            /* Error !  - return error in encoding?                   */
            /**********************************************************/
            DEBUG("Count should have divided by 3", ulCount);
            haltproc();
        }
#endif
        ulCount %= 3;
    }


    while (ulCount)
    {
        switch (SPad.ulState)
        {
            case RLE_IN_CHUNK:
                /******************************************************/
                /* Within chunk, convert pels for this line.          */
                /******************************************************/

                /******************************************************/
                /* Note: The code path for unencoded runs that end    */
                /* outside the chunk should be taken when the         */
                /* unencoded run ends exactly on the right hand edge  */
                /* of the chunk.  The other path does not contain     */
                /* code for wrapping the target pointer round.        */
                /******************************************************/
                if (SPad.ulChunkEndX - SPad.ulCurrentX + 1 > ulCount)
                {
                    /**************************************************/
                    /* All pels within chunk, convert them all.       */
                    /**************************************************/
                    SPad.cx = ulCount;
                    SPad.pfnvConvertLine();

                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;

    #ifdef XGADEBUG
                    if (SPad.ulCurrentX > SPad.ulChunkEndX)
                    {
                        DEBUG("X went past end of chunk(2)", SPad.ulCurrentX);
                        haltproc();
                    }
    #endif
                }
                else
                {
                    /**************************************************/
                    /* Pels end on or past end of chunk.  Only copy   */
                    /* the ones in the chunk.                         */
                    /*                                                */
                    /**************************************************/
                    ulPels = SPad.ulChunkEndX - SPad.ulCurrentX + 1;

                    SPad.cx = ulPels;
                    SPad.pfnvConvertLine();

                    /******************************************************/
                    /* Update position                                    */
                    /******************************************************/
    #ifdef DEBUG
                    if (ulPels > ulCount)
                    {
                        DEBUG("COUNT", ulCount);
                        DEBUG("PELS", ulPels);
                        haltproc();
                        ulPels = ulCount;
                    }
    #endif
                    SPad.ulCurrentX += ulPels;
                    ulCount -= ulPels;

                    /******************************************************/
                    /* Reset target pointer to beginning of next line as  */
                    /* this conversion filled to the right hand edge of   */
                    /* the chunk.                                         */
                    /******************************************************/
                    SPad.pbTrgPointer -= (SPad.ulCurrentX - SPad.ulChunkPosX) *
                                         (SPad.cInternalBitCount >> 3);
                    SPad.pbTrgPointer -= SPad.ulInternalBytesPerLine;

                    SPad.ulState = RLE_RIGHT_OR_BELOW_CHUNK;

                    /**************************************************/
                    /* Do we need to move onto the next line          */
                    /**************************************************/
                    if (SPad.ulCurrentX > SPad.ulExternalCx)
                    {
                        SPad.ulCurrentX -= SPad.ulExternalCx;
                        /**********************************************/
                        /* Is the start of the line in or out of the  */
                        /* chunk.                                     */
                        /**********************************************/
                        if (SPad.ulCurrentX < SPad.ulChunkPosX)
                        {
                            SPad.ulState = RLE_LEFT_CHUNK;
                        }
                        else
                        {
                            SPad.ulState = RLE_IN_CHUNK;
                        }
                        /**********************************************/
                        /* Have we now gone above the current chunk   */
                        /**********************************************/
                        if (++SPad.ulCurrentY > SPad.ulChunkEndY)
                        {
                            /******************************************/
                            /* Finished this chunk.                   */
                            /******************************************/
                            DEBUG("finished with", ulCount);
                            SPad.ulState = RLE_ABOVE_CHUNK;
                        }
                    }
                }
                break;

            case RLE_LEFT_CHUNK:
                /******************************************************/
                /* Skip over pels on line to the left of chunk.       */
                /******************************************************/
                ulPels = SPad.ulChunkPosX - SPad.ulCurrentX;
                if (ulPels > ulCount)
                {
                    /**************************************************/
                    /* All the pels are to the left of the chunk.     */
                    /* Skip them.                                     */
                    /**************************************************/
                    SPad.pbSrcPointer += ulCount * SPad.cExternalBytesPerPel;
                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;
                }
                else
                {
                    /**************************************************/
                    /* The pels start to the left of chunk, but end   */
                    /* up within it.  Just skip those to the left.    */
                    /**************************************************/
                    SPad.pbSrcPointer += ulPels * SPad.cExternalBytesPerPel;
                    SPad.ulCurrentX += ulPels;
                    ulCount -= ulPels;
                    SPad.ulState = RLE_IN_CHUNK;
                }
                break;

            case RLE_RIGHT_OR_BELOW_CHUNK:
                /******************************************************/
                /* skip over pels on line to the right or below       */
                /* chunk.                                             */
                /******************************************************/
                if (SPad.ulExternalCx - SPad.ulCurrentX > ulCount)
                {
                    /**************************************************/
                    /* Pels but do not reach the end of line.  Skip   */
                    /* over all of them.                              */
                    /**************************************************/
                    SPad.pbSrcPointer += ulCount * SPad.cExternalBytesPerPel;
                    SPad.ulCurrentX += ulCount;
                    ulCount = 0;
                }
                else
                {
                    /**************************************************/
                    /* Skip over pels until the end of line.          */
                    /**************************************************/
                    ulPels = SPad.ulExternalCx - SPad.ulCurrentX;
                    ulCount -= ulPels;

                    /**************************************************/
                    /* Update position                                */
                    /**************************************************/
                    SPad.pbSrcPointer += ulPels * SPad.cExternalBytesPerPel;
                    SPad.ulCurrentX = 0;
                    SPad.ulCurrentY++;

                    /**************************************************/
                    /* See if state has changed.                      */
                    /**************************************************/
                    if (SPad.ulCurrentY >= SPad.ulChunkPosY)
                    {
                        /**********************************************/
                        /* No Longer below chunk.                     */
                        /**********************************************/
                        if (SPad.ulCurrentY > SPad.ulChunkEndY)
                        {
                            /******************************************/
                            /* Finished - we're above chunk           */
                            /******************************************/
                            SPad.ulState = RLE_ABOVE_CHUNK;
                        }
                        else if (SPad.ulCurrentX >= SPad.ulChunkPosX)
                        {
                            /******************************************/
                            /* Moved inside chunk                     */
                            /******************************************/
                            SPad.ulState = RLE_IN_CHUNK;
                        }
                        else
                        {
                            /******************************************/
                            /* Moved onto line to left of chunk       */
                            /******************************************/
                            SPad.ulState = RLE_LEFT_CHUNK;
                        }
                    }
                }
                break;

            case RLE_ABOVE_CHUNK:
                /******************************************************/
                /* Finished chunk                                     */
                /******************************************************/
                ulCount = 0;
                break;

#ifdef FIREWALLS
            default:
                DEBUG("Bad RLE state", SPad.ulState);
                haltproc();
                SPad.ulState = RLE_ABOVE_CHUNK;
                break;
#endif
        }
    }

    /******************************************************************/
    /* Move the source pointer over the padding.                      */
    /******************************************************************/
     SPad.pbSrcPointer += ulAllignAdjust;
}

/**********************************************************************/
/* The colour conversion functions - convert the colour value pointed */
/* to by SPad.pbSrcPointer to an internal colour in SPad.usColour.    */
/* Exit with the source pointer pointing to the byte immediatly after */
/* the colour value.                                                  */
/**********************************************************************/
void    ConvColour24 ( void )
{
    RGB2    RGBColour;

    RGBColour.bBlue     = *SPad.pbSrcPointer++;
    RGBColour.bGreen    = *SPad.pbSrcPointer++;
    RGBColour.bRed      = *SPad.pbSrcPointer++;
    RGBColour.fcOptions = 0;

    SPad.usColour = (USHORT)SPad.pfnRGBToIndex(RGBColour);
}

void    ConvColour8to8  ( void )
{
    SPad.usColour = (BYTE)SPad.ausConvertTable[*SPad.pbSrcPointer++];
}

void    CopyColour8to8  ( void )
{
    SPad.usColour = *SPad.pbSrcPointer++;
}

void    ConvColour8to16 ( void )
{
    SPad.usColour = (RGB16)SPad.ausConvertTable[*SPad.pbSrcPointer++];
    /******************************************************************/
    /* Ensure 16bpp value is of the correct format                    */
    /******************************************************************/
    EnsureIntel16bpp(SPad.usColour);
}


/**********************************************************************/
/* This function sets up the conversion table when the BBO_PAL_COLORS */
/* flag has been passed in.  When this flag is set, the external      */
/* color table contains indices into the logical palette.             *
/*                                                                    */
/* If the destination is the screen we must translate each external   */
/* color table value into a logical palette index.  The we translate  */
/* the logical palette index into a HW slot value.                    */
/*                                                                    */
/* If the destination is memory we do not have to perform the last    */
/* step.                                                              */
/*                                                                    */
/**********************************************************************/
ULONG   GetConvertTableForBBO_PAL_COLORS(VOID)                     /*72854 TAV*/
{
   ULONG   i;
   ULONG   ulLogPalIndex;
   PULONG  pExtColorTable;

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


   /******************************************************************/
   /* If the destination is the screen then use the actual HW slots  */
   /* for the mapping.                                               */
   /******************************************************************/
   if(pdc->DCISelListEntry == &DirectListEntry) {

      for (i = 0; i < SPad.usExtTabSize; i++)
      {
         ulLogPalIndex = *pExtColorTable;

         /************************************************************/
         /* 72854 - TAV                                              */
         /* Must check if the index is actually within the scope of  */
         /* the number of Palette entries ... if so, proceed as      */
         /* usual, if not, return an error to the calling program.   */
         /************************************************************/
         if (ulLogPalIndex < (ULONG) pdc->Palette->usCountStored)  /*72854 TAV*/
            SPad.ausConvertTable[i] =
                             pdc->Palette->entries[ulLogPalIndex].bCurrent;
         else                                                      /*72854 TAV*/
            return(PMERR_INCOMPATIBLE_BITMAP);

         pExtColorTable++;
      }

   }
   /******************************************************************/
   /* If the destination is memory then the logical palette slots    */
   /* are sufficient for the mapping.                                */
   /******************************************************************/
   else {

      for (i = 0; i < SPad.usExtTabSize; i++)
      {
         SPad.ausConvertTable[i] = *pExtColorTable;
         pExtColorTable++;
      }

   }

   return(FALSE);                                                  /*72854 TAV*/
}

/**********************************************************************/
/*                                                                    */
/* During a DrawBits call, if the BBO_PAL_COLORS flag is ON, the      */
/* external color table contains indices into the logical palette.    */
/* If the destination DC is memory and the external color table       */
/* does not contain any indirection then we do not need to perform    */
/* any color translation later on in DrawBits.                        */
/*                                                                    */
/* This routine will check to see if the external color table         */
/* specifies any palette indirection.  If not we return FALSE         */
/* indicating that no color translation needs to take place.          */
/* This will speed things up a bit.                                   */
/*                                                                    */
/**********************************************************************/
USHORT PalIndirectionUsed(VOID)
{
   ULONG   i;
   PULONG  pExtColorTable;

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


   for (i = 0; i < SPad.usExtTabSize; i++)
   {
      if( *pExtColorTable != i)
         return(TRUE);
      pExtColorTable++;
   }
   return(FALSE);

}

/**********************************************************************/
/* This function sets up the conversion table when a palette is being */
/* used (but the BBO_PAL_COLORS flag has not been passed in) and the  */
/* destination is the screen.  The indexes in the external bitmap are */
/* indexes into the external bitmap color table.  These must be       */
/* nearest color mapped to the colors available in the palette and    */
/* the converted to physical indexes into the HWPalette using the     */
/* mapping with the logical palette. Note that this function is       */
/* very similar to GetConverTableForPalette in convext.c              */
/**********************************************************************/
VOID    GetConvertTableForScreenPalette(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 )
    {
        /**************************************************************/
        /* The BCE_PALETTE flag is set in the external bitmap. This   */
        /* means that the colors are indexes into the 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] = pdc->Palette->entries[
                                               pExtRGB->bBlue].bCurrent;

            /**********************************************************/
            /* 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] = (USHORT)pdc->Palette->entries[
                              NearestPaletteIndex(*pExtRGB)].bCurrent;

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