/*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.                                */
/*                                                                           */
/*****************************************************************************/
#ifdef DCAF
/**********************************************************************/
/*                                                                    */
/*   Module          = GETSCR.C                                       */
/*                                                                    */
/*   Description     = GetScreenBits Entry Point.                     */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS
#define INCL_DDIMISC
#define INCL_GRE_REGIONS
#define INCL_GRE_SCREEN
#include <eddinclt.h>
#include <memman.h>
#include <eddgextf.h>
#include <eddmextf.h>
#include <eddbcone.h>
#include <eddhcone.h>
#include <hwaccess.h>
#include <seamless.h>

#include <edddtypt.h>
#include <eddhtype.h>
#include <dcaf.h>

extern BOOL             fXgaDead;
extern PBYTE            pPhunkVirt;
extern DDTType          DDT;
extern PPFNL            EnginesDispatchTable;
extern GSB_PB           Spad;
extern ULONG            cFormatTableEntries;
extern SHORT            softDrawInUse;
#ifdef _8514
    extern MM8514Reg        Shadow8514Regs;
#else
    extern MMReg            ShadowXGARegs;
#endif
extern BitmapHeader     DirectListEntry;
extern ULONG            pPhunkPhys;
extern PBYTE            pConvertTable_8int_8ext;
extern PBYTE            pConvertTable_8_4;
extern PBYTE            pConvertTable_4int_4ext;
extern PDEVPAL          hForeGroundPal;
extern PRGB2            DirectDeviceDefaultPalette;

#ifdef SDBM20
#include <eddfcone.h>
#include <eddftype.h>
#ifdef _8514
    extern pMM8514Reg               p8514Regs;
#else
    extern pMMReg                   pRealXGARegs;
    extern pMMReg                   pXGARegs;
#endif
extern ULONG                    MarkerPhys;
extern ULONG                    LinePatternCur ;
extern ULONG                    LinePatternPhys ;
extern ULONG                    LinePatternSys ;
extern ULONG                    MarkerCur ;
extern ULONG                    pCurCacheBasePhy ;
extern ULONG                    pSysCacheStartPhy;
extern drawFunctionsTable       softDrawTable ;
extern drawFunctionsTable       hardDrawTable ;
extern SHORT                    foregroundSession ;
extern pDrawFunctionsTable      pDrawFunctions ;
#endif /* SDBM20 */

#ifdef S3
VOID    compress_row_24_16(VOID);       // Not sure if this routine is necessary?
VOID    compress_row_24_8(VOID);
VOID    compress_row_24_4(VOID);
VOID    compress_row_24_4pl(VOID);
#endif

VOID    compress_row_16_16(VOID);
VOID    compress_row_16_8(VOID);
VOID    compress_row_16_4(VOID);
VOID    compress_row_16_4pl(VOID);

VOID    compress_row_8_8(VOID);
VOID    compress_row_8_4(VOID);
VOID    compress_row_8_4pl(VOID);

VOID    compress_row_4_4(VOID);
VOID    compress_row_4_4pl(VOID);

PBYTE   CreateConvertTable_8int_8ext(VOID);
PBYTE   CreateConvertTable_8int_4ext(VOID);
PBYTE   CreateConvertTable_4int_4ext(VOID);
VOID    FreeConvertTable(PBYTE *ppConvertTable);

ULONG   CompressScreenBits ( HDC    hdc,
                             HRGN   hrgnApp,
                             PBYTE  pDestBuffer,
                             PULONG ulLength,
                             ULONG  flCmd,
                             PDC    pdcArg );

BOOL    DecompressScreenBits( PDC    pdcArg,
                              HRGN   hrgn,
                              PBYTE  pBuffer );
ULONG   CompressRect();
VOID    InitializeCache();

ULONG   ul_8int_8ext_PaletteChangedCount;
ULONG   ul_8int_4ext_PaletteChangedCount;

#ifdef SEAMLESS
extern SEAMLESSDATA     SeamlessData;
#endif /* SEAMLESS */

/**********************************************************************/
/* Define a table of valid source and destination data formats.       */
/* Each entry in this table represents a valid src and dst format     */
/* pairing.                                                           */
/**********************************************************************/
#ifdef S3
    #define NUM_VALID_FORMAT_PAIRS 13
#else
    #define NUM_VALID_FORMAT_PAIRS 9
#endif

VALID_DATA_FORMATS   aGSBValidDataFormats[NUM_VALID_FORMAT_PAIRS] =
{
    /******************************************************************/
    /* 4bpp linear internal -> 4bpp planar external                   */
    /******************************************************************/
    { (GSB_4BPP_LINEAR << 8) | GSB_4BPP_PLANAR,
       (PFN)compress_row_4_4pl,
       &pConvertTable_4int_4ext,
       (PFN)CreateConvertTable_4int_4ext },

    /******************************************************************/
    /* 4bpp linear internal -> 4bpp linear external                   */
    /******************************************************************/
    { (GSB_4BPP_LINEAR << 8) | GSB_4BPP_LINEAR,
       (PFN)compress_row_4_4,
       &pConvertTable_4int_4ext,
       (PFN)CreateConvertTable_4int_4ext },

    /******************************************************************/
    /* 8bpp linear internal -> 4bpp planar external                   */
    /******************************************************************/
    { (GSB_8BPP_LINEAR << 8) | GSB_4BPP_PLANAR,
       (PFN)compress_row_8_4pl,
       &pConvertTable_8_4,
       (PFN)CreateConvertTable_8int_4ext },

    /******************************************************************/
    /* 8bpp linear internal -> 4bpp linear external                   */
    /******************************************************************/
    { (GSB_8BPP_LINEAR << 8) | GSB_4BPP_LINEAR,
       (PFN)compress_row_8_4,
       &pConvertTable_8_4,
       (PFN)CreateConvertTable_8int_4ext },

    /******************************************************************/
    /* 8bpp linear internal -> 8bpp linear external                   */
    /******************************************************************/
    { (GSB_8BPP_LINEAR << 8) | GSB_8BPP_LINEAR,
       (PFN)compress_row_8_8,
       &pConvertTable_8int_8ext,
       (PFN)CreateConvertTable_8int_8ext },

    /******************************************************************/
    /* 16bpp linear internal -> 4bpp planar external                  */
    /******************************************************************/
    { (GSB_16BPP_LINEAR << 8) | GSB_4BPP_PLANAR,
       (PFN)compress_row_16_4pl,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 16bpp linear internal -> 4bpp linear external                  */
    /******************************************************************/
    { (GSB_16BPP_LINEAR << 8) | GSB_4BPP_LINEAR,
       (PFN)compress_row_16_4,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 16bpp linear internal -> 8bpp linear external                  */
    /******************************************************************/
    { (GSB_16BPP_LINEAR << 8) | GSB_8BPP_LINEAR,
       (PFN)compress_row_16_8,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 16bpp linear internal -> 16bpp linear external                 */
    /******************************************************************/
    { (GSB_16BPP_LINEAR << 8) | GSB_16BPP_LINEAR,
       (PFN)compress_row_16_16,
       NULL,
       (PFN)NULL },

#ifdef BPP24
    /******************************************************************/
    /* 24bpp linear internal -> 4bpp planar external                  */
    /******************************************************************/
    { (GSB_24BPP_LINEAR << 8) | GSB_4BPP_PLANAR,
       (PFN)compress_row_24_4pl,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 24bpp linear internal -> 4bpp linear external                  */
    /******************************************************************/
    { (GSB_24BPP_LINEAR << 8) | GSB_4BPP_LINEAR,
       (PFN)compress_row_24_4,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 24bpp linear internal -> 8bpp linear external                  */
    /******************************************************************/
    { (GSB_24BPP_LINEAR << 8) | GSB_8BPP_LINEAR,
       (PFN)compress_row_24_8,
       NULL,
       (PFN)NULL },

    /******************************************************************/
    /* 24bpp linear internal -> 16bpp linear external                 */
    /******************************************************************/
    { (GSB_24BPP_LINEAR << 8) | GSB_16BPP_LINEAR,
       (PFN)compress_row_24_16,
       NULL,
       (PFN)NULL },
#endif

};


/**********************************************************************/
/*                                                                    */
/* GetScreenBits                                                      */
/* -------------                                                      */
/*                                                                    */
/* A region of screen pixel data is saved into the memory provided by */
/* the caller. It is compressed, and can be converted into a format   */
/* suitable for another supported display device and will stop        */
/* either when:                                                       */
/*                                                                    */
/*     - the supplied memory area is full                             */
/*     - the requested region has been returned.                      */
/*                                                                    */
/* The region can be specified as either:                             */
/*                                                                    */
/*     - a pointer to a single rectangle (rcl - long values)          */
/*     - a region handle                                              */
/*                                                                    */
/* setting the GSB_OPT_HRGN flag in the flCmd parameter accordingly.  */
/* If a RECTL is specified then it is assumed to be inclusive.        */
/*                                                                    */
/* The function modifies the supplied rectangle/region to indicate    */
/* the area that was NOT returned in the call. If the whole requested */
/* region was returned the rectangle/region will be a null area.      */
/*                                                                    */
/* The supplied DC must be direct - it is the source of the pixel     */
/* data.                                                              */
/*                                                                    */
/* This is not a drawing primitive, therefore no correlation,         */
/* bounds accumulation or drawing will take place.                    */
/*                                                                    */
/* Returns:                                                           */
/*       1 => entire area was saved in buffer                         */
/*       2 => a subset of the area was saved in buffer                */
/* Error Returns:                                                     */
/*       0                                                            */
/**********************************************************************/

DDIENTRY GetScreenBits( HDC     hdc,
                        HRGN    hrgnApp,
                        PBYTE   pDest,
                        PULONG  pulLength,
                        ULONG   flCmd,
                        PDC     pdcArg,
                        ULONG   FunN )
{
    ULONG   ulErrorCode;
    ULONG   rc;

    /******************************************************************/
    /* Enter Driver.                                                  */
    /******************************************************************/
    EnterDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);

    /******************************************************************/
    /* Perform some initial error checking.                           */
    /******************************************************************/
    if (FunN & COM_PATH)
    {
        ulErrorCode = PMERR_INV_IN_PATH;
        goto gsb_log_err_exit;
    }

    if (FunN & COM_AREA)
    {
        ulErrorCode = PMERR_INV_IN_AREA;
        goto gsb_log_err_exit;
    }

    if (pdc->DCIDCType != OD_DIRECT)
    {
        ulErrorCode = PMERR_INV_DC_TYPE;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Now check the parameters.                                      */
    /* First check that the buffer is not bigger than 64K.            */
    /******************************************************************/
    if ( (*pulLength > 0x10000L) ||
         (*pulLength < MIN_BUFFER_SIZE) )
    {
        ulErrorCode = PMERR_INV_LENGTH_OR_COUNT;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Check the option flags are ones we know about.                 */
    /******************************************************************/
    if (flCmd & ~GSB_OPT_FLAGS)
    {
        ulErrorCode = PMERR_INV_FORMAT_CONTROL;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Check that we are not dead (i.e. not in a full screen session).*/
    /******************************************************************/
    if (fXgaDead)
    {
        ulErrorCode = PMERR_PEL_NOT_AVAILABLE;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Everything looks good - try to go ahead and compress the       */
    /* screen bits.                                                   */
    /******************************************************************/
    rc = CompressScreenBits ( hdc,
                              hrgnApp,
                              pDest,
                              pulLength,
                              flCmd,
                              pdcArg );

    /******************************************************************/
    /* ExitDriver.                                                    */
    /******************************************************************/
    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return(rc);

gsb_log_err_exit:
    LogError(ulErrorCode);

    ExitDriver(pdcArg, FunN, EDF_STANDARD | EDF_DONT_CLEAN);
    return(FALSE);

} /* GetScreenBits */





/**********************************************************************/
/* Define the CompressScreenBits function.                            */
/**********************************************************************/
ULONG   CompressScreenBits ( HDC    hdc,
                             HRGN   hrgnApp,
                             PBYTE  pDestBuffer,
                             PULONG pulBufferLength,
                             ULONG  flCmd,
                             PDC    pdcArg )
{
#define NRECTS 10
    RECTL   rclBound;           /* Bounding RECTL of region being queried */
    RECTL   arclBuffer[NRECTS]; /* Region rectangle buffer                */
    RGNRECT rgnControl;         /* Control struct for regions             */
    BOOL    fOutputBufferFull;  /* Flags whether output buffer is full    */
    ULONG   ulCombinedFormat;   /* Contains the src and dest data formats */
    ULONG   iFormat;
    ULONG   iRect;
    ULONG   cRemainingRectsInBuffer;  /* No. rects in buffer              */
    ULONG   cCompressedRectsInBuffer; /* No. rects compressed in buffer   */
    BOOL    fMoreEngineRects;         /* Flags more rects in engine       */
    ULONG   ulErrorCode;
    ULONG   ulSrcDataFormat;
    BOOL    fCalculateConversionTable;

    /******************************************************************/
    /* Check that the format is valid.                                */
    /* This is done by creating a word containing the internal        */
    /* (screen) format and the external (requested) format.           */
    /* The gsb_formats_table is then scanned for a match.             */
    /* If matched, the format is valid. Otherwise it is invalid.      */
    /******************************************************************/

    /******************************************************************/
    /* Calculate the internal format code.                            */
    /******************************************************************/
    if (DDT.BitCount == 4)
    {
        ulSrcDataFormat = GSB_4BPP_LINEAR;
    }
    else if (DDT.BitCount == 8)
    {
        ulSrcDataFormat = GSB_8BPP_LINEAR;
    }
    else if (DDT.BitCount == 16)
    {
        ulSrcDataFormat = GSB_16BPP_LINEAR;
    }
#ifdef S3
    else /* DDT.BitCount == 24 */
    {
        ulSrcDataFormat = GSB_24BPP_LINEAR;
    }
#endif

    /******************************************************************/
    /* Generate the format code, which consists of the internal       */
    /* (source) format flags in bits 8-15 and the external (requested)*/
    /* format flags in bits 0-7.                                      */
    /******************************************************************/
    ulCombinedFormat = (ulSrcDataFormat << 8) |
                                             (flCmd & GSB_FORMAT_FLAGS);

    /******************************************************************/
    /* Now search for this format code in the table of supported      */
    /* internal/external format combinations.                         */
    /******************************************************************/
    for ( iFormat = 0;
          iFormat < NUM_VALID_FORMAT_PAIRS;
          iFormat++ )
    {
        if (aGSBValidDataFormats[iFormat].ulSrcDstFormat ==
                                                       ulCombinedFormat)
        {
            break;
        }
    }

    /******************************************************************/
    /* If the format code was not found then raise an error.          */
    /******************************************************************/
    if (iFormat >= NUM_VALID_FORMAT_PAIRS)
    {
        ulErrorCode = PMERR_INV_FORMAT_CONTROL;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Fetch the pointer to the compression function for this         */
    /* internal/external format combination.                          */
    /******************************************************************/
    Spad.pfnCompressRow =
                  aGSBValidDataFormats[iFormat].pfnRowConversionRoutine;

    /******************************************************************/
    /* Initialize the conversion cache.                               */
    /* This is currently only required if source is 16bpp and         */
    /* destination is 8bpp or 4bpp.                                   */
#ifdef S3
    /* For S3 adapters, an internal 24bpp format is possible and must */
    /* be handled.                                                    */
#endif
    /******************************************************************/
#ifdef S3
    if ( ( (ulSrcDataFormat == GSB_16BPP_LINEAR) ||
           (ulSrcDataFormat == GSB_24BPP_LINEAR) ) &&
         ( ((flCmd & GSB_BPP_FLAGS) == GSB_OPT_8BPP) ||
           ((flCmd & GSB_BPP_FLAGS) == GSB_OPT_4BPP) ) )
#else
    if ( (ulSrcDataFormat == GSB_16BPP_LINEAR) &&
         ( ((flCmd & GSB_BPP_FLAGS) == GSB_OPT_8BPP) ||
           ((flCmd & GSB_BPP_FLAGS) == GSB_OPT_4BPP) ) )
#endif
    {
        InitializeCache();
    }

    /******************************************************************/
    /* Check whether we need a conversion table for this combination  */
    /* of formats.                                                    */
    /******************************************************************/
    if (aGSBValidDataFormats[iFormat].ppConvertTable != NULL)
    {
        /**************************************************************/
        /* Assume we do need to create a conversion table.            */
        /**************************************************************/
        fCalculateConversionTable = TRUE;

        /**************************************************************/
        /* If the internal format is 8bpp then we need to take        */
        /* special action.                                            */
        /*                                                            */
        /* If the requested format is 8bpp:                           */
        /*   - if the hardware palette is the default, then no        */
        /*     conversion is required.                                */
        /*   - else a palette has been realized and we need to        */
        /*     convert the internal indices into "standard" external  */
        /*     format i.e. indices into the default 256 color XGA     */
        /*     (fudged) palette.                                      */
        /*                                                            */
        /* If a conversion table has been previously created then     */
        /* we need to ensure that the mapping refers to the current   */
        /* palette (the internal palette may have changed since we    */
        /* calculated the conversion table). We can do this because   */
        /* there is a handy global variable that is incremented when  */
        /* the hardware palette is changed. We take a copy of this    */
        /* when the conversion table is created, and on subsequent    */
        /* calls check the value has not changed before using the     */
        /* table. If the value has changed then we throw away the old */
        /* table and create a new one.                                */
        /*                                                            */
        /* If the requested format is 4bpp:                           */
        /*   - we will always need a conversion table, but must       */
        /*     ensure that it correctly maps the current hardware     */
        /*     palette. We use the same scheme as for 8bpp in this    */
        /*     case.                                                  */
        /**************************************************************/
        if (ulSrcDataFormat == GSB_8BPP_LINEAR)
        {
            if ((flCmd & GSB_FORMAT_FLAGS) == GSB_8BPP_LINEAR)
            {
                if (hForeGroundPal ==
                                    (PDEVPAL)DirectDeviceDefaultPalette)
                {
                    /**************************************************/
                    /* This is 8bpp->8bpp and the palette is default. */
                    /* No conversion is required.                     */
                    /* If there is a conversion table then throw it   */
                    /* away.                                          */
                    /**************************************************/
                    if (pConvertTable_8int_8ext != NULL)
                    {
                        FreeConvertTable(&pConvertTable_8int_8ext);
                    }
                    fCalculateConversionTable = FALSE;
                }
                else
                {
                    /**************************************************/
                    /* This is 8bpp->8bpp and the palette is not      */
                    /* default.                                       */
                    /* We need a conversion table.                    */
                    /**************************************************/
                    if (pConvertTable_8int_8ext != NULL)
                    {
                        /**********************************************/
                        /* We already have a conversion table.        */
                        /* If the palette has changed since the       */
                        /* conversion table was created then we       */
                        /* must throw it away and calculate a new one.*/
                        /**********************************************/
                        if (ul_8int_8ext_PaletteChangedCount !=
                                           SeamlessData.ulLastPalUpdate)
                        {
                            FreeConvertTable(&pConvertTable_8int_8ext);
                            ul_8int_8ext_PaletteChangedCount =
                                           SeamlessData.ulLastPalUpdate;
                        }
                        else
                        {
                            /******************************************/
                            /* We can use the conversion table that   */
                            /* we already have.                       */
                            /******************************************/
                            fCalculateConversionTable = FALSE;
                        }
                    }
                }
            }
            else
            {
                /******************************************************/
                /* This is 8bpp->4bpp.                                */
                /* We need to ensure that the conversion table        */
                /* is valid for the current palette.                  */
                /******************************************************/
                if (pConvertTable_8_4 != NULL)
                {
                    /**************************************************/
                    /* We already have a conversion table - ensure    */
                    /* that it is valid.                              */
                    /**************************************************/
                    if (ul_8int_4ext_PaletteChangedCount !=
                                           SeamlessData.ulLastPalUpdate)
                    {
                        FreeConvertTable(&pConvertTable_8_4);
                        ul_8int_4ext_PaletteChangedCount =
                                           SeamlessData.ulLastPalUpdate;
                    }
                    else
                    {
                        /**********************************************/
                        /* We can use the conversion table that       */
                        /* we already have.                           */
                        /**********************************************/
                        fCalculateConversionTable = FALSE;
                    }

                }
            }
        }

        /**************************************************************/
        /* If we need to create a conversion table then               */
        /* see if it already exists - if not then create it.          */
        /* When a conversion table is created we never delete it -    */
        /* as it is likely to be required again.                      */
        /**************************************************************/
        if ( fCalculateConversionTable &&
             (*(aGSBValidDataFormats[iFormat].ppConvertTable) == NULL) )
        {
            /**********************************************************/
            /* Create the required conversion table.                  */
            /**********************************************************/
            *(aGSBValidDataFormats[iFormat].ppConvertTable) =
                (aGSBValidDataFormats[iFormat].pfnCreateConvertTable)();

            /**********************************************************/
            /* Check that the convert table was created.              */
            /**********************************************************/
            if (*(aGSBValidDataFormats[iFormat].ppConvertTable) == NULL)
            {
                goto gsb_err_exit;
            }
        }
    }

    /******************************************************************/
    /* The output buffer is initially empty.                          */
    /******************************************************************/
    fOutputBufferFull = FALSE;

    /******************************************************************/
    /* Initialise the destination pointer to the beginning of the     */
    /* destination buffer.                                            */
    /******************************************************************/
    Spad.pNextFreeDestByte = pDestBuffer;

    /******************************************************************/
    /* Store the screen height and screen bits per pel in the Spad.   */
    /******************************************************************/
    Spad.cyScreenHeight = DDT.ScreenHeight;
    Spad.cScreenBitsPerPel = DDT.BitCount;

    /******************************************************************/
    /* Now we take different action according to whether we have      */
    /* been passed a rectangle or a region.                           */
    /******************************************************************/
    if (flCmd & GSB_OPT_HRGN)
    {
        /**************************************************************/
        /* We've been passed a region handle.                         */
        /* Call back to the engine to get the bounding rectangle.     */
        /**************************************************************/
        if (0 == EnginesDispatchTable[NGreGetRegionBox & 0xff]
                                                  ( hdc,
                                                    hrgnApp,
                                                    &rclBound,
                                                    pdcArg,
                                                    NGreGetRegionBox ) )
        {
            goto gsb_err_exit;
        }

        /**************************************************************/
        /* Call the engine to prime our rectangle buffer with         */
        /* rectangles from the region.                                */
        /**************************************************************/
        rgnControl.ircStart = 1;
        rgnControl.crc = NRECTS;
        rgnControl.ulDirection = RECTDIR_LFRT_TOPBOT;

        if (0 == EnginesDispatchTable[NGreGetRegionRects & 0xff]
                                            ( hdc,
                                              hrgnApp,
                                              &rclBound,
                                              &rgnControl,
                                              arclBuffer,
                                              pdcArg,
                                              NGreGetRegionRects ))
        {
            goto gsb_err_exit;
        }

        /**************************************************************/
        /* Check that there is at least one rectangle to process.     */
        /**************************************************************/
        if (rgnControl.crcReturned == 0)
        {
            goto gsb_no_more_rects_exit;
        }

        /**************************************************************/
        /* Set the flag to indicate whether there are more rects      */
        /* in the engine.                                             */
        /* If there are less rects returned than we asked for then    */
        /* the engine has run out.                                    */
        /* If there are exactly the number of rectangles we asked for */
        /* then there are probably more in the engine.                */
        /* If the engine had exactly the number of rects that we      */
        /* asked for then at this point we will believe that there    */
        /* are more to come. This is not a problem because when       */
        /* we try to refill the buffer we will get zero rects         */
        /* returned and exit gracefully.                              */
        /**************************************************************/
        fMoreEngineRects = (rgnControl.crcReturned == NRECTS);

        cCompressedRectsInBuffer = 0;
        cRemainingRectsInBuffer = rgnControl.crcReturned;
        Spad.prclCurrent = arclBuffer;
    }
    else
    {
        /**************************************************************/
        /* We've been passed a pointer to a rectangle.                */
        /* Copy the rectangle into the local bounding rect, and       */
        /* change it from inclusive to exclusive.                     */
        /**************************************************************/
        rclBound = *((PRECTL)hrgnApp);
        rclBound.xRight++;
        rclBound.yTop++;

        /**************************************************************/
        /* Put the exclusive rectangle coords in the rectangle        */
        /* buffer.                                                    */
        /**************************************************************/
        arclBuffer[0] = rclBound;
        cRemainingRectsInBuffer = 1;
        fMoreEngineRects = FALSE;
        Spad.prclCurrent = arclBuffer;

        /**************************************************************/
        /* Check that the rectangle is well-ordered.                  */
        /**************************************************************/
        if ( (rclBound.xLeft > rclBound.xRight) ||
             (rclBound.yBottom > rclBound.yTop) )
        {
            ulErrorCode = PMERR_INV_IMAGE_DIMENSION;
            goto gsb_log_err_exit;
        }
        /**************************************************************/
        /* Check that the rectangle is not NULL.                      */
        /**************************************************************/
        if ( (rclBound.xLeft == rclBound.xRight) ||
             (rclBound.yBottom == rclBound.yTop) )
        {
            /**********************************************************/
            /* This is a NULL rectangle.                              */
            /* Let's get out of here!                                 */
            /**********************************************************/
            goto gsb_no_more_rects_exit;
        }
    }

    /******************************************************************/
    /* Check that the bounding rectangle does not exceed the size     */
    /* of the screen.                                                 */
    /******************************************************************/
    if ( (rclBound.xRight > (LONG)DDT.ScreenWidth) ||
         (rclBound.yTop   > (LONG)DDT.ScreenHeight) ||
         (rclBound.xLeft  < 0L) ||
         (rclBound.yBottom < 0L) )
    {
        ulErrorCode = PMERR_INV_IMAGE_DIMENSION;
        goto gsb_log_err_exit;
    }

    /******************************************************************/
    /* Calculate the data field size.                                 */
    /* If the data format is 4bpp then we use 8bit fields, otherwise  */
    /* 16bit fields.                                                  */
    /******************************************************************/
    Spad.ulDataFieldSize =
                     ((flCmd & GSB_BPP_FLAGS) == GSB_OPT_4BPP) ? 8 : 16;

    /******************************************************************/
    /* Write the format word into the data header.                    */
    /******************************************************************/
    ((PPACKETHDR)(Spad.pNextFreeDestByte))->usFormat =
                                     (USHORT)(flCmd & GSB_FORMAT_FLAGS);

    /******************************************************************/
    /* Now move the destination pointer past the header, and init the */
    /* free bytes in the buffer.                                      */
    /******************************************************************/
    Spad.pNextFreeDestByte += sizeof(PACKETHDR);
    Spad.cbFreeBytesInDestBuffer = *pulBufferLength - sizeof(PACKETHDR);

    /******************************************************************/
    /* Exclude the cursor from the bounding rectangle.                */
    /* We round the bounding rectangle up to 8 pel boundaries         */
    /* because the rectangles may be rounded up to 2 or 8 pel         */
    /* boundaries below (depending upon bpp).                         */
    /******************************************************************/
    rclBound.xLeft &= ~7L;
    rclBound.xRight = (rclBound.xRight+7) & ~7L;
    eddm_ExcludeCursor((pDevPoint)&rclBound, COORD_DEVICE);

    /******************************************************************/
    /* Now carve up the PHUNK into the buffers we need:               */
    /*    - a transfer buffer, for holding data from VRAM             */
    /*    - two conversion buffers, each capable of holding one       */
    /*      single row (worst case, this will be a whole scanline).   */
    /******************************************************************/
    Spad.pTransferBufferVirt = pPhunkVirt;
    Spad.pTransferBufferPhys = (PBYTE)pPhunkPhys;
    Spad.cbTransferBufferSize = PHUNK_SIZE - (2*MAX_SCANLINE_BYTES);
    Spad.pConvertBuffer1 = pPhunkVirt + Spad.cbTransferBufferSize;
    Spad.pConvertBuffer2 = Spad.pConvertBuffer1 + MAX_SCANLINE_BYTES;

    /******************************************************************/
    /* Set up the drawing mode to always use the real XGA hardware.   */
    /******************************************************************/
    SetDrawModeHard;

    /******************************************************************/
    /* Set up the invariant parts of the parameter block used to      */
    /* copy the screen into the Transfer Buffer (PHUNK).              */
    /******************************************************************/

    /******************************************************************/
    /* Set up screen pixmap values.                                   */
    /******************************************************************/
#ifdef S3
    Shadow8514Regs.PixMapBaseA   = DirectListEntry.BMPhys;
    Shadow8514Regs.PixMapWidthA  = DirectListEntry.Info.HWWidth;
    Shadow8514Regs.PixMapHeightA = DirectListEntry.Info.HWHeight;
    Shadow8514Regs.PixMapFormatA = (BYTE)DirectListEntry.Info.HWFormat;
#else
    ShadowXGARegs.PixMapBaseA   = DirectListEntry.BMPhys;
    ShadowXGARegs.PixMapWidthA  = DirectListEntry.Info.HWWidth;
    ShadowXGARegs.PixMapHeightA = DirectListEntry.Info.HWHeight;
    ShadowXGARegs.PixMapFormatA = (BYTE)DirectListEntry.Info.HWFormat;
#endif

    /******************************************************************/
    /* Set up all registers that don't depend on the rectangle that   */
    /* we are compressing.                                            */
    /******************************************************************/
#ifndef S3
    ShadowXGARegs.FgMix = ShadowXGARegs.BgMix = HWMIX_SOURCE;
    ShadowXGARegs.ColCompCond = COLCOMP_ALWAYS;

    /******************************************************************/
    /* Ensure that the hardware is ready, then transfer some of the   */
    /* registers that we have just set up.                            */
    /* At the moment, we can only transfer MAP_A and COLOR_MIX        */
    /* registers.                                                     */
    /* MAP_B, COORDINATES and PIXELOP will be transferred later       */
    /* (when we know all of the values).                              */
    /******************************************************************/
    WaitForRealHWFunction();
    TransferShadowRegisters(TSR_MAP_A | TSR_COLOUR_MIX);

    /******************************************************************/
    /* Store the pixop in the Spad for the assembly code to pick up.  */
    /******************************************************************/
    Spad.ulXGAPixOp = BACK_SRC_SRC_PIX_MAP |
                      FORE_SRC_SRC_PIX_MAP |
                      STEP_PXBLT |
                      SRC_PIX_MAP_A |
                      DST_PIX_MAP_B |
                      PAT_PIX_MAP_FORE |
                      MASK_PIX_MAP_OFF |
                      DRAW_MODE_DONTCARE |
                      DIR_OCTANT_LRTB;

    /******************************************************************/
    /* Set the format of the Transfer Buffer to be Motorola - the     */
    /* hardware will convert the data on the fly if necessary.        */
    /******************************************************************/
    Spad.ulPixMapFormatB = (BYTE)DirectListEntry.Info.HWFormat |
                                                               MOTOROLA;
#endif


    /******************************************************************/
    /* Start the main loop.                                           */
    /******************************************************************/
    while ( (!fOutputBufferFull) &&
            ((cRemainingRectsInBuffer > 0) || fMoreEngineRects)  )
    {
        /**************************************************************/
        /* If there are no rects left in our local buffer then we     */
        /* have to call back to the engine to fetch some more.        */
        /**************************************************************/
        if (cRemainingRectsInBuffer == 0)
        {
            /**********************************************************/
            /* First remove the rectangles that we have compressed    */
            /* from the supplied region.                              */
            /**********************************************************/
            for ( iRect = 0;
                  iRect < cCompressedRectsInBuffer;
                  iRect++ )
            {
                if (0 ==
                      EnginesDispatchTable[NGreCombineRectRegion & 0xff]
                                             ( hdc,
                                               hrgnApp,
                                               &arclBuffer[iRect],
                                               hrgnApp,
                                               CRGN_DIFF,
                                               pdcArg,
                                               NGreCombineRectRegion ) )
                {
                    goto gsb_err_exit;
                }
            }

            /**********************************************************/
            /* Now fill our rect buffer with fresh rectangles from    */
            /* the engine.                                            */
            /**********************************************************/
            rgnControl.ircStart = 1;
            rgnControl.crc = NRECTS;
            rgnControl.ulDirection = RECTDIR_LFRT_TOPBOT;

            if (0 == EnginesDispatchTable[NGreGetRegionRects & 0xff]
                                                ( hdc,
                                                  hrgnApp,
                                                  NULL,
                                                  &rgnControl,
                                                  arclBuffer,
                                                  pdcArg,
                                                  NGreGetRegionRects ))
            {
                goto gsb_err_exit;
            }

            /**************************************************************/
            /* Check that there is at least one rectangle to process.     */
            /**************************************************************/
            if (rgnControl.crcReturned == 0)
            {
                goto gsb_no_more_rects_exit;
            }

            /**************************************************************/
            /* Set the flag to indicate whether there are more rects      */
            /* in the engine.                                             */
            /* If there are less rects returned than we asked for then    */
            /* the engine has run out.                                    */
            /* If there are exactly the number of rectangles we asked for */
            /* then there are probably more in the engine.                */
            /* If the engine had exactly the number of rects that we      */
            /* asked for then at this point we will believe that there    */
            /* are more to come. This is not a problem because when       */
            /* we try to refill the buffer we will get zero rects         */
            /* returned and exit gracefully.                              */
            /**************************************************************/
            fMoreEngineRects = (rgnControl.crcReturned == NRECTS);

            cCompressedRectsInBuffer = 0;
            cRemainingRectsInBuffer = rgnControl.crcReturned;
            Spad.prclCurrent = arclBuffer;
        }

        /**************************************************************/
        /* There is now at least one rectangle in our rect buffer.    */
        /*                                                            */
        /* Adjust the current rectangle coordinates if necessary.     */
        /* We alter the coords according to format and bits/pel       */
        /* so that we do not have to worry about the masking          */
        /* associated with compressing/decompressing partial bytes.   */
        /*                                                            */
        /* 4bpp formats are rounded to 8 pel boundaries because the   */
        /* destination could be planar VGA.                           */
        /*                                                            */
        /* 8bpp formats are rounded to even pel boundaries because    */
        /* we transmit data in 16-bit fields i.e. two pels per data   */
        /* field.                                                     */
        /*                                                            */
        /**************************************************************/
        if ( ((ulCombinedFormat & GSB_BPP_FLAGS) == GSB_OPT_4BPP) ||
             (((ulCombinedFormat>>8) & GSB_BPP_FLAGS) == GSB_OPT_4BPP) )
        {
            /**********************************************************/
            /* Src or dst format is 4bpp - round x coords to a        */
            /* multiple of 8.                                         */
            /**********************************************************/
            Spad.prclCurrent->xLeft &= ~7L;
            Spad.prclCurrent->xRight =
                                     (Spad.prclCurrent->xRight+7) & ~7L;
        }
        else if (
             ((ulCombinedFormat & GSB_BPP_FLAGS) == GSB_OPT_8BPP) ||
             (((ulCombinedFormat>>8) & GSB_BPP_FLAGS) == GSB_OPT_8BPP) )
        {
            /**********************************************************/
            /* Src or dst format is 8bpp - round x coords to a        */
            /* multiple of 2.                                         */
            /**********************************************************/
            Spad.prclCurrent->xLeft &= ~1L;
            Spad.prclCurrent->xRight =
                                   (Spad.prclCurrent->xRight + 1) & ~1L;
        }

#ifdef FIREWALLS
        /**************************************************************/
        /* Check that the current rectangle is not NULL.              */
        /**************************************************************/
        if ( (Spad.prclCurrent->yBottom >= Spad.prclCurrent->yTop) ||
             (Spad.prclCurrent->xLeft >= Spad.prclCurrent->xRight) )
        {
            haltproc();
        }
#endif

        /**************************************************************/
        /* Compress the current rectangle!                            */
        /**************************************************************/
        fOutputBufferFull = CompressRect();

        /**************************************************************/
        /* Update the counters.                                       */
        /**************************************************************/
        cCompressedRectsInBuffer++;
        cRemainingRectsInBuffer--;
        Spad.prclCurrent++;

    }

gsb_no_more_rects_exit:
    /******************************************************************/
    /* If a region was supplied, then update it to reflect the        */
    /* compressed area.                                               */
    /******************************************************************/
    if (flCmd & GSB_OPT_HRGN)
    {
        if (fOutputBufferFull)
        {
            /**********************************************************/
            /* We terminated because the output buffer is full.       */
            /* Remove the rects that we compressed from the app       */
            /* region.                                                */
            /**********************************************************/
            for ( iRect = 0;
                  iRect < cCompressedRectsInBuffer;
                  iRect++ )
            {
                if (0 == EnginesDispatchTable[NGreCombineRectRegion & 0xff]
                                               ( hdc,
                                                 hrgnApp,
                                                 &arclBuffer[iRect],
                                                 hrgnApp,
                                                 CRGN_DIFF,
                                                 pdcArg,
                                                 NGreCombineRectRegion ) )
                {
                    goto gsb_err_exit;
                }
            }
        }
        else
        {
            /**********************************************************/
            /* The output buffer is not full, which means that we     */
            /* must have returned the whole region. We just have      */
            /* to set the supplied region to NULL.                    */
            /**********************************************************/
            if (0 == EnginesDispatchTable[NGreSetRectRegion & 0xff]
                                                  ( hdc,
                                                    hrgnApp,
                                                    NULL,
                                                    0L,
                                                    pdcArg,
                                                    NGreSetRectRegion ) )
            {
                goto gsb_err_exit;
            }
        }
    }
    else
    {
        /**************************************************************/
        /* A rectangle (rather than a region) was supplied.           */
        /* Update it to reflect the area not returned. Only yTop      */
        /* needs to be adjusted - all other edges will remain the     */
        /* same.                                                      */
        /**************************************************************/
        if (fOutputBufferFull)
        {
            /**********************************************************/
            /* The rectangle in arclBuffer has been updated to        */
            /* contain the area COMPRESSED.                           */
            /* We need to update the supplied rectangle to contain    */
            /* the area NOT compressed, which is the rectangle from   */
            /* the BOTTOM of the compressed rectangle downwards.      */
            /**********************************************************/
            ((PRECTL)hrgnApp)->yTop = arclBuffer[0].yBottom - 1;
        }
        else
        {
            /**********************************************************/
            /* The whole rectangle was returned. Set it to NULL.      */
            /* Note that the rectangle is inclusive, so we have to    */
            /* set yTop < yBottom.                                    */
            /**********************************************************/
            ((PRECTL)hrgnApp)->yTop = ((PRECTL)hrgnApp)->yBottom - 1;
        }

    }

    /******************************************************************/
    /* Fill in the data length in the supplied parameter and          */
    /* the packet header.                                             */
    /******************************************************************/
    *pulBufferLength =
    ((PPACKETHDR)pDestBuffer)->ulLength =
                     (ULONG)Spad.pNextFreeDestByte - (ULONG)pDestBuffer;


    /******************************************************************/
    /* Re-enable the cursor.                                          */
    /******************************************************************/
    reenable_cursor();

    return( fOutputBufferFull ?
                RC_COMPRESS_OK_UNCOMPLETED : RC_COMPRESS_OK_COMPLETED );


gsb_log_err_exit:
    LogError(ulErrorCode);
gsb_err_exit:
    reenable_cursor();
    return(RC_COMPRESS_ERROR);
}

#endif /* DCAF */
