/**************************************************************************

    DRAWDIB.C   - routines for drawing DIBs to the screen.

    this code handles stretching and dithering with custom code, none
    of this slow GDI code.

    the following DIB formats are supported:

        4bpp (will just draw it with GDI...)
        8bpp
        16bpp
        24bpp
        compressed DIBs

    drawing to:

        16 color DC         (will dither 8bpp down)
        256 (paletized) DC  (will dither 16 and 24bpp down)
        Full-color DC       (will just draw it!)

**************************************************************************/

#include <windows.h>
#include <windowsx.h>
#include "drawdibi.h"

#ifndef ICMODE_FASTDECOMPRESS
    #define ICMODE_FASTDECOMPRESS 3
#endif

#define USE_SETDI 1

#ifndef BI_BITMAP
    #define BI_BITMAP   0x4D544942      // 'BITM'
#endif

#ifdef WIN32
#define FlatToHuge(a, b, c)
#define HugeToFlat(a, b, c)
#else
extern FAR PASCAL FlatToHuge(LPVOID,DWORD,DWORD);
extern FAR PASCAL HugeToFlat(LPVOID,DWORD,DWORD);
#endif

//!!! not quite right.
#ifndef WIN32
#define IsScreenDC(hdc)     (GetDCOrg(hdc) != 0L)
#else
#define IsScreenDC(hdc)     (WindowFromDC(hdc) != NULL)
#endif

#define DCAlignment 3

__inline int DCNotAligned(HDC hdc, int xDst) {
    POINT pt;

    pt.x = xDst; pt.y = 0;
    LPtoDP(hdc, &pt, 1);
    xDst = pt.x;

#ifdef _WIN32
    GetDCOrgEx(hdc, &pt);
#else
    pt.x = LOWORD(GetDCOrg(hdc));
#endif
    return (pt.x + xDst) & DCAlignment;
}

/**************************************************************************
**************************************************************************/

UINT                gwScreenBitDepth = (UINT)-1;
UINT                gwRasterCaps = 0;
BOOL                gf286= FALSE;
static UINT         gUsage = 0;
static BOOL         gfInit = FALSE;
static BOOL         gfHalftone = FALSE;
static BOOL         gfBitmap   = FALSE;
static BOOL         gfBitmapX  = FALSE;
static BOOL         gfScreenX  = FALSE;
static BOOL         gfDrawX    = FALSE;
static HBITMAP      hbmStockMono;               // the stock mono bitmap.

#ifndef WIN32
static  BOOL gfDisplayHasBrokenRasters;
static  HDC hdcDCI;
static  DCISURFACEINFO FAR *pdci;

static  struct {
        BITMAPINFOHEADER bi;
        DWORD            dwMask[3];
}       biScreen;
static  LPVOID lpScreen;

SZCODE  szDVA[] = TEXT("dva");
#endif

/**************************************************************************
**************************************************************************/

SZCODE  szDrawDib[]             = TEXT("DrawDib");
SZCODE  szHalftone[]            = TEXT("Halftone");
SZCODE  szDrawToBitmap[]        = TEXT("DrawToBitmap");
SZCODE  szDecompressToBitmap[]  = TEXT("DecompressToBitmap");
SZCODE  szDecompressToScreen[]  = TEXT("DecompressToScreen");
SZCODE  szDrawToScreen[]        = TEXT("DrawToScreen");

/**************************************************************************
**************************************************************************/

static BOOL NEAR PASCAL DrawDibFree(PDD pdd, BOOL fSameDib, BOOL fSameSize);
static HPALETTE CreateBIPalette(HPALETTE hpal, LPBITMAPINFOHEADER lpbi);
static BOOL NEAR IsIdentityPalette(HPALETTE hpal);
static BOOL NEAR AreColorsAllGDIColors(LPBITMAPINFOHEADER lpbi);
static BOOL SetPalFlags(HPALETTE hpal, int iIndex, int cntEntries, UINT wFlags);
static HPALETTE CreateExplicitPalette(void);

void DrawDibPalChange(PDD pdd, HDC hdc, HPALETTE hpal);
void DrawDibClipChange(PDD pdd, UINT wFlags);

static BOOL FixUpCodecPalette(HIC hic, LPBITMAPINFOHEADER lpbi);
static BOOL NEAR SendSetPalette(PDD pdd);

#ifndef WIN32
extern BOOL gf286;

/**************************************************************************
**************************************************************************/
static void InitDCI()
{
    UINT WidthBytes;

    //
    // initialize DCI and open a surface handle to it.
    //

    // if DVA = 0 in WIN.INI, don't use DCI or DVA.
    // PSS tells people to use this if they have video problems,
    // so we shouldn't change the string.

    if (gf286 || !GetProfileInt(szDrawDib, szDVA, TRUE))
        return;

    hdcDCI = DCIOpenProvider();

    if (hdcDCI == NULL)
        return;

    DCICreatePrimary(hdcDCI, &pdci);

    if (pdci == NULL)
        return;

    WidthBytes = abs((UINT)pdci->lStride);

    //
    // convert DCISURFACEINFO into a BITMAPINFOHEADER...
    //
    biScreen.bi.biSize          = sizeof(BITMAPINFOHEADER);
    biScreen.bi.biWidth         = WidthBytes*8/(UINT)pdci->dwBitCount;
    biScreen.bi.biHeight        = pdci->dwHeight;
    biScreen.bi.biPlanes        = 1;
    biScreen.bi.biBitCount      = (UINT)pdci->dwBitCount;
    biScreen.bi.biCompression   = BI_1632;  //!!!???
    biScreen.bi.biSizeImage     = pdci->dwHeight * WidthBytes;
    biScreen.bi.biXPelsPerMeter = WidthBytes;
    biScreen.bi.biYPelsPerMeter = 0;
    biScreen.bi.biClrUsed       = 0;
    biScreen.bi.biClrImportant  = 0;
    biScreen.dwMask[0]          = pdci->dwMask[0];
    biScreen.dwMask[1]          = pdci->dwMask[1];
    biScreen.dwMask[2]          = pdci->dwMask[2];

    if (pdci->dwCompression == 0 && (UINT)pdci->dwBitCount == 16)
    {
        biScreen.dwMask[0] = 0x007C00;
        biScreen.dwMask[1] = 0x0003E0;
        biScreen.dwMask[2] = 0x00001F;
    }

    if (pdci->dwCompression == 0 && (UINT)pdci->dwBitCount >= 24)
    {
        biScreen.dwMask[0] = 0xFF0000;
        biScreen.dwMask[1] = 0x00FF00;
        biScreen.dwMask[2] = 0x0000FF;
    }

    DPF(("DCI Surface: %dx%dx%d", (int)pdci->dwWidth, (int)pdci->dwHeight, (int)LOWORD(pdci->dwBitCount)));
    DPF(("DCI Surface: biCompression=%ld Masks: %04lX %04lX %04lX",biScreen.bi.biCompression,biScreen.dwMask[0],biScreen.dwMask[1],biScreen.dwMask[2]));

    if (pdci->lStride > 0)
        biScreen.bi.biHeight = -(int)pdci->dwHeight;
    else
        pdci->dwOffSurface -= biScreen.bi.biSizeImage;

    //
    // make sure the pointer is valid.
    //
    if (pdci->dwOffSurface >= 0x10000)
    {
        DPF(("DCI Surface cant be supported"));

        lpScreen = NULL;
        biScreen.bi.biSize = 0;
    }
    else
    {
        lpScreen = (LPVOID)MAKELP(pdci->wSelSurface,pdci->dwOffSurface);
    }

    //
    // check if the display has broken rasters.
    //
    if (pdci->dwDCICaps & DCI_1632_ACCESS)
        gfDisplayHasBrokenRasters = (0x10000l % WidthBytes) != 0;

    if (gfDisplayHasBrokenRasters)
    {
        DPF(("*** Display has broken rasters"));
    }
}

void TermDCI()
{
    if (pdci)
    {
        DCIDestroy(pdci);
        pdci = NULL;
    }

    if (hdcDCI)
    {
        DeleteDC(hdcDCI);
        hdcDCI = NULL;
    }
}

#else
    #define InitDCI()
    #define TermDCI()
#endif

/**************************************************************************
* @doc INTERNAL DrawDib
*
* @api BOOL | DrawDibInit | This function initalizes the DrawDib library.
*
* @rdesc Returns TRUE if the library is initialized properly, otherwise
*        it returns FALSE.
*
* @comm Users don't need to call this, because <f DrawDibOpen> does it for them.
*
* @xref DrawDibTerm
*
**************************************************************************/
BOOL VFWAPI DrawDibInit()
{
    HDC hdc;

    if (gfInit)
        return TRUE;

    gf286 = (BOOL)(GetWinFlags() & WF_CPU286);

    hdc = GetDC(NULL);

    gwScreenBitDepth = GetDeviceCaps(hdc, BITSPIXEL) * GetDeviceCaps(hdc, PLANES);
    gwRasterCaps = GetDeviceCaps(hdc, RASTERCAPS);

    InitDCI();

    ReleaseDC(NULL, hdc);

    gfHalftone = GetProfileInt(szDrawDib, szHalftone, FALSE);
    gfBitmap   = GetProfileInt(szDrawDib, szDrawToBitmap, -1);
    gfBitmapX  = GetProfileInt(szDrawDib, szDecompressToBitmap, TRUE);
    gfScreenX  = GetProfileInt(szDrawDib, szDecompressToScreen, TRUE);
    gfDrawX    = GetProfileInt(szDrawDib, szDrawToScreen, TRUE);

#ifdef DEBUG
    gwRasterCaps = GetProfileIntA("drawdib", "RasterCaps", gwRasterCaps);
    gwScreenBitDepth = GetProfileIntA("drawdib", "ScreenBitDepth", gwScreenBitDepth);
    gf286 = GetProfileIntA("drawdib", "cpu", gf286 ? 286 : 386) == 286;
#endif

    //
    // fix up the bit-depth of the display.
    //
    if (gwScreenBitDepth > 32)
        gwScreenBitDepth = 32;

    if (gwScreenBitDepth == 16 || gwScreenBitDepth == 32)
    {
        BITMAPINFOHEADER bi;
        UINT u;

        bi.biSize           = sizeof(bi);
        bi.biWidth          = 1;
        bi.biHeight         = 1;
        bi.biPlanes         = 1;
        bi.biBitCount       = gwScreenBitDepth;
        bi.biCompression    = 0;
        bi.biSizeImage      = 0;
        bi.biXPelsPerMeter  = 0;
        bi.biYPelsPerMeter  = 0;
        bi.biClrUsed        = 0;
        bi.biClrImportant   = 0;

        u = (UINT)DrawDibProfileDisplay(&bi);

        if (u == 0)
        {
            DPF(("Pretending display is 24 bit (not %d)", gwScreenBitDepth));
            gwScreenBitDepth = 24;
        }
    }

    gfInit = TRUE;
    return TRUE;
}

/**************************************************************************
* @doc INTERNAL DrawTerm
*
* @api BOOL | DrawDibTerm | This function teminates the DrawDib library.
*
* @rdesc Returns TRUE.
*
* @comm Users don't need to call this, because <f DrawDibClose> does it for them.
*
* @xref DrawDibInit
*
**************************************************************************/
BOOL VFWAPI DrawDibTerm()
{
    //
    //  free global stuff.
    //
    TermDCI();
    gfInit = FALSE;
    return TRUE;
}

/**************************************************************************
* @doc INTERNAL DrawDib
*
* @api void | DrawDibCleanup | clean up drawdib stuff
*   called in MSVIDEOs WEP()
*
**************************************************************************/
void FAR PASCAL DrawDibCleanup(HTASK hTask)
{
    if (gUsage > 0)
        RPF(("%d DrawDib handles left open", gUsage));

    DrawDibTerm();
}

/**************************************************************************
* @doc  INTERNAL
*
* @api BOOL | DibEq | This function compares two dibs.
*
* @parm LPBITMAPINFOHEADER | lpbi1 | Pointer to one bitmap.
*       this DIB is assumed to have the colors after biSize bytes.
*
* @parm LPBITMAPINFOHEADER | lpbi2 | Pointer to second bitmap.
*       this DIB is assumed to have the colors after biSize bytes.
*
* @rdesc Returns TRUE if bitmaps are identical, FALSE otherwise.
*
**************************************************************************/
INLINE BOOL NEAR PASCAL DibEq(LPBITMAPINFOHEADER lpbi1, LPBITMAPINFOHEADER lpbi2)
{
    if (lpbi1 == NULL || lpbi2 == NULL)
	return FALSE;

    return
             lpbi1->biCompression == lpbi2->biCompression   &&
        (int)lpbi1->biSize        == (int)lpbi2->biSize     &&
        (int)lpbi1->biWidth       == (int)lpbi2->biWidth    &&
        (int)lpbi1->biHeight      == (int)lpbi2->biHeight   &&
        (int)lpbi1->biBitCount    == (int)lpbi2->biBitCount &&
        ((int)lpbi1->biBitCount > 8 ||
            (int)lpbi1->biClrUsed == (int)lpbi2->biClrUsed  &&
            _fmemcmp((LPBYTE)lpbi1 + lpbi1->biSize,
		(LPBYTE)lpbi2 + lpbi2->biSize,
                (int)lpbi1->biClrUsed*sizeof(RGBQUAD)) == 0);
}

/**************************************************************************
* @doc INTERNAL
*
* @api PDD NEAR | DrawDibLock | Lock the DrawDib handle.
*
* @parm HDRAWDIB | hdd | DrawDib handle.
*
* @rdesc Returns a pointer to a <t DRAWDIB_STRUCT> if successful, NULL otherwise.
*
**************************************************************************/

#define DrawDibLock(hdd) (\
    hdd == NULL || ((PDD)hdd)->wSize != sizeof(DRAWDIB_STRUCT) ? NULL : \
    (PDD)hdd)

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api HDRAWDIB | DrawDibOpen | This function opens a DrawDib context for drawing.
*
* @rdesc Returns a handle to a DrawDib context if successful,
*        otherwise it returns NULL.
*
* @comm Use this function to obtain a handle to a DrawDib context
*       before drawing device independent bitmaps.
*
*       If drawing multiple device independent bitmaps simultaneously,
*       obtain a handle to a DrawDib context for each bitmap.
*
* @xref <f DrawDibClose>
*
**************************************************************************/
HDRAWDIB VFWAPI DrawDibOpen(void)
{
    HDRAWDIB hdd;
    PDD      pdd;

    hdd = LocalAlloc(LPTR, sizeof(DRAWDIB_STRUCT));    /* zero init */

    if (hdd == NULL)
        return NULL;

    pdd = (PDD)hdd;
    pdd->wSize = sizeof(DRAWDIB_STRUCT);

    if (gUsage++ == 0)
        DrawDibInit();

    return hdd;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibClose | This function closes a DrawDib context
*      and frees the resources DrawDib allocated for it.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @rdesc Returns TRUE if the context closed successfully.
*
* @comm Use this function to free the <p hdd> handle
*       after the application has finished drawing.
*
* @xref <f DrawDibOpen>
**************************************************************************/
BOOL VFWAPI DrawDibClose(HDRAWDIB hdd)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    DrawDibFree(pdd, FALSE, FALSE);

    pdd->wSize = 0;
    LocalFree(hdd);

    if (--gUsage == 0)
        DrawDibTerm();

    return TRUE;
}

/**************************************************************************
* @doc INTERNAL
*
* @api BOOL | DrawDibFree | Free up everything in a <t DRAWDIB_STRUCT>.
*
* @parm PDD | pdd | Pointer to a <t DRAWDIB_STRUCT>.
*
* @rdesc Returns TRUE if successful, FALSE otherwise.
*
**************************************************************************/
static BOOL NEAR PASCAL DrawDibFree(PDD pdd, BOOL fSameDib, BOOL fSameSize)
{
    if (pdd == NULL)
        return FALSE;

    //
    // If the draw palette has changed, the compressor may now be giving us DIBs
    // mapped to a different palette, so we need to clean up so we'll produce
    // a new mapping table so we'll actually draw with the new palette.
    // (see SendSetPalette)
    //
    if (!fSameDib)
    {
        //
        // if this palette is selected as the foreground palette
        // and we delete it we are going to hose GDI!
        //
        if (pdd->hpal)
            DeleteObject(pdd->hpal);
        if (pdd->hpalCopy)
            DeleteObject(pdd->hpalCopy);

        pdd->hpal = NULL;
        pdd->hpalCopy = NULL;
    }

    if (!fSameDib)
    {

	if (pdd->lpbi) {
	    GlobalFreePtr(pdd->lpbi);
	    pdd->lpbi = NULL;
	    pdd->lpargbqIn = NULL;
	}

        if (pdd->lpDitherTable)
        {
            DitherTerm(pdd->lpDitherTable);
            pdd->lpDitherTable = NULL;
        }

        if (pdd->hic && pdd->hic != (HIC)-1)
        {
            ICDecompressEnd(pdd->hic);
            ICDecompressExEnd(pdd->hic);
            ICClose(pdd->hic);
        }

        pdd->ulFlags &= ~(DDF_IDENTITYPAL);
        pdd->hic  = NULL;

        pdd->iAnimateStart = 0;
        pdd->iAnimateLen = 0;
        pdd->iAnimateEnd = 0;
    }

    if (!fSameSize || !fSameDib)
    {
        if (pdd->hdcDraw && hbmStockMono)
            SelectObject(pdd->hdcDraw, hbmStockMono);

        if (pdd->hdcDraw)
            DeleteDC(pdd->hdcDraw);

        if (pdd->hbmDraw) {
            DeleteObject(pdd->hbmDraw);

            //
            // if we have a bitmap pointer lose it
            //
            if (pdd->ulFlags & (DDF_CANBITMAPX))
                pdd->pbBitmap = NULL;
        }

        if ((pdd->pbStretch) && (pdd->pbStretch != pdd->lpDIBSection))
            GlobalFreePtr(pdd->pbStretch);

        if ((pdd->pbDither) && (pdd->pbDither != pdd->lpDIBSection))
            GlobalFreePtr(pdd->pbDither);

        if ((pdd->pbBuffer) && (pdd->pbBuffer != pdd->lpDIBSection))
            GlobalFreePtr(pdd->pbBuffer);

#if USE_SETDI
        if (pdd->hbmDraw)
            SetBitmapEnd(&pdd->sd);
#endif

        pdd->hdcDraw = NULL;
        pdd->hbmDraw = NULL;
	pdd->lpDIBSection = NULL;
        pdd->pbStretch = NULL;
        pdd->pbDither = NULL;
        pdd->pbBuffer = NULL;

        pdd->biDraw.biBitCount = 0;
        pdd->biDraw.biWidth    = 0;
        pdd->biDraw.biHeight   = 0;

        pdd->biBuffer.biBitCount = 0;
        pdd->biBuffer.biWidth    = 0;
        pdd->biBuffer.biHeight   = 0;

        // clear all the internal flags (except palette stuff)
        pdd->ulFlags &= ~(DDF_OURFLAGS ^ DDF_IDENTITYPAL);
        pdd->ulFlags |= DDF_DIRTY;

        pdd->iDecompress = 0;
    }

    return TRUE;
}

/**************************************************************************
* @doc INTERNAL
*
* @api UINT | QueryDraw | see if the current display device
*             (DISPDIB or GDI) can draw the given dib
*
* @parm PDD | pdd | pointer to a <t DRAWDIB_STRUCT>.
*
* @parm LPBITMAPINFOHEADER | lpbi | pointer to a bitmap.
*
* @rdesc Returns display flags, see profdisp.h
*
**************************************************************************/

static UINT NEAR QueryDraw(PDD pdd, LPBITMAPINFOHEADER lpbi)
{
    return (UINT)DrawDibProfileDisplay(lpbi);
}

/**************************************************************************
* @doc INTERNAL DrawDib
*
* @comm Called from DrawDibBegin to try decompression to a bitmap.
*
**************************************************************************/
BOOL DrawDibQueryBitmapX(
    PDD pdd
)
{
    BITMAPINFOHEADER *pbi;

#ifndef _WIN32
    if (!CanLockBitmaps()) {
        return FALSE;
    }
#endif

    if (gwScreenBitDepth == 8 && !(gwRasterCaps & RC_PALETTE))
        return FALSE;

    if ((gwRasterCaps & RC_PALETTE) && !(pdd->ulFlags & DDF_IDENTITYPAL))
        return FALSE;

    pbi = &pdd->biStretch;

    if (!GetDIBBitmap(pdd->hbmDraw, pbi))
        return FALSE;

#ifdef XDEBUG
    if (ICDecompressQuery(pdd->hic, pdd->lpbi, pbi) != ICERR_OK)
    {
        if (mmGetProfileIntA(szDrawDib, "ForceDecompressToBitmap", FALSE))
        {
            pbi->biHeight = -pbi->biHeight;
            pbi->biCompression = 0;
        }
    }
#endif
    if (ICDecompressQuery(pdd->hic, pdd->lpbi, pbi) != ICERR_OK)
    {
        if (pbi->biCompression == BI_BITMAP &&
            pbi->biSizeImage <= 128*1024l &&
            (pbi->biXPelsPerMeter & 0x03) == 0 &&
            pbi->biSizeImage > 64*1024l)
        {
            pdd->ulFlags |= DDF_HUGEBITMAP;
            pbi->biCompression = 0;

            pbi->biSizeImage -= pbi->biYPelsPerMeter;   //FillBytes

            if (ICDecompressQuery(pdd->hic, pdd->lpbi, pbi) != ICERR_OK)
                return FALSE;
        }
        else
            return FALSE;
    }

    pdd->ulFlags |= DDF_NEWPALETTE;     // force check in DrawDibRealize
    pdd->ulFlags |= DDF_CANBITMAPX;     // can decompress to bitmaps

    if (pdd->ulFlags & DDF_HUGEBITMAP)
        RPF(("    Can decompress '%4.4hs' to a HUGE BITMAP (%dx%dx%d)",(LPSTR)&pdd->lpbi->biCompression, PUSHBI(*pbi)));
    else
        RPF(("    Can decompress '%4.4hs' to a BITMAP (%dx%dx%d)",(LPSTR)&pdd->lpbi->biCompression, PUSHBI(*pbi)));

    //
    // reuse the stretch buffer for the bitmap.
    //
    pdd->biStretch = *pbi;
#ifndef _WIN32
    pdd->pbStretch = LockBitmap(pdd->hbmDraw);

    if (pdd->pbStretch == NULL)
    {
        DPF(("    Unable to lock bitmap!"));
        pdd->ulFlags &= ~DDF_CANBITMAPX; // can't decompress to bitmaps
        return FALSE;
    }
#endif

    return TRUE;
}



#define Is565(bi)   (((bi)->biCompression == BI_BITFIELDS) &&   \
		    ((bi)->biBitCount == 16) &&			\
		    (((LPDWORD)((bi)+1))[0] == 0x00F800) &&	\
		    (((LPDWORD)((bi)+1))[1] == 0x0007E0) &&	\
		    (((LPDWORD)((bi)+1))[2] == 0x00001F) )



/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibBegin | This function changes parameters
*      of a DrawDib context or it initializes a new DrawDib context.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm HDC | hdc | Specifies a handle to a display context for drawing (optional).
*
* @parm int | dxDest | Specifies the width of the destination rectangle.
*       Width is specified in MM_TEXT client units.
*
* @parm int | dyDest | Specifies the height of the destination rectangle.
*       Height is specified in MM_TEXT client units.
*
* @parm LPBITMAPINFOHEADER | lpbi | Specifies a pointer to a
*       <t BITMAPINFOHEADER> structure containing the
*       image format. The color table for the DIB follows the
*       image format.
*
* @parm int | dxSrc | Specifies the width of the source rectangle.
*       Width is specified in pixels.
*
* @parm int | dySrc | Specifies the height of the source rectangle.
*       Height is specified in pixels.
*
* @parm UNIT | wFlags | Specifies the applicable flags for
*       the function. The following flags are defined:
*
* @flag DDF_SAME_HDC | Assumes the handle to the display context
*       is already specified. When this flag is used,
*       DrawDib also assumes the correct palette has already been
*       realized into the device context (possibly by
*       <f DrawDibRealize>).
*
* @flag DDF_SAME_DRAW | Uses the drawing parameters previously
*       specified for this function.  Use this flag only
*       if <p lpbi>, <p dxDst>, <p dyDst>, <p dxSrc>, and <p dySrc>
*       have not changed since using <f DrawDibDraw> or <f DrawDibBegin>.
*
* @flag DDF_DONTDRAW | Indicates the frame is to be decompressed
*       and not drawn. The DDF_UPDATE flag can be used later
*       to actually draw the image.
*
* @flag DDF_ANIMATE | Allows palette animation. If this flag is present,
*       the palette <f DrawDib> creates will have the PC_RESERVED flag set for
*       as many entries as possible, and the palette can be animated by
*       <f DrawDibChangePalette>. If using <f DrawDibBegin> with
*       <f DrawDibDraw>, set this flag with <f DrawDibBegin>
*       rather than <f DrawDibDraw>.
*
* @flag DDF_JUSTDRAWIT | Uses GDI to draw the image. This prevents
*       the DrawDib functions from calling ICM to decompress
*       the image or prevents them from
*       using their own routines to stretch or dither the image.
*       This essentially reduces <f DrawDibDraw> to <f StretchDIBits>.
*
* @flag DDF_BACKGROUNDPAL | Realizes the palette used for drawing
*       in the background leaving the actual palette used for display
*       unchanged.  (This flag is valid only if DDF_SAME_HDC is not set.)
*
* @flag DDF_HALFTONE | Always dithers the DIB to a standard palette
*       regardless of the palette of the DIB. If using <f DrawDibBegin> with
*       <f DrawDibDraw>, set this flag with <f DrawDibBegin>
*       rather than <f DrawDibDraw>.
*
* @flag DDF_BUFFER | Indicates DrawDib should try to use a
*       offscreen buffer so DDF_UPDATE can be used. This
*       disables decompression and drawing directly to the screen.
*       If DrawDib is unable to create an offscreen buffer,
*       it will decompress or draw directly to the screen.
*
*       For more information, see the DDF_UPDATE and DDF_DONTDRAW
*       flags described for <f DrawDibDraw>.
*
*
* @rdesc Returns TRUE if successful.
*
* @comm This function prepares to draw a bitmap specified by <p lpbi>
*       to the display context <p hdc>. The image is stretched to
*       the size specified by <p dxDest> and <p dyDest>. If <p dxDest> and
*       <p dyDest> are (-1, -1), the bitmap is drawn to a
*       1:1 scale without stretching.
*
*       Use this function only if you want to prepare DrawDib
*       before using <f DrawDibDraw> to draw the image.
*       If you do not use this function, <f DrawDibDraw> implicitly
*       uses it when it draws the image.
*
*       To update the flags set with <f DrawDibBegin>, call <f DrawDibBegin>
*       with new parameters.
*
*       When <f DrawDibBegin> is used, the <f DDF_SAME_DRAW>
*       flag is normally set for <f DrawDibDraw>.
*
*       If the parameters of <f DrawDibBegin> have not changed, subsequent
*       uses of it have not effect.
*
*       Use <f DrawDibEnd> to free memory used by the DrawDib context.
*
* @xref <f DrawDibEnd> <f DrawDibDraw>
**************************************************************************/
//#ifndef WIN32
//#pragma message("Make DrawDibBegin faster for changing the size only!")
//#endif
BOOL VFWAPI DrawDibBegin(HDRAWDIB hdd,
                             HDC      hdc,
                             int      dxDst,
                             int      dyDst,
                             LPBITMAPINFOHEADER lpbi,
                             int      dxSrc,
                             int      dySrc,
                             UINT     wFlags)
{
    PDD pdd;
    int ScreenBitDepth;
    int dxSave,dySave;
    BOOL     fNewPal;
    BOOL     fSameDib;
    BOOL     fSameSize;
    BOOL     fSameFlags;
    BOOL     fSameHdc;
    UINT    wFlagsChanged;
    DWORD   ulFlagsSave;
    DWORD    dw;
    UINT     w;
    HPALETTE hPal;
    LONG    lSize;

    //
    // Quick sanity checks....
    //
    if (lpbi == NULL)
        return FALSE;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (!gfInit)
        DrawDibInit();

    //
    // fill in defaults.
    //
    if (dxSrc < 0)
        dxSrc = (int)lpbi->biWidth;

    if (dySrc < 0)
        dySrc = (int)lpbi->biHeight;

    if (dxDst < 0)
        dxDst = dxSrc;

    if (dyDst < 0)
        dyDst = dySrc;

    if (dxSrc == 0 || dySrc == 0)       // !!! || dxDst == 0 || dyDst == 0)
        return FALSE;

    ulFlagsSave = pdd->ulFlags;
    wFlagsChanged = ((UINT)pdd->ulFlags ^ wFlags);

    fSameHdc = hdc == pdd->hdcLast;
    fSameDib  = DibEq(pdd->lpbi, lpbi) &&
                        !(wFlagsChanged & DDF_HALFTONE) &&
                        (pdd->hpalDraw == pdd->hpalDrawLast);

    fSameFlags = (pdd->ulFlags & DDF_BEGINFLAGS) == (wFlags & DDF_BEGINFLAGS);
    fSameSize = pdd->dxDst == dxDst && pdd->dyDst == dyDst &&
                pdd->dxSrc == dxSrc && pdd->dySrc == dySrc;
    pdd->hdcLast = hdc;

    //
    // do a quick check to see if the params have changed.
    // If the DIB, and size of the DIB, and flags used are the same as the last
    // time we called DrawDibBegin, then there's nothing to do and we'll only
    // waste time plodding through this code.
    // There is one case when all these could be the same, but the situation
    // has still changed enough so that we need to recompute things... if the
    // hdc is different than last time, and we're dealing with RLE.  You see,
    // we make some decisions about RLE (like we can go direct to a screen DC
    // but not a memory DC) that are affected by what hdc we're using.  So
    // we will not bail out early if we are using RLE and the hdc's are
    // different.
    //
    if (fSameDib && fSameSize && fSameFlags)
    {
	if ((lpbi->biCompression != BI_RLE8 && lpbi->biCompression != BI_RLE4)
								|| fSameHdc)
	    return TRUE;
    }

    pdd->hpalDrawLast = pdd->hpalDraw;

    RPF(("DrawDibBegin %dx%dx%d '%4.4hs' [%d %d] [%d %d]",
            (int)lpbi->biWidth,
            (int)lpbi->biHeight,
            (int)lpbi->biBitCount,
                (lpbi->biCompression == BI_RGB  ? (LPSTR)"None" :
                 lpbi->biCompression == BI_RLE8 ? (LPSTR)"Rle8" :
                 lpbi->biCompression == BI_RLE4 ? (LPSTR)"Rle4" :
                 (LPSTR)&lpbi->biCompression),
            dxSrc, dySrc, dxDst, dyDst));

    fNewPal = pdd->hpal == NULL || !fSameDib;

    //
    // make sure this palette is not the in the DC, because we
    // are going to delete it, and GDI get real upset if we do this.
    //

    if (fNewPal && pdd->hpal && hdc)
    {
        hPal = SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), TRUE);

        if (hPal == pdd->hpal)
            RPF(("    Warning unselecting palette..."));
    }

    DrawDibFree(pdd, fSameDib, fSameSize);

    pdd->dxSrc = dxSrc;
    pdd->dySrc = dySrc;
    pdd->dxDst = dxDst;
    pdd->dyDst = dyDst;

    //
    // copy the source DIB header and the colors.
    //
    if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
        lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);

////if (lpbi->biClrUsed != 0 && lpbi->biBitCount > 8)
////    lpbi->biClrUsed = 0;

    // Make a copy of the source format.  Remember, some codec could have
    // defined a custom format larger than a BITMAPINFOHEADER so make a copy
    // of EVERYTHING.
    if (!fSameDib) {
	lSize = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
	pdd->lpbi = (LPBITMAPINFOHEADER)GlobalAllocPtr(GPTR, lSize);
	if (pdd->lpbi == NULL)
	    return FALSE;
	_fmemcpy(pdd->lpbi, lpbi, (int)lSize);
	// This is where the colour info is
	pdd->lpargbqIn = (LPVOID)((LPBYTE)lpbi + lpbi->biSize);
    }

    pdd->biBuffer = *lpbi;

    pdd->lpbi->biSizeImage = 0;
    pdd->biBuffer.biSizeImage = 0;

    //
    // init all other color tables to be the initial colors
    //
    if (lpbi->biBitCount <= 8)
    {
        _fmemcpy(pdd->argbq,   (LPBYTE)lpbi+(int)lpbi->biSize,
                (int)lpbi->biClrUsed * sizeof(RGBQUAD));
        _fmemcpy(pdd->aw,      (LPBYTE)lpbi+(int)lpbi->biSize,
                (int)lpbi->biClrUsed * sizeof(RGBQUAD));
    }

    // set PalUse to default: DIB_PAL_COLORS. This will be set
    // to DIB_PAL_INDICES if DrawdibCheckPalette is called and detects
    // that it is safe to use indices. DIB_PAL_COLORS is a safe
    // default.

    pdd->uiPalUse = DIB_RGB_COLORS;     // assume RGB colors for now.

    //
    // make sure the device is a palette device before dinking with
    // palette animation.
    //
    if (wFlags & DDF_ANIMATE)
    {
        if (!(gwRasterCaps & RC_PALETTE) ||
            (int)lpbi->biBitCount > 8 || pdd->hpalDraw)
            wFlags &= ~DDF_ANIMATE;
    }

    //
    // copy the flags
    //
try_again:
    pdd->ulFlags &= ~DDF_USERFLAGS;
    pdd->ulFlags |= (wFlags & DDF_USERFLAGS);

    pdd->ulFlags &= ~DDF_UPDATE;

    //
    // deal with a decompressor if needed.
    //
    switch (lpbi->biCompression)
    {
        case BI_RGB:
            break;

        default:
            //
            //  see if the DISPLAY/DISPDIB can draw the format directly!
            //
            //  if the buffer flag is set we MUST use a decompress buffer.
            //  regardless of what the display can do
            //
            if (wFlags & DDF_BUFFER)
                w = 0;
            else
                w = QueryDraw(pdd, lpbi);

            if (w & PD_CAN_DRAW_DIB)
            {
                if (((dxSrc == dxDst && dySrc == dyDst) && (w & PD_STRETCHDIB_1_1_OK)) ||
                    ((dxSrc != dxDst || dySrc != dyDst) && (w & PD_STRETCHDIB_1_N_OK)) ||
                    ((dxDst % dxSrc) == 0 && (dyDst % dySrc) == 0 && (w & PD_STRETCHDIB_1_2_OK)))
                {
		    // GDI can't handle drawing RLEs to a memory DC so we will
		    // have to pretend that RLE can't be drawn to the screen
		    // and decompress it first.
		    // We also can't DITHER RLE, so if we're running on a
		    // 16 colour display, make sure we're using a decompressor.
		    if (((lpbi->biCompression != BI_RLE8) &&
				(lpbi->biCompression != BI_RLE4)) ||
			(hdc && IsScreenDC(hdc) && gwScreenBitDepth >=8))
		    {
                	wFlags |= DDF_JUSTDRAWIT;

                	if (pdd->hic)
                	    ICClose(pdd->hic);

                	pdd->hic = NULL;
                	goto no_decomp;
		    }
                }
            }

            if (pdd->hic == NULL)
            {
                DWORD fccHandler;

                fccHandler = 0;

                if (lpbi->biCompression == BI_RLE8)
                    fccHandler = mmioFOURCC('R','L','E',' ');

                pdd->hic = ICLocate(ICTYPE_VIDEO,
                                    fccHandler,
                                    lpbi, NULL,
                                    ICMODE_FASTDECOMPRESS);

                if (pdd->hic == NULL)
                    pdd->hic = ICDecompressOpen(ICTYPE_VIDEO,
                                                fccHandler,lpbi,NULL);

                if (pdd->hic)
                {
                    //
                    //  make sure the codec uses its default palette out of the gate
                    //
                    if (ICDecompressSetPalette(pdd->hic, NULL) == ICERR_OK)
                    {
                        pdd->ulFlags |= DDF_CANSETPAL;
                        RPF(("    codec supports ICM_SET_PALETTE"));
                    }
                    else
                    {
                        pdd->ulFlags &= ~DDF_CANSETPAL;
                    }
                }
            }

            if (pdd->hic == NULL || pdd->hic == (HIC)-1)
            {
                RPF(("    Unable to open compressor '%4.4ls'",(LPSTR)&lpbi->biCompression));
                pdd->hic = (HIC)-1;

                if (wFlags & DDF_BUFFER)
                {
                    RPF(("   Turning DDF_BUFFER off"));
                    wFlags &= ~DDF_BUFFER;
                    goto try_again;
                }

                return FALSE;
            }

            //
            //  now find the best DIB format to decompress to.
            //
            if (!ICGetDisplayFormat(pdd->hic, lpbi, &pdd->biBuffer,
                (gfHalftone || (wFlags & DDF_HALFTONE)) ? 16 : 0,
                MulDiv(dxDst,abs((int)lpbi->biWidth),dxSrc),
                MulDiv(dyDst,abs((int)lpbi->biHeight),dySrc)))
            {
                RPF(("    Compressor error!"));
codec_error:
                //ICClose(pdd->hic);
                //pdd->hic = (HIC)-1;
                return FALSE;
            }

            //
            // we have new source params
            //
            dxSrc = MulDiv(dxSrc, abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
            dySrc = MulDiv(dySrc, abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);
//          xSrc  = MulDiv(xSrc,  abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
//          ySrc  = MulDiv(ySrc,  abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);

            //
            // now allocate the decompress buffer!
            //
            pdd->biBuffer.biSizeImage = DIBSIZEIMAGE(pdd->biBuffer);
            pdd->pbBuffer = GlobalAllocPtr(GHND,pdd->biBuffer.biSizeImage);

            if (pdd->pbBuffer == NULL)
            {
                RPF(("    No Memory for decompress buffer"));
                ICClose(pdd->hic);
                pdd->hic = (HIC)-1;
                return FALSE;
            }
            pdd->ulFlags |= DDF_DIRTY;

            dw = ICDecompressBegin(pdd->hic, lpbi, &pdd->biBuffer);

            if (dw != ICERR_OK)
            {
                RPF(("    Compressor failed ICM_DECOMPRESS_BEGIN"));
		goto codec_error;
                return FALSE;
            }

            RPF(("    Decompressing '%4.4hs' to %dx%dx%d%s",(LPSTR)&lpbi->biCompression, PUSHBI(pdd->biBuffer),
			     	Is565(&pdd->biBuffer) ? (LPSTR) "(565)" : (LPSTR) ""
	       ));
            pdd->iDecompress = DECOMPRESS_BUFFER;

            _fmemcpy(pdd->aw,pdd->argbq, 256*sizeof(RGBQUAD));
            lpbi = &pdd->biBuffer;
            break;
    }
no_decomp:
    pdd->biDraw = pdd->biBuffer;
    pdd->biDraw.biSizeImage = 0;

    pdd->biDraw.biHeight = abs((int)pdd->biDraw.biHeight);

    if ((!(wFlags & DDF_JUSTDRAWIT)) && (lpbi->biCompression == BI_RGB))
    {
        //
        //  test the display device for this DIB format
        //
        w = QueryDraw(pdd, lpbi);

        //
        // get the bit depth of the screen device.
        //
        ScreenBitDepth = gwScreenBitDepth;

        if (ScreenBitDepth > 24)
            ScreenBitDepth = 32;        //???!!!

        // does the display support drawing 16bpp DIBs?
        // if it does not, treat it like a 24bpp device.

        if (ScreenBitDepth >= 24 && lpbi->biBitCount == 32 && !(w & PD_CAN_DRAW_DIB))
            ScreenBitDepth = 24;

        if (ScreenBitDepth >= 16 && lpbi->biBitCount == 16 && !(w & PD_CAN_DRAW_DIB))
            ScreenBitDepth = 24;

        //
        // check if the display driver isn't very good, for this format
        //
        if (!(w & PD_STRETCHDIB_1_1_OK))
        {
            pdd->ulFlags |= DDF_BITMAP;
        }

        //
        //  if the display driver isn't very good make a bitmap to copy into
        //  to draw.
        //
        switch (gfBitmap)
        {
            case 0:
                pdd->ulFlags &= ~DDF_BITMAP;
                break;

            case 1:
                pdd->ulFlags |= DDF_BITMAP;
                break;
        }

#ifndef WIN32	
	//
	// for 16/32 bit DIBs, the display may not support DIBS at all and
	// we should use bitmaps anyway just in case, even if the user
	// tried to override
	//
	if ((pdd->biDraw.biBitCount == 16 || pdd->biDraw.biBitCount == 32) &&
	    w == PD_CAN_DRAW_DIB)
	{
	    pdd->ulFlags |= DDF_BITMAP;
	}
#endif

        if ((dxSrc != dxDst || dySrc != dyDst) && !(w & PD_STRETCHDIB_1_N_OK))
            pdd->ulFlags |= DDF_STRETCH;

        if (dxSrc*2 == dxDst && dySrc*2 == dyDst && (w & PD_STRETCHDIB_1_2_OK))
            pdd->ulFlags &= ~DDF_STRETCH;

        if ((dxDst % dxSrc) == 0 && (dyDst % dySrc) == 0 && (w & PD_STRETCHDIB_1_2_OK))
            pdd->ulFlags &= ~DDF_STRETCH;

        if ((int)lpbi->biBitCount > ScreenBitDepth)
            pdd->ulFlags |= DDF_DITHER;

        //
        //  force halftone palette
        //
        if ((gfHalftone || (wFlags & DDF_HALFTONE)) && ScreenBitDepth <= 8)
            pdd->ulFlags |= DDF_DITHER;

        // NOTE we treat a convert up (ie 16->24) as a dither too.
        if ((int)lpbi->biBitCount > 8 && (int)lpbi->biBitCount < ScreenBitDepth)
            pdd->ulFlags |= DDF_DITHER;

	if (pdd->ulFlags & DDF_DITHER) {
	    if (lpbi->biBitCount == 16 && (w & PD_CAN_DRAW_DIB)) {
	    pdd->ulFlags &= ~DDF_DITHER;
		DPF(("Turning off DITHER for 16-bit DIBs, since we can draw them"));
	    }

	    if (lpbi->biBitCount == 32 && (w & PD_CAN_DRAW_DIB)) {
		pdd->ulFlags &= ~DDF_DITHER;
		DPF(("Turning off DITHER for 32-bit DIBs, since we can draw them"));
	    }

	    if (lpbi->biBitCount == 8 &&
				lpbi->biClrUsed <= 16 &&
				AreColorsAllGDIColors(lpbi)) {
		pdd->ulFlags &= ~DDF_DITHER;
		DPF(("Turning off DITHER for 8-bit DIBs already using the VGA colors"));
	    }
	}

	// force stretching in drawdib if we are dithering
	if ((pdd->ulFlags & DDF_DITHER) &&
	    ((dxSrc != dxDst) || (dySrc != dyDst))) {
		pdd->ulFlags |= DDF_STRETCH;
	}

        //
        // force a buffer if we dont have one.
        //
        if ((pdd->ulFlags & DDF_BUFFER) &&
            pdd->hic == NULL &&
            !(pdd->ulFlags & DDF_DITHER) &&
            !(pdd->ulFlags & DDF_STRETCH) &&
            !(pdd->ulFlags & DDF_BITMAP))
        {
            RPF(("    Using a buffer because DDF_BUFFER is set."));
            pdd->ulFlags |= DDF_STRETCH;    // force a 1:1 stretch
        }

	if (lpbi->biBitCount != 8
		    && lpbi->biBitCount != 16
		    && lpbi->biBitCount != 24
#ifndef _WIN32
		    && lpbi->biBitCount != 32
#endif
	    ) {
            DPF(("Turning off stretch for an unsupported format...."));
            pdd->ulFlags &= ~(DDF_STRETCH);
        }

    }

    //
    // delete the palette if we are changing who dithers.
    //
    if (pdd->hpal &&
        pdd->lpbi->biBitCount > 8 &&
        ((pdd->ulFlags ^ ulFlagsSave) & (DDF_DITHER)))
    {
        DPF(("    Dither person has changed..."));

        if (pdd->lpDitherTable)
        {
            DitherTerm(pdd->lpDitherTable);
            pdd->lpDitherTable = NULL;
        }

        if (hdc) {
            hPal = SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), TRUE);

            if (hPal == pdd->hpal)
	        RPF(("    Warning unselecting palette..."));
        }

        DeleteObject(pdd->hpal);
        if (pdd->hpalCopy)
            DeleteObject(pdd->hpalCopy);
        pdd->hpal = NULL;
        pdd->hpalCopy = NULL;
    }

    if (pdd->ulFlags & DDF_STRETCH)
    {
        /* the code for stretching *only* works on a 386+ */
        if (gf286 || pdd->biBuffer.biBitCount < 8)
        {
            RPF(("    Using GDI to stretch"));
            pdd->ulFlags &= ~DDF_STRETCH;
        }
        else
        {
            //
            // we have stretching to do, this requires extra
            // headers and buffers.
            //
            pdd->biStretch = pdd->biBuffer;
            pdd->biStretch.biWidth = dxDst;
            pdd->biStretch.biHeight = dyDst;
            pdd->biStretch.biSizeImage = DIBSIZEIMAGE(pdd->biStretch);

            pdd->pbStretch = GlobalAllocPtr(GHND,pdd->biStretch.biSizeImage);

            if (pdd->pbStretch == NULL)
            {
                RPF(("    No memory for stretch buffer, using GDI"));
                pdd->ulFlags &= ~DDF_STRETCH;
            }
            else
            {
                RPF(("    Stretching %dx%dx%d%s --> %dx%dx%d",
			    dxSrc, dySrc, (int)lpbi->biBitCount,
			    (LPSTR) (Is565(lpbi) ? "(565)":""),
			    dxDst, dyDst, (int)pdd->biStretch.biBitCount,
			    (LPSTR) (Is565(&pdd->biStretch) ? "(565)":"")
		   ));
                pdd->biDraw.biWidth = dxDst;
                pdd->biDraw.biHeight = dyDst;
                dxSrc = dxDst;
                dySrc = dyDst;
                lpbi = &pdd->biStretch;
            }
        }
    }

    if (pdd->ulFlags & DDF_DITHER)
    {
        pdd->ulFlags &= ~DDF_ANIMATE;        // cant  animate and dither!

        if (ScreenBitDepth <= 8)
            pdd->biDraw.biBitCount = 8;
        else if (lpbi->biBitCount <= 8)
            pdd->biDraw.biBitCount = lpbi->biBitCount;
        else
////////////pdd->biDraw.biBitCount = 24; //!!! what about 16bit DIB support
            pdd->biDraw.biBitCount = ScreenBitDepth;

        w = QueryDraw(pdd, &pdd->biDraw);

        if (w & PD_STRETCHDIB_1_1_OK)
            pdd->ulFlags &= ~DDF_BITMAP;
        else
            pdd->ulFlags |= DDF_BITMAP;

	// this is wrong isn't it ? biDraw will be set to
	// dxDst if we are stretching, or dxSrc if not. If we are asked to
	// dither and stretch together, and we choose to leave the stretching
	// to GDI, we will here set biDraw so that we ask the dither code
	// to dither from dx/dySrc to dx/dyDst - ie stretch and dither in
	// one go. Our current dither code will crash if you ask it to do this.
        dxSave = (int)pdd->biDraw.biWidth;
        dySave = (int)pdd->biDraw.biHeight;
#if 0
        pdd->biDraw.biWidth  = dxDst; // lpbi->biWidth;
        pdd->biDraw.biHeight = dyDst; // lpbi->biHeight;
#endif
	// !!! So DrawDibDraw will not DebugBreak
	pdd->biDraw.biWidth = dxSrc;
	pdd->biDraw.biHeight = dySrc;
        pdd->biDraw.biSizeImage = DIBSIZEIMAGE(pdd->biDraw);


        RPF(("    Dithering %dx%dx%d --> %dx%dx%d", PUSHBI(*lpbi), PUSHBI(pdd->biDraw)));

        //
        //  NOTE we need to use &pdd->biBuffer *not* lpbi because in the
        //  stretched case lpbi will point to pdd->biStretch and biStretch
        //  has NO COLOR TABLE
        //
        pdd->lpDitherTable = DitherInit(&pdd->biBuffer, &pdd->biDraw,
            &pdd->DitherProc, pdd->lpDitherTable);

        if (pdd->lpDitherTable == (LPVOID)-1 ||
            pdd->DitherProc == NULL ||
            !(pdd->pbDither = GlobalAllocPtr(GHND,pdd->biDraw.biSizeImage)))
        {
            if (pdd->lpDitherTable == (LPVOID)-1)
                pdd->lpDitherTable = NULL;

            if (pdd->lpDitherTable)
                DitherTerm(pdd->lpDitherTable);

            if (pdd->pbDither)
                GlobalFreePtr(pdd->pbDither);

            pdd->lpDitherTable = NULL;
            pdd->pbDither = NULL;
            pdd->biDraw.biBitCount = pdd->biBuffer.biBitCount;
            pdd->biDraw.biWidth  = dxSave;
            pdd->biDraw.biHeight = dySave;
            pdd->biDraw.biSizeImage = 0;
            pdd->ulFlags &= ~DDF_DITHER;

#ifdef DEBUG_RETAIL
            if (pdd->DitherProc)
                RPF(("    No Memory for dither tables!"));
            else
                RPF(("    No DitherProc!"));
#endif
        }
    }

    //
    // create a palette (if needed)
    //
    if ((gwRasterCaps & RC_PALETTE) &&
        pdd->biDraw.biBitCount <= 8 &&
        pdd->hpal == NULL)
    {
        pdd->hpal = CreateBIPalette(pdd->hpal, &pdd->biDraw);
        pdd->ulFlags |= DDF_NEWPALETTE;
    }

    //
    // make sure we treat the palette as new when starting/stopping animate
    //
    if (wFlagsChanged & DDF_ANIMATE)
    {
        pdd->ulFlags |= DDF_NEWPALETTE;
    }

    //
    // check for a identity palette
    //
    if (pdd->hpal == NULL)
    {
        pdd->ClrUsed = 0;
    }
    else if (pdd->ulFlags & DDF_NEWPALETTE)
    {
        GetObject(pdd->hpal,sizeof(int),(LPVOID)&pdd->ClrUsed);

        if (wFlagsChanged & DDF_ANIMATE)
            SetPalFlags(pdd->hpal,0,pdd->ClrUsed,0);

        if (IsIdentityPalette(pdd->hpal))
        {
            pdd->ulFlags |= DDF_IDENTITYPAL;
            pdd->iAnimateStart = 10;
        }
        else
        {
            pdd->ulFlags &= ~DDF_IDENTITYPAL;
            pdd->iAnimateStart = 0;
        }

        pdd->iAnimateLen = min(236,pdd->ClrUsed);
        pdd->iAnimateEnd = pdd->iAnimateStart + pdd->iAnimateLen;

        if (pdd->ulFlags & DDF_ANIMATE)
        {
            RPF(("    Palette animation"));
            SetPalFlags(pdd->hpal,pdd->iAnimateStart,pdd->iAnimateLen,PC_RESERVED);
        }
    }

    //
    // because of bugs in GDIs StretchDIBits (doing a stretch) we
    // always set the number of colors to be the maximum.
    //
    // this is not a big deal because we are mostly drawing full-color-table
    // DIBs any way.
    //
    if (pdd->biDraw.biBitCount <= 8)
        pdd->biDraw.biClrUsed = (1 << (int)pdd->biDraw.biBitCount);
    else
        pdd->biDraw.biClrUsed = 0;

    DrawDibSetPalette(hdd, pdd->hpalDraw);

    if (pdd->hpal)
    {
        if (pdd->ulFlags & DDF_IDENTITYPAL)
            RPF(("    Drawing with an identity palette"));
        else
            RPF(("    Drawing with a non-identity palette"));
    }

    if (pdd->uiPalUse == DIB_RGB_COLORS)
        RPF(("    Using DIB_RGB_COLORS"));
    else
        RPF(("    Using DIB_PAL_COLORS"));

    if (pdd->hpalDraw)
        RPF(("    Mapping to another palette"));

    if (pdd->ulFlags & DDF_BITMAP)
    {
        BOOL fGetDC;
        BOOL f;
        HWND hwndActive;

        RPF(("    Display driver slow for DIBs, using bitmaps"));

        if (fGetDC = (hdc == NULL))
        {
            hwndActive = GetActiveWindow();
            hdc = GetDC(hwndActive);
        }

        if (pdd->hdcDraw) {
	    if (hbmStockMono) {
		SelectObject(pdd->hdcDraw, hbmStockMono);
	    }
	} else /* if (!pdd->hdcDraw) */ {
	    pdd->hdcDraw = CreateCompatibleDC(hdc);
	}

        if (pdd->hbmDraw) {
	    // This fixes a memory leak.  Perhaps we can just use the old one?
	    DPF(("Freeing hbmDraw!\n"));
            DeleteObject(pdd->hbmDraw);
	}

        //
        // NOTE the bitmap must be as wide as the source DIB when we are
        // using SetDIBits() because SetDIBits() only takes a start scan not a (x,y)
        //
//      pdd->hbmDraw = CreateCompatibleBitmap(hdc, (int)pdd->biDraw.biWidth, (int)pdd->biDraw.biHeight);
        pdd->hbmDraw = CreateCompatibleBitmap(hdc, (int)pdd->biDraw.biWidth, dySrc);

        if (pdd->hbmDraw == NULL || pdd->hdcDraw == NULL)
            goto bitmap_fail;

        hbmStockMono = SelectObject(pdd->hdcDraw,pdd->hbmDraw);

        pdd->ulFlags |= DDF_NEWPALETTE;

#if USE_SETDI
        f = SetBitmapBegin(
                    &pdd->sd,       //  structure
                    hdc,            //  device
                    pdd->hbmDraw,   //  bitmap to set into
                    &pdd->biDraw,   //  --> BITMAPINFO of source
                    pdd->uiPalUse);
#else
        f = TRUE;
#endif
        if (!f)
        {
bitmap_fail:
            if (pdd->hdcDraw)
                DeleteDC(pdd->hdcDraw);

            if (pdd->hbmDraw)
                DeleteObject(pdd->hbmDraw);

            pdd->hdcDraw = NULL;
            pdd->hbmDraw = NULL;
            pdd->ulFlags &= ~DDF_BITMAP;
        }

        if (fGetDC)
        {
            ReleaseDC(hwndActive, hdc);
            hdc = NULL;
        }
    }

    //
    //  now try to decompress to a bitmap, we only decompress to
    //  bitmaps if the following is true.
    //
    //      the decompressor must decompress direct, we will not
    //      stretch/dither afterward
    //
    //      if on a palette device, the color table must be 1:1
    //
    //  we should check a decompressor flag
    //
    if (pdd->hic &&
        !(pdd->ulFlags & (DDF_STRETCH|DDF_DITHER)) &&
        gfBitmapX &&
	(pdd->lpDIBSection == NULL) &&
        (dxDst == pdd->lpbi->biWidth) &&
        (dyDst == pdd->lpbi->biHeight)
        )
    {

        if (pdd->ulFlags & DDF_BITMAP) {
            if (pdd->hbmDraw) {
                DrawDibQueryBitmapX(pdd);
            }
        } else {

            //even though we decided not to use bitmaps, it might still
            //be worth trying decompression to bitmaps. the DDF_BITMAP
            //flag is based on a comparison of StretchDIBits vs
            //SetDIBits+BitBlt. Decompressing to a bitmap could be
            //faster even when SetDIBits+Bitblt is slower
            // but in this case, if we fail, we have to make sure we don't
            // end up doing DDF_BITMAP as we know that's slower.

#if 0
            if (QueryDraw(pdd, &pdd->biBuffer) & PD_BITBLT_FAST) {

                BOOL fGetDC;
                HWND hwndActive;

                RPF(("    Not using BITMAPS, but trying Decomp to Bitmap"));

                if (fGetDC = (hdc == NULL))
                {
                    hwndActive = GetActiveWindow();
                    hdc = GetDC(hwndActive);
                }

                pdd->hdcDraw = CreateCompatibleDC(hdc);
                pdd->hbmDraw = CreateCompatibleBitmap(hdc, (int)pdd->biDraw.biWidth, (int)pdd->biDraw.biHeight);

                if ((pdd->hbmDraw != NULL) && (pdd->hdcDraw != NULL)) {

                    hbmStockMono = SelectObject(pdd->hdcDraw,pdd->hbmDraw);

                    if (fGetDC)
                    {
                        ReleaseDC(hwndActive, hdc);
                        hdc = NULL;
                    }

                    DrawDibQueryBitmapX(pdd);
                }

                if (!(pdd->ulFlags & DDF_CANBITMAPX)) {
                        if (pdd->hdcDraw) {
                            DeleteDC(pdd->hdcDraw);
			    pdd->hdcDraw = NULL;
			}

                        if (pdd->hbmDraw) {
                            DeleteObject(pdd->hbmDraw);
			    pdd->hbmDraw = NULL;
			}
                }
            }
#endif
        }
    }

#ifndef WIN32    // Note: pdci will be 0 for Win32
    //
    //  see if the decompressor can decompress directly to the screen
    //  doing everything, stretching and all.
    //
    if (pdd->hic && pdci && gfScreenX)
    {
        if (wFlags & DDF_BUFFER)
        {
            DPF(("    DDF_BUFFER specified, unable to decompress to screen"));
            goto cant_do_screen;
        }

	//
	// try to decompress to screen.
	//
        if (((gwRasterCaps & RC_PALETTE) && !(pdd->ulFlags & DDF_IDENTITYPAL)) ||
            (gwScreenBitDepth == 8 && !(gwRasterCaps & RC_PALETTE)) ||
            (pdd->ulFlags & (DDF_STRETCH|DDF_DITHER)) ||
            (ICDecompressExQuery(pdd->hic, 0,
				pdd->lpbi, NULL, 0, 0, pdd->dxSrc, pdd->dySrc,
				(LPBITMAPINFOHEADER) &biScreen, lpScreen,
				0, 0, pdd->dxDst, pdd->dyDst) != ICERR_OK))
	{
cant_do_screen:
	    ; // we can't decompress to the screen
	}
	else
	{   // we can decompress to the screen
	    pdd->ulFlags |= DDF_CLIPCHECK;  // we need clipping checking
	    pdd->ulFlags |= DDF_NEWPALETTE; // force check in DrawDibRealize
	    pdd->ulFlags |= DDF_CANSCREENX; // we can decompress to screen
	    pdd->ulFlags |= DDF_CLIPPED;    // we are initialized for clipped now

	    RPF(("    Can decompress '%4.4hs' to the SCREEN",(LPSTR)&pdd->lpbi->biCompression));
	}
    }

#if 0
    //
    //  see if we can draw direct to the screen
    //
    if (pdd->hic && pdci && gfDrawX)
    {
        if (TRUE)
            goto cant_draw_screen;

        pdd->ulFlags |= DDF_CLIPCHECK;  // we need clipping checking
        pdd->ulFlags |= DDF_NEWPALETTE; // force check in DrawDibRealize
        pdd->ulFlags |= DDF_CANDRAWX;   // we can decompress to screen
        pdd->ulFlags |= DDF_CLIPPED;    // we are initialized for clipped now

        RPF(("    Can draw to the SCREEN"));

cant_draw_screen:
        ;
    }
#endif

#endif

    //
    // see if the source cordinates need translated
    //
    if (abs((int)pdd->biBuffer.biWidth)  != (int)pdd->lpbi->biWidth ||
        abs((int)pdd->biBuffer.biHeight) != (int)pdd->lpbi->biHeight)
    {
        pdd->ulFlags |= DDF_XLATSOURCE;
    }

    return TRUE;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibEnd | This function frees a DrawDib context.
*
* @parm HDRAWDIB | hdd | Specifies the handle to the DrawDib context to free.
*
* @rdesc Returns TRUE if successful.
*
* @comm Any flags set or palette changes made by <f DrawDibBegin> or
*       <f DrawDibDraw> is discarded by <f DrawDibEnd>.
*
**************************************************************************/
BOOL VFWAPI DrawDibEnd(HDRAWDIB hdd)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    DrawDibFree(pdd, FALSE, FALSE);

    return TRUE;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibTime | Returns timing information about
*       the drawing during debug operation.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm LPDRAWDIBTIME | lpddtime | Specifies a pointer to
*       a <t DRAWDIBTIME> structure.
*
* @rdesc Returns TRUE if successful.
*
**************************************************************************/
BOOL VFWAPI DrawDibTime(HDRAWDIB hdd, LPDRAWDIBTIME lpddtime)
{
#ifdef DEBUG_RETAIL
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (lpddtime)
        *lpddtime = pdd->ddtime;

    if (pdd->ddtime.timeCount > 0)
    {
        RPF(("timeCount:       %u",        (UINT)pdd->ddtime.timeCount));
        RPF(("timeDraw:        %ums (%u)", (UINT)pdd->ddtime.timeDraw, (UINT)pdd->ddtime.timeDraw/(UINT)pdd->ddtime.timeCount));
        RPF(("timeDecompress:  %ums (%u)", (UINT)pdd->ddtime.timeDecompress, (UINT)pdd->ddtime.timeDecompress/(UINT)pdd->ddtime.timeCount));
        RPF(("timeDither:      %ums (%u)", (UINT)pdd->ddtime.timeDither, (UINT)pdd->ddtime.timeDither/(UINT)pdd->ddtime.timeCount));
        RPF(("timeStretch:     %ums (%u)", (UINT)pdd->ddtime.timeStretch, (UINT)pdd->ddtime.timeStretch/(UINT)pdd->ddtime.timeCount));
        RPF(("timeSetDIBits:   %ums (%u)", (UINT)pdd->ddtime.timeSetDIBits, (UINT)pdd->ddtime.timeSetDIBits/(UINT)pdd->ddtime.timeCount));
        RPF(("timeBlt:         %ums (%u)", (UINT)pdd->ddtime.timeBlt, (UINT)pdd->ddtime.timeBlt/(UINT)pdd->ddtime.timeCount));
    }

    pdd->ddtime.timeCount      = 0;
    pdd->ddtime.timeDraw       = 0;
    pdd->ddtime.timeDecompress = 0;
    pdd->ddtime.timeDither     = 0;
    pdd->ddtime.timeStretch    = 0;
    pdd->ddtime.timeSetDIBits  = 0;
    pdd->ddtime.timeBlt        = 0;

    return TRUE;
#else
    return FALSE;
#endif
}


/*
 * CopyPal -- copy a palette
 */
HPALETTE CopyPal(HPALETTE hpal)
{
    NPLOGPALETTE    pLogPal = NULL;
    HPALETTE        hpalNew = NULL;
    int             iSizePalette = 0;           // size of entire palette

    if (hpal == NULL)
        return NULL;

    GetObject(hpal,sizeof(iSizePalette),(LPSTR)&iSizePalette);

    pLogPal = (NPLOGPALETTE)LocalAlloc(LPTR, sizeof(LOGPALETTE)
            + iSizePalette * sizeof(PALETTEENTRY));

    if (!pLogPal)
        return NULL;

    pLogPal->palVersion = 0x300;
    pLogPal->palNumEntries = iSizePalette;

    GetPaletteEntries(hpal, 0, iSizePalette, pLogPal->palPalEntry);

    hpal = CreatePalette(pLogPal);

    LocalFree((HLOCAL) pLogPal);

    return hpal;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api HPALETTE | DrawDibGetPalette | This function obtains the palette
*      used by a DrawDib context.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @rdesc Returns a handle for the palette if successful, otherwise
*        it returns NULL.
*
* @comm Use <f DrawDibRealize> instead of this function
*       to realize the correct palette in response to a window
*       message. You should rarely need to call this function.
*
*       Applications do not have exclusive use of the palette
*       obtained with this function. Applications should not
*       free the palette or assign it to a display context
*       with functions such as <f SelectPalette>. Applications
*       should also anticipate that some other
*       application can invalidate the handle. The palette
*       handle might also become invalid after the next use of a DrawDib function.
*
*       This function returns a valid handle only after
*       <f DrawDibBegin> has been used without pairing it with
*       <f DrawDibEnd>, or if <f DrawDibDraw> has been used.
*
* @xref <f DrawDibSetPalette> <f DrawDibRealize>
*
**************************************************************************/
HPALETTE VFWAPI DrawDibGetPalette(HDRAWDIB hdd)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return NULL;

    if (pdd->hpalDraw)
        return pdd->hpalDraw;
    else {
        // For palette animation we can't return a different palette than the
        // real palette, so return hpal, not hpalCopy.
        // Just trust me.  - Toddla

        if (pdd->ulFlags & DDF_ANIMATE)
            return pdd->hpal;

        // In order for us to play direct to screen, etc, all palette
        // realization has to come through DrawDibRealize.  But that won't
        // always happen.  Some apps will always ask us for our palette and
        // realize it themselves.  So if we give them a copy of our palette,
        // and never our true palette, when our play code realizes the true
        // palette it's guarenteed to cause an actual palette change and we'll
        // correctly detect playing to screen. (BUG 1761)

        if (pdd->hpalCopy == NULL)
            pdd->hpalCopy = CopyPal(pdd->hpal);

        return pdd->hpalCopy;
    }
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibSetPalette | This function sets the palette
*      used for drawing device independent bitmaps.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm HPALETTE | hpal | Specifies a handle to the palette.
*       Specify NULL to use the default palette.
*
* @rdesc Returns TRUE if successful.
*
* @comm Use this function when the application needs to realize an
*   alternate palette. The function forces the DrawDib context to use the
*   specified palette, possibly at the expense of image quality.
*
*   Do not free a palette assigned to a DrawDib context until
*   either a new palette replaces it (for example, if hpal1 is the
*   current palette, replacing it with DrawDibSetPalette(hdd, hpal2)),
*   or until the palette handle for the DrawDib context is set to
*   to the default palette (for example, DrawDibSetPalette(hdd, NULL)).
*
* @xref <f DrawDibGetPalette>
*
**************************************************************************/
BOOL VFWAPI DrawDibSetPalette(HDRAWDIB hdd, HPALETTE hpal)
{
    PDD pdd;
    int i;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (hpal == pdd->hpalCopy)
        hpal = NULL;

    if (pdd->hpalDraw != hpal)
        pdd->ulFlags |= DDF_NEWPALETTE;

    pdd->hpalDraw = hpal;       // always set this variable

    if (pdd->hpal == NULL)      // no palette to dink with
        return TRUE;

    if (pdd->biDraw.biBitCount > 8) // make sure we are drawing palettized
        return TRUE;

    if (pdd->ulFlags & DDF_ANIMATE)
    {
        DPF(("DrawDibSetPalette called while in DDF_ANIMATE mode!"));
    }

    //
    //  we are now using PAL colors...
    //
    pdd->uiPalUse = DIB_PAL_COLORS;

    if (pdd->hpalDraw != NULL)
    {
        /* Set up table for BI_PAL_COLORS non 1:1 drawing */

        //
        //  map all of our colors onto the given palette
        //  NOTE we can't use the select background trick
        //  because the given palette <hpalDraw> may have
        //  PC_RESERVED entries in it.
        //
        // SendSetPalette(pdd);

        for (i=0; i < 256; i++)
        {
            if (pdd->biBuffer.biBitCount == 8)
            {
                pdd->aw[i] = GetNearestPaletteIndex(pdd->hpalDraw,
                                                    RGB(pdd->argbq[i].rgbRed,
                                                        pdd->argbq[i].rgbGreen,
                                                        pdd->argbq[i].rgbBlue));
            }
            else
            {
                PALETTEENTRY pe;
                GetPaletteEntries(pdd->hpal, i, 1, &pe);
                pdd->aw[i] = GetNearestPaletteIndex(pdd->hpalDraw,
                                                    RGB(pe.peRed,
                                                        pe.peGreen,
                                                        pe.peBlue));
            }
        }

        for (; i<256; i++)
            pdd->aw[i] = 0;
    }
    else
    {
        /* Set up table for BI_PAL_COLORS 1:1 drawing */

        // SendSetPalette(pdd);
        for (i=0; i<(int)pdd->ClrUsed; i++)
            pdd->aw[i] = i;

        for (; i<256; i++)
            pdd->aw[i] = 0;
    }

    return TRUE;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibChangePalette | This function sets the palette entries
*      used for drawing device independent bitmaps.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm int | iStart | Specifies the starting palette entry number.
*
* @parm int | iLen | Specifies the number of palette entries.
*
* @parm LPPALETTEENTRY | lppe | Specifies a pointer to an
*       array of palette entries.
*
* @rdesc Returns TRUE if successful.
*
* @comm
*   Use this function when the DIB color table changes and
*   other parameters stay constant. This function changes
*   the physical palette only if the current
*   DrawDib palette is curently realized by calling <f DrawDibRealize>.
*
*   The DIB color table must be changed by the user or
*   the next use of <f DrawDibDraw> without the DDF_SAME_DRAW flag
*   implicity calls <f DrawDibBegin>.
*
*   If the DDF_ANIMATE flag is not set in the previous call to
*   <f DrawDibBegin> or <f DrawDibDraw>, this function will
*   animate the palette. In this case, update the DIB color
*   table from the palette specified by <p lppe> and use
*   <f DrawDibRealize> to realize the updated palette. Redraw
*   the image to see the updated colors.
*

* @xref <f DrawDibSetPalette> <f DrawDibRealize>
*
**************************************************************************/
BOOL VFWAPI DrawDibChangePalette(HDRAWDIB hdd, int iStart, int iLen, LPPALETTEENTRY lppe)
{
    PDD pdd;
    int i;

    int iStartSave;
    int iLenSave;
    LPPALETTEENTRY lppeSave;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (pdd->biBuffer.biBitCount != 8)
        return FALSE;

    if (lppe == NULL || iStart < 0 || iLen + iStart > 256)
        return FALSE;

    for (i=0; i<iLen; i++)
    {
	(*(pdd->lpargbqIn))[iStart+i].rgbRed   = lppe[i].peRed;
	(*(pdd->lpargbqIn))[iStart+i].rgbGreen = lppe[i].peGreen;
	(*(pdd->lpargbqIn))[iStart+i].rgbBlue  = lppe[i].peBlue;
    }

    //
    // handle a palette change for 8bit dither
    //
    if (pdd->lpDitherTable)
    {
        for (i=0; i<iLen; i++)
        {
            pdd->argbq[iStart+i].rgbRed   = lppe[i].peRed;
            pdd->argbq[iStart+i].rgbGreen = lppe[i].peGreen;
            pdd->argbq[iStart+i].rgbBlue  = lppe[i].peBlue;
        }

        pdd->lpDitherTable = DitherInit(pdd->lpbi, &pdd->biDraw, &pdd->DitherProc, pdd->lpDitherTable);
    }
    else if (pdd->hpalDraw)
    {
        SetPaletteEntries(pdd->hpal, iStart, iLen, lppe);
        pdd->ulFlags |= DDF_NEWPALETTE;

        for (i=iStart; i<iLen; i++)
        {
            pdd->aw[i] = GetNearestPaletteIndex(pdd->hpalDraw,
                RGB(lppe[i].peRed,lppe[i].peGreen,lppe[i].peBlue));
        }
    }
    else if (pdd->ulFlags & DDF_ANIMATE)
    {
        for (i=iStart; i<iStart+iLen; i++)
        {
            if (i >= pdd->iAnimateStart && i < pdd->iAnimateEnd)
                lppe[i-iStart].peFlags = PC_RESERVED;
            else
                lppe[i-iStart].peFlags = 0;
        }

        /* Change iLen, iStart so that they only include the colors
        ** we can actually animate.  If we don't do this, the
        ** AnimatePalette() call just returns without doing anything.
        */

        iStartSave = iStart;
        iLenSave   = iLen;
        lppeSave   = lppe;

        if (iStart < pdd->iAnimateStart)
        {
            iLen -= (pdd->iAnimateStart - iStart);
            lppe += (pdd->iAnimateStart - iStart);
            iStart = pdd->iAnimateStart;
        }

        if (iStart + iLen > pdd->iAnimateEnd)
            iLen = pdd->iAnimateEnd - iStart;

        AnimatePalette(pdd->hpal, iStart, iLen, lppe);

        //
        //  any colors we could not animate, map to nearest
        //
        for (i=iStartSave; i<iStartSave+iLenSave; i++)
        {
            if (i >= pdd->iAnimateStart && i < pdd->iAnimateEnd)
                pdd->aw[i] = i;
            else
                pdd->aw[i] = GetNearestPaletteIndex(pdd->hpal,
                    RGB(lppeSave[i-iStartSave].peRed,
                        lppeSave[i-iStartSave].peGreen,
                        lppeSave[i-iStartSave].peBlue));
        }
    }
    else if (pdd->hpal)
    {
        SetPaletteEntries(pdd->hpal, iStart, iLen, lppe);
        pdd->ulFlags |= DDF_NEWPALETTE;
    }
    else
    {
        for (i=0; i<iLen; i++)
        {
            ((RGBQUAD *)pdd->aw)[iStart+i].rgbRed   = lppe[i].peRed;
            ((RGBQUAD *)pdd->aw)[iStart+i].rgbGreen = lppe[i].peGreen;
            ((RGBQUAD *)pdd->aw)[iStart+i].rgbBlue  = lppe[i].peBlue;
        }

        if (pdd->hbmDraw)
            pdd->ulFlags |= DDF_NEWPALETTE;
    }

#ifdef DIBSECTION
    if (pdd->lpDIBSection) {

	// the colour table of a DIB Section is not changed when the palette
	// used to create it changes. We need to explicitly change it.
	SetDIBColorTable(pdd->hdcDraw, iStart, iLen,
		&(*(pdd->lpargbqIn))[iStart]);
    }
#endif

//    We'll break buggy apps if we delete a palette we've given them
//    even though we told them not to use it.
//
//    if (pdd->hpalCopy)
//      DeleteObject(pdd->hpalCopy);
//    pdd->hpalCopy = NULL;

    return TRUE;
}

/**************************************************************************
*
* @doc EXTERNAL DrawDib
*
* @api UINT | DrawDibRealize | This function realizes palette
*      of the display context specified into the DrawDib context.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm HDC | hdc | Specifies a handle to the display context containing
*       the palette.
*
* @parm BOOL | fBackground | If set to a nonzero value,
*       the selected palette is selected as a background palette.
*       If this is set to zero and the device context is attached
*       to a window, the logical palette is a foreground palette when
*       the window has the input focus. (The device context is attached
*       to a window if it was obtained by using the <f GetDC> function
*       or if the window-class style is CS_OWNDC.)
*
* @rdesc Returns number of entries in the logical palette
*        mapped to different values in the system palette. If
*        an error occurs or no colors were updated, it returns zero.
*
* @comm  This function should only be used to
*        handle a <m WM_PALETTECHANGE> or <m WM_QUERYNEWPALETTE>
*        message, or used in conjunction with the DDF_SAME_HDC flag
*        to prepare a display context prior to calling <f DrawDibDraw>
*        multiple times.
*
* @ex    The following example shows how the function is used to
*        handle a <m WM_PALETTECHANGE> or <m WM_QUERYNEWPALETTE>
*        message: |
*
*               case WM_PALETTECHANGE:
*                   if ((HWND)wParam == hwnd)
*                       break;
*
*               case WM_QUERYNEWPALETTE:
*                   hdc = GetDC(hwnd);
*
*                   f = DrawDibRealize(hdd, hdc, FALSE) > 0;
*
*                   ReleaseDC(hwnd, hdc);
*
*                   if (f)
*                       InvalidateRect(hwnd, NULL, TRUE);
*                   break;
*
* @ex   The following example shows using <f DrawDibRealize> prior to
*       calling <f DrawDibDraw> multiple times: |
*
*               hdc = GetDC(hwnd);
*               DrawDibRealize(hdd, hdc, fBackground);
*               DrawDibDraw(hdd, hdc, ..........., DDF_SAME_DRAW|DDF_SAME_HDC);
*               DrawDibDraw(hdd, hdc, ..........., DDF_SAME_DRAW|DDF_SAME_HDC);
*               DrawDibDraw(hdd, hdc, ..........., DDF_SAME_DRAW|DDF_SAME_HDC);
*               ReleaseDC(hwnd, hdc);
*
* @ex   The following example shows using <f DrawDibRealize> with <f DDF_ANIMATE>
*       and (f DrawDibChangePalette> to do palette animation |
*
*               hdc = GetDC(hwnd);
*               DrawDibBegin(hdd, ....., DDF_ANIMATE);
*               DrawDibRealize(hdd, hdc, fBackground);
*               DrawDibDraw(hdd, hdc, ...., DDF_SAME_DRAW|DDF_SAME_HDC);
*               DrawDibChangePalette(hdd, ....);
*               ReleaseDC(hwnd, hdc);
*
* @comm To draw an image mapped to another palette use <f DrawDibSetPalette>.
*
*        To make <f DrawDibDraw> select its palette as a background palette
*        use the DDF_BACKGROUNDPAL flag and not this function.
*
*        While the DrawDib palette is selected into the display context,
*        do not call <f DrawDibEnd>, <f DrawDibClose>, <f DrawDibBegin>, or
*        <f DrawDibDraw> (with a different draw/format) on the same DrawDib
*        context <p hdd>. These can free the selected palette
*        while it is being used by your display context and cause
*        a GDI error.
*
*        <f DrawDibRealize> will return 0 if called before a
*        <f DrawDibBegin> or <f DrawDibDraw>
*
* @xref <f SelectPalette>
*
**************************************************************************/
UINT VFWAPI DrawDibRealize(HDRAWDIB hdd, HDC hdc, BOOL fBackground)
{
    PDD pdd;
    HPALETTE hpal;
    UINT u;

    if (hdc == NULL)
	return 0;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return 0;

    if (IsScreenDC(hdc))
        pdd->ulFlags &= ~DDF_MEMORYDC;
    else
        pdd->ulFlags |= DDF_MEMORYDC;

    if (pdd->ulFlags & DDF_MEMORYDC)
        DPF(("Drawing to a memory DC"));

    SetStretchBltMode(hdc, COLORONCOLOR);

    //
    // what palette should we realize
    //
    hpal = pdd->hpalDraw ? pdd->hpalDraw : pdd->hpal;

    //
    // if we dont have a palette, we have nothing to realize
    // still call DrawDibPalChange though
    //
    if (hpal == NULL)
    {
        if (pdd->ulFlags & DDF_NEWPALETTE)
        {
            DrawDibPalChange(pdd, hdc, hpal);
            pdd->ulFlags &= ~DDF_NEWPALETTE;
        }

        return 0;
    }

// !!! There is a bug in GDI that will not map an identity palette 1-1 into
// !!! the system palette every time, which hoses us and makes it look like
// !!! dog spew.  This ICKITY-ACKITY-OOP code will flush the palette and
// !!! prevent the bug... BUT it introduces another bug where if we are a
// !!! background app, we hose everybody else's palette but ours.  So let's
// !!! live with the GDI bug.  One other thing... attempting this fix will
// !!! cause the bug to repro more often than it would have if you had left
// !!! it alone, unless you do the fix JUST RIGHT!  I don't trust myself
// !!! that much.
#if 0
    if ((pdd->ulFlags & DDF_NEWPALETTE) && (pdd->ulFlags & DDF_IDENTITYPAL) &&
                !fBackground)
    {
        //
        // this will flush the palette clean to avoid a GDI BUG!!!
        //
        SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC);
        SetSystemPaletteUse(hdc, SYSPAL_STATIC);
    }
#endif

    //
    // select and realize it
    //
    SelectPalette(hdc, hpal, fBackground);
    u = RealizePalette(hdc);

    // !!! If two DrawDib instances share the same palette handle, the second
    // one will not change any colours and u will be 0, and it will not stop
    // decompressing to screen or recompute stuff for bitmaps when it goes
    // into the background and it will get a messed up palette.
    // !!! This is a known bug we don't care about
    //
    // this should be fixed by the hpalCopy stuff.

    if (u > 0 || (pdd->ulFlags & DDF_NEWPALETTE))
    {
        pdd->ulFlags |= DDF_NEWPALETTE;
        DrawDibPalChange(pdd, hdc, hpal);
        pdd->ulFlags &= ~DDF_NEWPALETTE;
    }

    return u;
}

/**************************************************************************
* @doc EXTERNAL DrawDib VFW11
*
* @api LPVOID | DrawDibGetBuffer | This function returns the pointer
*      to the DrawDib decompress buffer.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm LPBITMAPINFOHEADER | lpbi | Specifies a pointer to a
*        <t BITMAPINFOHEADER> structure.
*
* @parm DWORD | dwSize | Specifies the size of the buffer pointed to by <p lpbi>
*
* @parm DWORD | dwFlags | Set to zero.
*
* @rdesc Returns a pointer to the buffer used by DrawDib for decompression,
*        or NULL if no buffer is used. If <p lpbi> is not NULL,
*        it is filled in with a copy of the <t BITMAPINFOHEADER>
*        describing the buffer.
*
*        The structure for <p lpbi> must have room for a
*        <t BITMAPINFOHEADER> and 256 colors.
*
**************************************************************************/

LPVOID VFWAPI DrawDibGetBuffer(HDRAWDIB hdd, LPBITMAPINFOHEADER lpbi, DWORD dwSize, DWORD dwFlags)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return NULL;

    if (lpbi)
    {
        hmemcpy(lpbi, &pdd->biBuffer,
            min(dwSize, pdd->biBuffer.biSize + 256*sizeof(RGBQUAD)));
    }

    return pdd->pbBuffer;
}

LPVOID VFWAPI DrawDibGetBufferOld(HDRAWDIB hdd, LPBITMAPINFOHEADER lpbi)
{
    return DrawDibGetBuffer(hdd, lpbi, sizeof(BITMAPINFOHEADER), 0);
}

/**************************************************************************
* @doc EXTERNAL DrawDibStart
*
* @api BOOL | DrawDibStart | This function prepares a DrawDib
*      context for streaming playback.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm LONG | rate | Specifies the playback rate (in microseconds per frame).
*
* @rdesc Returns TRUE if successful.
*
* @xref <f DrawDibStop>
*
**************************************************************************/
BOOL VFWAPI DrawDibStart(HDRAWDIB hdd, DWORD rate)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (pdd->hic == (HIC)-1)
        return FALSE;

    // if the codec does not care about this message dont fail.

    if (pdd->hic != NULL)
        ICSendMessage(pdd->hic, ICM_DRAW_START, rate, 0);

    return TRUE;
}

/**************************************************************************
* @doc EXTERNAL DrawDibStop
*
* @api BOOL | DrawDibStop | This function frees the resources
*      used by a DrawDib context for streaming playback.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @rdesc Returns TRUE if successful.
*
* @xref <f DrawDibStart>
*
**************************************************************************/
BOOL VFWAPI DrawDibStop(HDRAWDIB hdd)
{
    PDD pdd;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (pdd->hic == (HIC)-1)
        return FALSE;

    if (pdd->hic != NULL)
        ICSendMessage(pdd->hic, ICM_DRAW_STOP, 0, 0);

    return TRUE;
}

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibUpdate | This macro updates the last
*      buffered frame drawn.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm HDC | hdc | Specifies a handle to the display context.
*
* @parm int | xDst | Specifies the x-coordinate of the upper left-corner
*       of the destination rectangle. Coordinates are specified
*       in MM_TEXT client coordinates.
*
* @parm int | yDst | Specifies the y-coordinate of the upper-left corner
*       of the destination rectangle.  Coordinates are specified
*       in MM_TEXT client coordinates.
*
* @rdesc Returns TRUE if successful.
*
* @comm This macro uses <f DrawDibDraw> to send the DDF_UPDATE flag
*       to the DrawDib context.
*
**************************************************************************/

/**************************************************************************
* @doc EXTERNAL DrawDib
*
* @api BOOL | DrawDibDraw | This function draws a device independent
*      bitmap to the screen.
*
* @parm HDRAWDIB | hdd | Specifies a handle to a DrawDib context.
*
* @parm HDC | hdc | Specifies a handle to the display context.
*
* @parm int | xDst | Specifies the x-coordinate of the upper left-corner
*       of the destination rectangle. Coordinates are specified
*       in MM_TEXT client coordinates.
*
* @parm int | yDst | Specifies the y-coordinate of the upper-left corner
*       of the destination rectangle. Coordinates are specified
*       in MM_TEXT client coordinates.
*
* @parm int | dxDst | Specifies the width of the destination rectangle.
*       The width is specified in MM_TEXT client coordinates. If
*       <p dxDst> is -1, the width of the bitmap is used.
*
* @parm int | dyDst | Specifies the height of the destination rectangle.
*       The height is specified in MM_TEXT client coordinates. If
*       <p dyDst> is -1, the height of the bitmap is used.
*
* @parm LPBITMAPINFOHEADER | lpbi | Specifies a pointer to the
*       <t BITMAPINFOHEADER> structure for the bitmap. The color
*       table for the DIB follows the format information. The
*       height specified for the DIB in the structure must be
*       positive (that is, this function will not draw inverted DIBs).
*
* @parm LPVOID | lpBits | Specifies a pointer to the buffer
*       containing the bitmap bits.
*
* @parm int | xSrc | Specifies the x-coordinate of the upper-left corner
*       source rectangle. Coordinates are specified in pixels.
*       The coordinates (0,0) represent the upper left corner
*       of the bitmap.
*
* @parm int | ySrc | Specifies the y-coordinate of the upper left corner
*       source rectangle. Coordinates are specified in pixels.
*       The coordinates (0,0) represent the upper left corner
*       of the bitmap.
*
* @parm int | dxSrc | Specifies the width of the source rectangle.
*       The width is specified in pixels.
*
* @parm int | dySrc | Specifies the height of the source rectangle.
*       The height is specified in pixels.
*
* @parm UINT | wFlags | Specifies any applicable flags for drawing.
*       The following flags are defined:
*
* @flag DDF_UPDATE | Indicates the last buffered bitmap is to be redrawn.
*       If drawing fails with this flag, a buffered image is not available
*       and a new image needs to be specified before the display is updated.
*
* @flag DDF_SAME_HDC | Assumes the display context has been prepared with
*       <f DrawDibRealize> prior to this call and <f DrawDibDraw> should not
*       initialize it.
*
* @flag DDF_SAME_DRAW | Uses the drawing parameters previously
*       specified for this function.  Use this flag only
*       if <p lpbi>, <p dxDst>, <p dyDst>, <p dxSrc>, and <p dySrc>
*       have not changed since using <f DrawDibDraw> or <f DrawDibBegin>.
*       Normally <f DrawDibDraw> checks the parameters, and if they
*       have changed, <f DrawDibBegin> prepares the DrawDib context
*       for drawing.
*
* @flag DDF_DONTDRAW | Indicates the frame is not to be drawn and will
*       later be recalled with the <f DDF_UPDATE> flag. DrawDib does
*       not buffer an image if an offscreen buffer does not exist.
*       In this case, DDF_DONTDRAW draws the frame to the screen and
*       the subsequent use of DDF_UPDATE fails. DrawDib does
*       guarantee that the following will
*       always draw "image" B to the screen.
*
*           DrawDibDraw(hdd, ..., lpbiA, ..., DDF_DONTDRAW);
*
*           DrawDibDraw(hdd, ..., lpbiB, ..., DDF_DONTDRAW);
*
*           DrawDibDraw(hdd, ..., NULL,  ..., DDF_UPDATE);
*
*       The DDF_UPDATE and DDF_DONTDRAW flags are used
*       together to create composite images
*       offscreen, and then do a final update when finished.
*
* @flag DDF_HURRYUP | Indicates the data does not have to
*       drawn (that is, it can be dropped) and the DDF_UPDATE flags will
*       not be used to recall this information. DrawDib looks at
*       this data only if it is required to build the next frame, otherwise
*       the data is ignored.
*
*       This flag is usually used to resynchronize video and audio. When
*       resynchronizing data, applications should send the image
*       with this flag in case the driver needs to
*       to buffer the frame to decompress subsequent frames.
*
* @flag DDF_UPDATE | Indicates the last buffered bitmap is to be redrawn.
*       If drawing fails with this flag, a buffered image is not available
*       and a new image needs to be specified before the display is updated.
*       For more information, see the <f DDF_DONTDRAW> flag.
*
* @flag DDF_BACKGROUNDPAL | Realizes the palette used for drawing
*       in the background leaving the actual palette used for display
*       unchanged.  (This flag is valid only if DDF_SAME_HDC is not set.)
*
* @flag DDF_HALFTONE | Always dithers the DIB to a standard palette
*       regardless of the palette of the DIB. If using <f DrawDibBegin>,
*       set this flag for it rather than <f DrawDibDraw>.
*
* @flag DDF_NOTKEYFRAME | Indicates the DIB data is not a key frame.
*
* @flag DDF_HURRYUP | Indicates the DIB data does not have to
*       drawn (that is, it can be dropped). This flag is usually
*       used to resynchronize the video to the audio. When
*       resynchronizing data, applications should send the image
*       with this flag in case the driver needs to
*       to buffer the frame to decompress subsequent frames.
*
* @rdesc Returns TRUE if successful.
*
* @comm This function replaces <f StretchDIBits> and supports
*       decompression of bitmaps by installable compressors.
*       This function dithers true color bitmaps properly on
*       8-bit display devices.
*
**************************************************************************/

BOOL VFWAPI DrawDibDraw(HDRAWDIB hdd,
                            HDC      hdc,
                            int      xDst,
                            int      yDst,
                            int      dxDst,
                            int      dyDst,
                            LPBITMAPINFOHEADER lpbi,
                            LPVOID   lpBits,
                            int      xSrc,
                            int      ySrc,
                            int      dxSrc,
                            int      dySrc,
                            UINT     wFlags)
{
    PDD     pdd;
    BOOL    f;
    RECT    rc;
    DWORD   icFlags;
    DWORD   dw;

    if ((pdd = DrawDibLock(hdd)) == NULL)
        return FALSE;

    if (hdc == NULL)
        return FALSE;

    if (wFlags & DDF_UPDATE)
    {
        lpbi = pdd->lpbi;

        dxDst = pdd->dxDst;
        dyDst = pdd->dyDst;
        dxSrc = pdd->dxSrc;
        dySrc = pdd->dySrc;
    }
    else
    {
        if (lpbi == NULL)
            return FALSE;

        //
        // fill in defaults.
        //
        if (dxSrc < 0)
            dxSrc = (int)lpbi->biWidth - xSrc;

        if (dySrc < 0)
            dySrc = (int)lpbi->biHeight - ySrc;

        if (dxDst < 0)
            dxDst = dxSrc;

        if (dyDst < 0)
            dyDst = dySrc;
    }
#ifdef DEBUG_RETAIL
    if (xSrc  <  0 ||
        ySrc  <  0 ||
        dxSrc <= 0 ||
        dySrc <= 0 ||
        xSrc + dxSrc > (int)lpbi->biWidth ||
        ySrc + dySrc > (int)lpbi->biHeight)
    {
        RPF(("DrawDibBegin(): bad source parameters [%d %d %d %d]", xSrc, ySrc, dxSrc, dySrc));
//      return 0;   // see what happens.
    }
#endif

    if (dxSrc == 0 || dySrc == 0)       // !!! || dxDst == 0 || dyDst == 0)
        return FALSE;

    //
    // check and make sure the params of the draw has not changed
    //
    if (!(wFlags & (DDF_SAME_DRAW|DDF_UPDATE)) &&
        !(DibEq(pdd->lpbi, lpbi) &&
          !(((UINT)pdd->ulFlags ^ wFlags) & DDF_HALFTONE) &&
        pdd->dxDst == dxDst &&
        pdd->dyDst == dyDst &&
        pdd->dxSrc == dxSrc &&
        pdd->dySrc == dySrc))
    {
        wFlags &= ~(DDF_UPDATE | DDF_FULLSCREEN);
        if (!DrawDibBegin(hdd, hdc, dxDst, dyDst, lpbi, dxSrc, dySrc, wFlags))
            return FALSE;
    }

    TIMEINC();      // should we include DibEq?
    TIMESTART(timeDraw);

    // convert to DIB cordinates
    ySrc = (int)pdd->lpbi->biHeight - (ySrc + dySrc);

    //
    // Initialize the DC:  We need to realize the palette if we are not
    // guarenteed to be using the same DC as before, if we've been told we
    // have a new palette, or if we are mapping to somebody else's palette.
    // The owner of the palette could be changing it on us all the time or
    // doing who knows what, so to be safe we will realize it every frame.
    // If nothing's changed, this should be a really cheap operation, and
    // it doesn't appear to be causing any palette fights that end in somebody
    // getting hurt.  This is required for Magic School Bus, and PageMaster,
    // at the very least. (WIN95B 12204 and 9637)
    //
    if (!(wFlags & DDF_SAME_HDC) || (pdd->ulFlags & DDF_NEWPALETTE) ||
							pdd->hpalDraw)
    {
        //
        // image will be totally clipped anyway
        //
        if (GetClipBox(hdc, &rc) == NULLREGION)
        {
            wFlags |= DDF_DONTDRAW;
        }

        //
        // select and realize the palette.
        //
        // NOTE you must unselect this thing, dont return early
        //

        DrawDibRealize(hdd, hdc, (wFlags & DDF_BACKGROUNDPAL) != 0);
    }

#ifndef WIN32
    //
    //  do a clipping check
    //
    if (pdd->ulFlags & DDF_CLIPCHECK)
    {
        RECT  rc;

        if (!(pdd->ulFlags & DDF_CLIPPED) &&
            (pdd->iDecompress == DECOMPRESS_SCREEN) && // (pdd->ulFlags & DDF_SCREENX) &&
            (wFlags & (DDF_PREROLL|DDF_DONTDRAW)))
        {
            DPF(("DDF_DONTDRAW while decompressing to screen, staying clipped"));
        }

        if ((pdd->ulFlags & DDF_MEMORYDC) ||
            GetClipBox(hdc, &rc) != SIMPLEREGION ||
            xDst < rc.left ||
            yDst < rc.top ||
            xDst + dxDst > rc.right ||
            yDst + dyDst > rc.bottom ||
            (wFlags & (DDF_PREROLL|DDF_DONTDRAW)) ||
            (gfDisplayHasBrokenRasters &&
		     (DCNotAligned(hdc, xDst) || gwScreenBitDepth == 24)))
	    // Note: if we're on a 24-bit display with broken rasters, we don't
	    // decompress to the screen even if the rectangle is aligned,
	    // because it's just too easy for somebody to try to write out
	    // a whole pixel in one gulp and hit the 64K boundary.
        {
            //
            //  we are clipped, check for a change.
            //
            if (!(pdd->ulFlags & DDF_CLIPPED))
            {
                pdd->ulFlags |= DDF_CLIPPED;
                DrawDibClipChange(pdd, wFlags);
            }
        }
        else
        {
            //
            // we are now unclipped, check for a change
            //
            if (pdd->ulFlags & DDF_CLIPPED)
            {
#ifdef DEBUG
		if (DCNotAligned(hdc, xDst))
                    DPF(("Warning draw is not aligned on 4 pixel boundry"));
#endif
                pdd->ulFlags &= ~DDF_CLIPPED;
                DrawDibClipChange(pdd, wFlags);
            }
        }
    }

#endif  //WIN32
    if (pdd->ulFlags & DDF_WANTKEY)
    {
        //
        // Adobe hack: If the DDF_UPDATE flag is on in our internal
        // flags, that means we've just been getting a bunch of frames
        // with the DONTDRAW flag set.  In that case, if this frame
        // immediately after those frames is marked as a key frame
        // we assume that it might not be a key frame and refrain from
        // switching immediately to decompressing to screen.
        //
        if (!(wFlags & DDF_NOTKEYFRAME) && !(pdd->ulFlags & DDF_UPDATE))
        {
            pdd->ulFlags &= ~DDF_WANTKEY;
            DrawDibClipChange(pdd, wFlags);
        }
    }

    //
    // if update is set re-draw what ever we drew last time
    //
    if (wFlags & DDF_UPDATE)
    {
        if (pdd->hic == (HIC)-1 || (pdd->ulFlags & DDF_DIRTY))
        {
            f = FALSE;
            goto exit;
        }

        if (pdd->hic)
        {
            if (pdd->ulFlags & DDF_UPDATE)
            {
                goto redraw;
            }

            lpbi = &pdd->biBuffer;
            lpBits = pdd->pbBuffer;

            //!!! set the source right.

            if ((pdd->ulFlags & DDF_XLATSOURCE))
            {
                dxSrc = MulDiv(dxSrc, abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
                dySrc = MulDiv(dySrc, abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);
                xSrc  = MulDiv(xSrc,  abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
                ySrc  = MulDiv(ySrc,  abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);
            }
        }

        if (pdd->ulFlags & DDF_STRETCH)
        {
            lpbi  = &pdd->biStretch;
            lpBits = pdd->pbStretch;
            dxSrc  = dxDst;
            dySrc  = dyDst;
            xSrc   = 0;
            ySrc   = 0;
        }

        if (pdd->ulFlags & DDF_DITHER)
        {
            lpBits = pdd->pbDither;
            xSrc = 0;
            ySrc = 0;
        }

        if (pdd->lpDIBSection != NULL)
	    goto bltDIB;

        if (pdd->hbmDraw && (pdd->ulFlags & DDF_BITMAP))
            goto bltit;


        if (lpBits == NULL)
        {
            f = FALSE;       // no buffer, can't update....
            goto exit;
        }

        goto drawit;
    }

    //
    // default for bits pointerdefault
    //
    if (lpBits == NULL)
        lpBits = (LPBYTE)lpbi+(int)lpbi->biSize + (int)lpbi->biClrUsed * sizeof(RGBQUAD);

    //
    // call any decompressor if needed
    //
    if (pdd->hic)
    {
        if (pdd->hic == (HIC)-1)
        {
            f = FALSE;
            goto exit;
        }

#ifndef WIN32 // exclude all code that references biscreen

        if (pdd->iDecompress == DECOMPRESS_SCREEN) // ulFlags & DDF_SCREENX
        {
            //
            // we are decompressing to the screen not the buffer, so
            // the buffer is dirty now
            //
            pdd->ulFlags |= DDF_DIRTY;

            dw = GetDCOrg(hdc);
            xDst += LOWORD(dw);
            yDst += HIWORD(dw);


            //
            // if DCIBeginAccess fails we are in the background, and should
            // not draw.
            //
            if (DCIBeginAccess(pdci, xDst, yDst, dxDst, dyDst) != 0)
            {
                DPF(("DCIBeginAccess failed!!!"));
                f = TRUE;       //!!! is this right?
                goto exit;
            }

            //convert to DIB corrds.
            yDst = (int)pdci->dwHeight - (yDst + dyDst);

            TIMESTART(timeDecompress);

            icFlags = 0;

            if (wFlags & DDF_HURRYUP)
                icFlags |= ICDECOMPRESS_HURRYUP;

            if (wFlags & DDF_NOTKEYFRAME)
                icFlags |= ICDECOMPRESS_NOTKEYFRAME;

            dw = ICDecompressEx(pdd->hic, icFlags,
                                lpbi, lpBits, xSrc, ySrc, dxSrc, dySrc,
                                &biScreen, lpScreen,
                                xDst, yDst, dxDst, dyDst);

            if (dw == ICERR_DONTDRAW)
                dw = ICERR_OK;

            f = (dw == ICERR_OK);

            TIMEEND(timeDecompress);

            DCIEndAccess(pdci);
            goto exit;
        }
        else
#endif // biscreen references
        {
            //
            //  if the offscreen buffer is dirty, only a key frame will
            //  clean our soul.
            //
            if (pdd->ulFlags & DDF_DIRTY)
            {
                if (wFlags & DDF_NOTKEYFRAME)
                {
                    //!!! playing files with no key frames we will get into
                    //a state where we will never draw a frame ever again.
                    //we need a punt count?

                    DPF(("punt frame"));

                    f = TRUE;
                    goto exit;
                }
                else // if (!(wFlags & DDF_HURRYUP))
                {
                    pdd->ulFlags &= ~DDF_DIRTY;
                }
            }

            TIMESTART(timeDecompress);

            icFlags = 0;

            if (wFlags & DDF_HURRYUP)
                icFlags |= ICDECOMPRESS_HURRYUP;

            if (wFlags & DDF_NOTKEYFRAME)
                icFlags |= ICDECOMPRESS_NOTKEYFRAME;

            if (pdd->iDecompress == DECOMPRESS_BITMAP) // ulFlags & DDF_BITMAPX
            {
                //!!! should we check FASTTEMPORALD?
                if (pdd->ulFlags & DDF_HUGEBITMAP) {
                    HugeToFlat(pdd->pbBitmap,pdd->biBitmap.biSizeImage,pdd->biBitmap.biYPelsPerMeter);
		}

                dw = ICDecompress(pdd->hic, icFlags, lpbi, lpBits, &pdd->biBitmap, pdd->pbBitmap);

                if (pdd->ulFlags & DDF_HUGEBITMAP) {
                    FlatToHuge(pdd->pbBitmap,pdd->biBitmap.biSizeImage,pdd->biBitmap.biYPelsPerMeter);
		}
            }
            else
            {
                dw = ICDecompress(pdd->hic, icFlags, lpbi, lpBits, &pdd->biBuffer, pdd->pbBuffer);
            }

            TIMEEND(timeDecompress);

            if (dw == ICERR_DONTDRAW) {
                // Decompressor doesn't want us to draw, for some reason....
                wFlags |= DDF_DONTDRAW;
            } else if (dw != 0) {
                f = FALSE;
                DPF(("Error %ld from decompressor!\n", dw));
                goto exit;
            }
        }

        //
        // if don't draw is set we just need to decompress
        //
        if (wFlags & (DDF_DONTDRAW|DDF_HURRYUP))
        {
            f = TRUE;
            pdd->ulFlags |= DDF_UPDATE|DDF_DONTDRAW;    // make sure update knows what to do
            goto exit;
        }

        //
        //  draw RLE delta's to the screen even when we are buffering.
        //
        if (!(pdd->ulFlags & (DDF_DONTDRAW|DDF_STRETCH|DDF_DITHER|DDF_NAKED)) &&
            lpbi->biCompression == BI_RLE8 &&
            lpbi->biSizeImage != pdd->biBuffer.biSizeImage)
        {
            pdd->ulFlags |= DDF_UPDATE;    // make sure update knows what to do
            pdd->biDraw.biCompression = BI_RLE8;
            goto drawit;
        }
redraw:
        pdd->ulFlags &= ~(DDF_UPDATE|DDF_DONTDRAW);

        if ((pdd->ulFlags & DDF_XLATSOURCE))
        {
            dxSrc = MulDiv(dxSrc, abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
            dySrc = MulDiv(dySrc, abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);
            xSrc  = MulDiv(xSrc,  abs((int)pdd->biBuffer.biWidth),  (int)pdd->lpbi->biWidth);
            ySrc  = MulDiv(ySrc,  abs((int)pdd->biBuffer.biHeight), (int)pdd->lpbi->biHeight);
        }

        lpbi = &pdd->biBuffer;
        lpBits = pdd->pbBuffer;

        pdd->biDraw.biCompression = pdd->biBuffer.biCompression;
    }
    else
    {
	if (pdd->lpDIBSection && ((pdd->ulFlags & (DDF_STRETCH|DDF_DITHER)) == 0)) {
	    // Include time taken here as 'stretching'.
	    // Really, though, we shouldn't be using DIB Sections in this case.
            TIMESTART(timeStretch);
	    if (lpbi->biCompression == BI_RGB) {
		lpbi->biSizeImage = DIBSIZEIMAGE(*lpbi);
	    }

	    hmemcpy(pdd->lpDIBSection, lpBits, lpbi->biSizeImage);
            TIMEEND(timeStretch);
	}

        //
        // when directly drawing RLE data we cant hurry
        //
        if (pdd->lpbi->biCompression == BI_RLE8)
            wFlags &= ~DDF_HURRYUP;

        //
        // if don't draw is set we just need to stretch/dither
        //
        if (wFlags & DDF_HURRYUP)
        {
            f = TRUE;
            pdd->ulFlags |= DDF_DIRTY;
            goto exit;
        }

        pdd->ulFlags &= ~DDF_DIRTY;
        pdd->biDraw.biCompression = lpbi->biCompression;
    }

    if (pdd->biDraw.biCompression == BI_RGB &&
        (pdd->ulFlags & (DDF_DITHER|DDF_STRETCH)))
    {
        if (pdd->ulFlags & DDF_STRETCH)
        {
            TIMESTART(timeStretch);

            StretchDIB(&pdd->biStretch, pdd->pbStretch,
                0, 0, dxDst, dyDst,
                lpbi,lpBits,
                xSrc,ySrc,dxSrc,dySrc);

            TIMEEND(timeStretch);

            lpbi  = &pdd->biStretch;
            lpBits = pdd->pbStretch;
            dxSrc  = dxDst;
            dySrc  = dyDst;
            xSrc   = 0;
            ySrc   = 0;
        }

        if (pdd->ulFlags & DDF_DITHER)
        {
            TIMESTART(timeDither);

            pdd->DitherProc(&pdd->biDraw, pdd->pbDither,0,0,dxSrc,dySrc,
                lpbi,lpBits,xSrc, ySrc, pdd->lpDitherTable);

            TIMEEND(timeDither);

            lpBits = pdd->pbDither;
            xSrc = 0;
            ySrc = 0;
        }

        if ((wFlags & DDF_DONTDRAW) && !pdd->hbmDraw)
        {
            f = TRUE;
            goto exit;
        }
    }
    else if (pdd->biDraw.biCompression == BI_RLE8)
    {
        /*
         * if drawing RLE deltas on NT, the biSizeImage field needs to
         * accurately reflect the amount of RLE data present in lpBits.
         */
        pdd->biDraw.biSizeImage = lpbi->biSizeImage;
    }

    if (pdd->lpDIBSection != NULL) {

	//ASSERT(pdd->hbmDraw != NULL);

        if (wFlags & DDF_DONTDRAW)
        {
            f = TRUE;
            goto exit;
        }

bltDIB:
        TIMESTART(timeBlt);

	// Put things back in right-side-up coordinates
	ySrc = (int)pdd->biDraw.biHeight - (ySrc + dySrc);
//      ySrc = 0; // Was like this for Chicago M6!

        f = StretchBlt(hdc,xDst,yDst,dxDst,dyDst,pdd->hdcDraw,
            xSrc,ySrc,dxSrc,dySrc,SRCCOPY) != 0;

        TIMEEND(timeBlt);


    } else if (pdd->hbmDraw)
    {
        //
        //  when MCIAVI is playing we need realize our palette for each
        //  draw operation because another app may have drawn a translated
        //  bitmap thus screwing up the GDI *global* device translate table.
        //  RonG I hate you some times
        //
        if (pdd->hpal && (wFlags & DDF_SAME_HDC)) {
            if (GetProfileInt(szDrawDib, "switchpalette", TRUE))
                RealizePalette(hdc);
        }

        if (pdd->iDecompress != DECOMPRESS_BITMAP) // !(pdd->ulFlags & DDF_BITMAPX)
        {
            TIMESTART(timeSetDIBits);
#if USE_SETDI
            pdd->sd.hdc = hdc;      //!!!ack!
            SetBitmap(&pdd->sd,xSrc,0,dxSrc,dySrc,lpBits,xSrc,ySrc,dxSrc,dySrc);
            pdd->sd.hdc = NULL;     //!!!ack!
            ySrc = 0;
#else
            SetDIBits(hdc, pdd->hbmDraw, 0, dySrc,
                lpBits, (LPBITMAPINFO)&pdd->biDraw, pdd->uiPalUse);
#endif
            TIMEEND(timeSetDIBits);
        }

        if (wFlags & DDF_DONTDRAW)
        {
            f = TRUE;
            goto exit;
        }
bltit:
        TIMESTART(timeBlt);

	// Put things back in right-side-up coordinates
	ySrc = (int)pdd->biDraw.biHeight - (ySrc + dySrc);
//      ySrc = 0;

        f = StretchBlt(hdc,xDst,yDst,dxDst,dyDst,pdd->hdcDraw,
            xSrc,ySrc,dxSrc,dySrc,SRCCOPY) != 0;

        TIMEEND(timeBlt);
    }
    else
drawit:
    {

        // Sometimes when you read an RLE file, you get RGB data back (ie. the
        // first frame).  Passing RGB data to a display driver who thinks it
        // is getting RLE data will blow it up.  If the RLE data is the exact
        // size of RGB data, we decide that's just too much of a coincidence.
        BOOL fNotReallyRLE = (pdd->biDraw.biCompression == BI_RLE8 &&
            lpbi->biSizeImage == DIBWIDTHBYTES(*lpbi) * (DWORD)lpbi->biHeight);

// !!! We've been told not to draw, but I'm going to draw anyway, to fix bug
// WIN95C 14453.  MCIAVI draws from a keyframe forward, saying DONTDRAW on
// every frame but the last.  Works fine in theory, but in theory, communism
// works! Because of other bugs declared WONTFIX, Drawdib doesn't buffer the
// images as it goes along, so when it comes time to draw the result at the
// end, it goes "ACK! I have no idea what I was told to draw!".  So the only
// safe way to fix it is to draw even though we were told not to.  I feel safe
// doing this because this is the way VFW1.1 worked, and nobody has complained
// in over a year.
#if 0
        if (wFlags & DDF_DONTDRAW)
        {
            f = TRUE;
            goto exit;
        }
#endif

        if (fNotReallyRLE)
            pdd->biDraw.biCompression = BI_RGB;

        if (pdd->biDraw.biCompression == BI_RLE8)
        {
            /*
             * if drawing RLE deltas on NT, the biSizeImage field needs to
             * accurately reflect the amount of RLE data present in lpBits.
             */
            pdd->biDraw.biSizeImage = lpbi->biSizeImage;
        }

        TIMESTART(timeBlt);

        /*
         * NT stretchdibits does not work with RLE deltas, even 1:1
         *
         * also note use of pdd->uiPalUse: this is DIB_PAL_COLORS by
         * default, but may be set to DIB_PAL_INDICES if we detect that
         * the system palette is identical to ours, and thus
         * we can safely take this huge performance benefit (on NT,
         * DIB_PAL_INDICES nearly halves the cost of this call)
         */
        if ((dxDst == dxSrc) && (dyDst == dySrc))
        {
            f = SetDIBitsToDevice(hdc, xDst, yDst, dxDst, dyDst,
                    xSrc, ySrc, 0, (UINT)pdd->biDraw.biHeight, lpBits,
                    (LPBITMAPINFO)&pdd->biDraw, pdd->uiPalUse) != 0;
        }
        else
        {
            f = StretchDIBits(hdc,xDst,yDst,dxDst,dyDst,
                xSrc,ySrc,dxSrc,dySrc,
                lpBits, (LPBITMAPINFO)&pdd->biDraw,
                pdd->uiPalUse, SRCCOPY) != 0;
        }

        TIMEEND(timeBlt);
        if (fNotReallyRLE)
            pdd->biDraw.biCompression = BI_RLE8;
    }

exit:
    if (!(wFlags & DDF_SAME_HDC) && pdd->hpal)
        SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), TRUE);

    TIMEEND(timeDraw);
    return f;
}

#if 0
/**************************************************************************
* @doc INTERNAL
*
* @api BOOL| InitDrawToScreen | init drawing to the screen via DCI
*
**************************************************************************/
static BOOL InitDrawToScreen(PDD pdd)
{
    BOOL f;

    if (!(pdd->ulFlags & DDF_CANDRAWX))
        return FALSE;

    f = !(pdd->ulFlags & DDF_CLIPPED);

    if (f && !(pdd->ulFlags & DDF_DRAWX))
    {
        DPF(("drawing to SCREEN now"));

        pdd->ulFlags |= DDF_DRAWX;
    }
    else if (!f && (pdd->ulFlags & DDF_DRAWX))
    {
        DPF(("not drawing to SCREEN anymore"));

        pdd->ulFlags &= ~DDF_DRAWX;
    }
}
#endif

/**************************************************************************
* @doc INTERNAL
*
* @api BOOL| InitDecompress | init every thing for decompressing
*   to the screen or a bitmap or a memory buffer.
*
* we can decompress to the screen if the following is true:
*
*   palette must be 1:1
*   must be unclipped
*
**************************************************************************/
static BOOL InitDecompress(PDD pdd)
{
    BOOL f;
    BOOL fBitmap;
    BOOL fScreen;

    //
    // nothing to init
    //
    if (!(pdd->ulFlags & (DDF_CANSCREENX|DDF_CANBITMAPX)))
        return TRUE;

    //
    // make sure we rebegin when the palette changes
    //
    if (pdd->ulFlags & (DDF_NEWPALETTE|DDF_WANTKEY))
        pdd->iDecompress = 0;

    //
    // we need to decompress to either a memory bitmap or buffer.
    //
    fBitmap = (pdd->ulFlags & DDF_CANBITMAPX) &&
              (pdd->ulFlags & (DDF_IDENTITYPAL|DDF_CANSETPAL));

    fScreen = (pdd->ulFlags & DDF_CANSCREENX) &&
             !(pdd->ulFlags & DDF_CLIPPED)    &&
             !(pdd->ulFlags & DDF_MEMORYDC)   &&
              (pdd->ulFlags & (DDF_IDENTITYPAL|DDF_CANSETPAL));

    //
    // should we be decompressing to the screen?
    //
    if (fScreen && pdd->iDecompress != DECOMPRESS_SCREEN)
    {
        if (pdd->ulFlags & DDF_IDENTITYPAL)
        {
            if (pdd->hpalDraw)
                ICDecompressSetPalette(pdd->hic, &pdd->biBuffer);
            else
                ICDecompressSetPalette(pdd->hic, NULL);
        }
        else
        {
            if (FixUpCodecPalette(pdd->hic, pdd->lpbi))
            {
                DPF(("Codec notified of palette change...."));
            }
            else
            {
                DPF(("Codec failed palette change...."));
                pdd->iDecompress = 0;
                goto ack;
            }
        }

        //
        // now init the compressor for screen decompress.
        //
        f = ICDecompressExBegin(pdd->hic, 0,
            pdd->lpbi, NULL, 0, 0, pdd->dxSrc, pdd->dySrc,
            &biScreen, lpScreen, 0, 0, pdd->dxDst, pdd->dyDst) == ICERR_OK;

        if (f)
        {
            pdd->ulFlags |= DDF_DIRTY;          // buffer is dirty now?
            RPF(("Decompressing to screen now"));
            pdd->iDecompress = DECOMPRESS_SCREEN;
            return TRUE;
        }
        else
        {
ack:        DPF(("Compressor failed decompress to SCREEN, so not decompressing to screen!!!!"));
            pdd->iDecompress = 0;
            pdd->ulFlags &= ~DDF_CANSCREENX;
        }
    }
    else if (fScreen)
    {
        //
        //  already decompressing to screen.
        //
        return TRUE;
    }

    //
    // should we decompress to a bitmap?
    //
    if (fBitmap && pdd->iDecompress != DECOMPRESS_BITMAP)
    {
        if (pdd->ulFlags & DDF_IDENTITYPAL)
        {
            if (pdd->hpalDraw)
                ICDecompressSetPalette(pdd->hic, &pdd->biBuffer);
            else
                ICDecompressSetPalette(pdd->hic, NULL);
        }
        else
        {
            if (FixUpCodecPalette(pdd->hic, pdd->lpbi))
            {
                DPF(("Codec notified of palette change...."));
            }
            else
            {
                DPF(("Codec failed palette change...."));
                pdd->iDecompress = 0;
                goto ackack;
            }
        }

        f = ICDecompressBegin(pdd->hic, pdd->lpbi, &pdd->biBitmap) == ICERR_OK;

        if (f)
        {
            pdd->ulFlags |= DDF_DIRTY;          // buffer is dirty now?
            DPF(("decompressing to BITMAP now"));
            pdd->iDecompress = DECOMPRESS_BITMAP;

            return TRUE;
        }
        else
        {
ackack:     DPF(("Unable to init decompress to bitmap"));
            pdd->iDecompress = 0;
        }
    }
    else if (fBitmap)
    {
        //
        //  already decompressing to bitmap
        //
        return TRUE;
    }

    //
    // should we decompress to a buffer?
    //
    if (pdd->iDecompress != DECOMPRESS_BUFFER)
    {
        DPF(("decompressing to DIB now"));

        pdd->ulFlags |= DDF_DIRTY;          // buffer is dirty now?
        pdd->iDecompress = DECOMPRESS_BUFFER;

        if (pdd->hpalDraw)
            ICDecompressSetPalette(pdd->hic, &pdd->biBuffer);
        else
            ICDecompressSetPalette(pdd->hic, NULL);

        f = ICDecompressBegin(pdd->hic, pdd->lpbi, &pdd->biBuffer) == ICERR_OK;

        if (!f)
        {
            DPF(("Unable to re-begin compressor"));
        }
    }

    return TRUE;    // nothing to change
}

/**************************************************************************
* @doc INTERNAL
*
* @api void | DrawDibClipChange | called when the clipping has changed
*   from clipped to totaly un-clipped or whatever.
*
**************************************************************************/
void DrawDibClipChange(PDD pdd, UINT wFlags)
{
    if (!(pdd->ulFlags & DDF_NEWPALETTE))
    {
        if (pdd->ulFlags & DDF_CLIPPED)
            DPF(("now clipped"));
        else
            DPF(("now un-clipped"));
    }

////InitDrawToScreen(pdd);

    //
    // dont change Decompressors on a non key frame, unless we have
    // to (ie going clipped and decompressing to screen)
    //
    if (pdd->ulFlags & DDF_NEWPALETTE)
    {
        if (wFlags & DDF_NOTKEYFRAME)
        {
            if (pdd->iDecompress == DECOMPRESS_BUFFER) // !(pdd->ulFlags & DDF_SCREENX))
            {
                DPF(("waiting for a key frame to change (palette) decompressor"));
                pdd->ulFlags |= DDF_WANTKEY;
                return;
            }
        }
    }
    else
    {
        if (wFlags & DDF_NOTKEYFRAME)
        {
            if (pdd->iDecompress != DECOMPRESS_SCREEN) // !(pdd->ulFlags & DDF_SCREENX))
            {
                DPF(("waiting for a key frame to change (clipped) decompressor"));
                pdd->ulFlags |= DDF_WANTKEY;
                return;
            }
        }
    }

    InitDecompress(pdd);
    pdd->ulFlags &= ~DDF_WANTKEY;
}

/**************************************************************************
* @doc INTERNAL
*
* @api void | DrawDibPalChange | called when the physical palette mapping
*   has changed.
*
**************************************************************************/
void DrawDibPalChange(PDD pdd, HDC hdc, HPALETTE hpal)
{
#ifndef WIN32
#ifdef DEBUG
//      extern BOOL FAR PASCAL IsDCCurrentPalette(HDC hdc);

//      BOOL fForeground = IsDCCurrentPalette(hdc);

//      if (fForeground)
//          DPF(("Palette mapping has changed (foreground)..."));
//      else
//          DPF(("Palette mapping has changed (background)..."));
#endif
#endif

    //
    // if we are on a palette device we need to do some special stuff.
    //
    if (gwScreenBitDepth == 8 && (gwRasterCaps & RC_PALETTE))
    {
        //
        // get the logical->physical mapping
        //
        if (GetPhysDibPaletteMap(hdc, &pdd->biDraw, pdd->uiPalUse, pdd->ab))
            pdd->ulFlags |= DDF_IDENTITYPAL;
        else
            pdd->ulFlags &= ~DDF_IDENTITYPAL;

        if (pdd->ulFlags & DDF_IDENTITYPAL)
            DPF(("Palette mapping is 1:1"));
        else
            DPF(("Palette mapping is not 1:1"));

#ifdef WIN32    //!!!NT Only
        if (pdd->ulFlags & DDF_IDENTITYPAL)
            pdd->uiPalUse = DIB_PAL_INDICES;
        else
            pdd->uiPalUse = DIB_PAL_COLORS;
#endif
    }
    else
    {
        //
        // we are not on a palette device, some code checks DDF_IDENTITYPAL
        // anyway so set it.
        //
        pdd->ulFlags |= DDF_IDENTITYPAL;
    }

    if (pdd->hbmDraw && (pdd->ulFlags & DDF_BITMAP))
    {
        //!!! we should pass pdd->ab to this function!
        //!!! and use a naked translate.
        SetBitmapColorChange(&pdd->sd, hdc, hpal);
    }

    DrawDibClipChange(pdd, DDF_NOTKEYFRAME);
}

/**************************************************************************
* @doc INTERNAL
*
* @api HPALETTE | CreateBIPalette | Create palette from bitmap.
*
* @parm LPBITMAPINFOHEADER | lpbi | Pointer to bitmap.
*
* @rdesc Returns handle to the palette, NULL if error.
*
**************************************************************************/
static HPALETTE CreateBIPalette(HPALETTE hpal, LPBITMAPINFOHEADER lpbi)
{
    LPRGBQUAD prgb;
    int i;

    // This structure is the same as LOGPALETTE EXCEPT for the array of
    // palette entries which here is 256 long.  The "template" in the
    // SDK header files only has an array of size one, hence the "duplication".
    struct {
        WORD         palVersion;                /* tomor - don't mess with word */
        WORD         palNumEntries;
        PALETTEENTRY palPalEntry[256];
    }   pal;

    pal.palVersion = 0x300;
    pal.palNumEntries = (int)lpbi->biClrUsed;

    if (pal.palNumEntries == 0 && lpbi->biBitCount <= 8)
        pal.palNumEntries = (1 << (int)lpbi->biBitCount);

    if (pal.palNumEntries == 0)
        return NULL;

    prgb = (LPRGBQUAD)(lpbi+1);

    for (i=0; i<(int)pal.palNumEntries; i++)
    {
        pal.palPalEntry[i].peRed   = prgb[i].rgbRed;
        pal.palPalEntry[i].peGreen = prgb[i].rgbGreen;
        pal.palPalEntry[i].peBlue  = prgb[i].rgbBlue;
        pal.palPalEntry[i].peFlags = 0;
    }

    if (hpal)
    {
        ResizePalette(hpal, pal.palNumEntries);
        SetPaletteEntries(hpal, 0, pal.palNumEntries, pal.palPalEntry);
    }
    else
    {
        hpal = CreatePalette((LPLOGPALETTE)&pal);
    }

    return hpal;
}

/**************************************************************************
* @doc INTERNAL
*
* @api BOOL | SetPalFlags | Modifies the palette flags.
*
* @parm HPALETTE | hpal | Handle to the palette.
*
* @parm int | iIndex | Starting palette index.
*
* @parm int | cntEntries | Number of entries to set flags on.
*
* @parm UINT | wFlags | Palette flags.
*
* @rdesc Returns TRUE if successful, FALSE otherwise.
*
**************************************************************************/
static BOOL SetPalFlags(HPALETTE hpal, int iIndex, int cntEntries, UINT wFlags)
{
    int     i;
    PALETTEENTRY ape[256];

    if (hpal == NULL)
        return FALSE;

    if (cntEntries < 0)
        GetObject(hpal,sizeof(int),(LPSTR)&cntEntries);

    GetPaletteEntries(hpal, iIndex, cntEntries, ape);

    for (i=0; i<cntEntries; i++)
        ape[i].peFlags = (BYTE)wFlags;

    return SetPaletteEntries(hpal, iIndex, cntEntries, ape);
}


/**************************************************************************
* @doc INTERNAL
*
* @api BOOL | IsIdentityPalette | Check if palette is an identity palette.
*
* @parm HPALETTE | hpal | Handle to the palette.
*
* @rdesc Returns TRUE if the palette is an identity palette, FALSE otherwise.
*
**************************************************************************/

#define CODE _based(_segname("_CODE"))

//
// These are the standard VGA colors, we will be stuck with until the
// end of time!
//
static PALETTEENTRY CODE apeCosmic[16] = {
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x80, 0x00, 0x00, 0x00,     // 0001  dark red
    0x00, 0x80, 0x00, 0x00,     // 0010  dark green
    0x80, 0x80, 0x00, 0x00,     // 0011  mustard
    0x00, 0x00, 0x80, 0x00,     // 0100  dark blue
    0x80, 0x00, 0x80, 0x00,     // 0101  purple
    0x00, 0x80, 0x80, 0x00,     // 0110  dark turquoise
    0xC0, 0xC0, 0xC0, 0x00,     // 1000  gray
    0x80, 0x80, 0x80, 0x00,     // 0111  dark gray
    0xFF, 0x00, 0x00, 0x00,     // 1001  red
    0x00, 0xFF, 0x00, 0x00,     // 1010  green
    0xFF, 0xFF, 0x00, 0x00,     // 1011  yellow
    0x00, 0x00, 0xFF, 0x00,     // 1100  blue
    0xFF, 0x00, 0xFF, 0x00,     // 1101  pink (magenta)
    0x00, 0xFF, 0xFF, 0x00,     // 1110  cyan
    0xFF, 0xFF, 0xFF, 0x00      // 1111  white
    };

static PALETTEENTRY CODE apeFake[16] = {
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0xBF, 0x00, 0x00, 0x00,     // 0001  dark red
    0x00, 0xBF, 0x00, 0x00,     // 0010  dark green
    0xBF, 0xBF, 0x00, 0x00,     // 0011  mustard
    0x00, 0x00, 0xBF, 0x00,     // 0100  dark blue
    0xBF, 0x00, 0xBF, 0x00,     // 0101  purple
    0x00, 0xBF, 0xBF, 0x00,     // 0110  dark turquoise
    0xC0, 0xC0, 0xC0, 0x00,     // 1000  gray
    0x80, 0x80, 0x80, 0x00,     // 0111  dark gray
    0xFF, 0x00, 0x00, 0x00,     // 1001  red
    0x00, 0xFF, 0x00, 0x00,     // 1010  green
    0xFF, 0xFF, 0x00, 0x00,     // 1011  yellow
    0x00, 0x00, 0xFF, 0x00,     // 1100  blue
    0xFF, 0x00, 0xFF, 0x00,     // 1101  pink (magenta)
    0x00, 0xFF, 0xFF, 0x00,     // 1110  cyan
    0xFF, 0xFF, 0xFF, 0x00,     // 1111  white
    };

static PALETTEENTRY CODE apeBlackWhite[16] = {
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0x00, 0x00, 0x00, 0x00,     // 0000  black
    0xFF, 0xFF, 0xFF, 0x00      // 1111  white
    };

static BOOL NEAR IsIdentityPalette(HPALETTE hpal)
{
    int i,n=0;    // n is initialised as GetObject returns a 2 byte value
    HDC hdc;

    PALETTEENTRY ape[256];
    PALETTEENTRY apeSystem[16];

    if (hpal == NULL || !(gwRasterCaps & RC_PALETTE) || gwScreenBitDepth != 8)
        return FALSE;

    // Some wierd display cards actually have different numbers of system
    // colours! We definitely don't want to think we can do identity palettes.
    hdc = GetDC(NULL);
    n = GetDeviceCaps(hdc, NUMRESERVED);
    ReleaseDC(NULL, hdc);

    if (n != 20)
        return FALSE;

    GetObject(hpal, sizeof(n), (LPVOID)&n);

    if (n != 256)
        return FALSE;

    GetPaletteEntries(hpal, 0,   8, &ape[0]);
    GetPaletteEntries(hpal, 248, 8, &ape[8]);

    for (i=0; i<16; i++)
        ape[i].peFlags = 0;

    if (!_fmemcmp(ape, apeCosmic, sizeof(apeCosmic)))
        goto DoneChecking;

    if (!_fmemcmp(ape, apeFake, sizeof(apeFake)))
        goto DoneChecking;

    if (!_fmemcmp(ape, apeBlackWhite, sizeof(apeBlackWhite)))
        goto DoneChecking;

    hdc = GetDC(NULL);
    GetSystemPaletteEntries(hdc, 0,   8, &apeSystem[0]);
    GetSystemPaletteEntries(hdc, 248, 8, &apeSystem[8]);
    ReleaseDC(NULL, hdc);

    for (i=0; i<16; i++)
        apeSystem[i].peFlags = 0;

    if (!_fmemcmp(ape, apeSystem, sizeof(apeSystem)))
        goto DoneChecking;

    return FALSE;

DoneChecking:
    //
    // if we have an identity palette then, patch the colors to match
    // the driver ones exactly.
    //
    GetPaletteEntries(hpal, 0, 256, ape);

    hdc = GetDC(NULL);
    GetSystemPaletteEntries(hdc, 0,   10, &ape[0]);
    GetSystemPaletteEntries(hdc, 246, 10, &ape[246]);
    ReleaseDC(NULL, hdc);

    for (i=0; i<10; i++)
        ape[i].peFlags = 0;

    for (i=10; i<246; i++)
        ape[i].peFlags = PC_NOCOLLAPSE;

    for (i=246; i<256; i++)
        ape[i].peFlags = 0;

    SetPaletteEntries(hpal, 0, 256, ape);
    UnrealizeObject(hpal);      //??? needed

    return TRUE;
}

#define COLORMASK 0xF8
STATICFN BOOL NEAR AreColorsAllGDIColors(LPBITMAPINFOHEADER lpbi)
{
    int	    cColors;
    LPRGBQUAD lprgb = (LPRGBQUAD) ((LPBYTE) lpbi + lpbi->biSize);
    int	    i;

    for (cColors = (int) lpbi->biClrUsed; cColors > 0; cColors--, lprgb++) {
	for (i = 0; i < 16; i++) {
	    if (((lprgb->rgbRed & COLORMASK) == (apeCosmic[i].peRed & COLORMASK)) &&
		((lprgb->rgbGreen & COLORMASK) == (apeCosmic[i].peGreen & COLORMASK)) &&
		((lprgb->rgbBlue & COLORMASK) == (apeCosmic[i].peBlue & COLORMASK)))
		goto Onward;
	
	    if (((lprgb->rgbRed & COLORMASK) == (apeFake[i].peRed & COLORMASK)) &&
		((lprgb->rgbGreen & COLORMASK) == (apeFake[i].peGreen & COLORMASK)) &&
		((lprgb->rgbBlue & COLORMASK) == (apeFake[i].peBlue & COLORMASK)))
		goto Onward;
	}

	return FALSE;
Onward:
	;	// There's got to be a nicer way to arrange this code!
    }

    return TRUE; // !!!!!
}

/**************************************************************************

let codec adapt to the system palette.

**************************************************************************/

static BOOL FixUpCodecPalette(HIC hic, LPBITMAPINFOHEADER lpbi)
{
    struct {
        BITMAPINFOHEADER bi;
        RGBQUAD          argbq[256];
    } s;
    int                 i;
    HDC                 hdc;

    s.bi.biSize           = sizeof(s.bi);
    s.bi.biWidth          = lpbi->biWidth;
    s.bi.biHeight         = lpbi->biHeight;
    s.bi.biPlanes         = 1;
    s.bi.biBitCount       = 8;
    s.bi.biCompression    = 0;
    s.bi.biSizeImage      = 0;
    s.bi.biXPelsPerMeter  = 0;
    s.bi.biYPelsPerMeter  = 0;
    s.bi.biClrUsed        = 256;
    s.bi.biClrImportant   = 0;

    hdc = GetDC(NULL);
    GetSystemPaletteEntries(hdc, 0, 256, (LPPALETTEENTRY) &s.argbq);
    ReleaseDC(NULL, hdc);

    for (i = 0; i < 256; i++)
        ((DWORD FAR*)s.argbq)[i] = i < 8 || i >= 248 ? 0 :
            RGB(s.argbq[i].rgbRed,s.argbq[i].rgbGreen,s.argbq[i].rgbBlue);

    return ICDecompressSetPalette(hic, &s.bi) == ICERR_OK;
}

/**************************************************************************

let codec adapt to a palette passed by the app.

**************************************************************************/

static BOOL NEAR SendSetPalette(PDD pdd)
{
    int  i;
    int  iPalColors = 0;
    BOOL f;

    if (pdd->hic == NULL)               // nobody to send too
        return FALSE;

    if (pdd->biBuffer.biBitCount != 8)  // not decompressing to 8bit
        return FALSE;

    if (!(gwRasterCaps & RC_PALETTE))   // not a palette device who cares.
        return FALSE;

    if (pdd->hpalDraw)
    {
        GetObject(pdd->hpalDraw, sizeof(iPalColors), (void FAR *)&iPalColors);

        if (iPalColors == 0)
            return FALSE;

        if (iPalColors > 256)
            iPalColors = 256;

        pdd->biBuffer.biClrUsed = iPalColors;
        GetPaletteEntries(pdd->hpalDraw, 0, iPalColors, (PALETTEENTRY FAR *)pdd->argbq);

        for (i = 0; i < iPalColors; i++)
            ((DWORD*)pdd->argbq)[i] = RGB(pdd->argbq[i].rgbRed,pdd->argbq[i].rgbGreen,pdd->argbq[i].rgbBlue);

        f = ICDecompressSetPalette(pdd->hic, &pdd->biBuffer) == ICERR_OK;
        ICDecompressGetPalette(pdd->hic, pdd->lpbi, &pdd->biBuffer);
    }
    else
    {
        pdd->biBuffer.biClrUsed = pdd->ClrUsed;
        f = ICDecompressSetPalette(pdd->hic, NULL) == ICERR_OK;
        ICDecompressGetPalette(pdd->hic, pdd->lpbi, &pdd->biBuffer);
    }

    return f;
}

#ifdef DEBUG_RETAIL

#define _WINDLL
#include <stdarg.h>
#include <stdio.h>

void FAR CDECL ddprintf(LPSTR szFormat, ...)
{
    char ach[128];
    va_list va;
    UINT n;
    static int fDebug = -1;

    if (fDebug == -1)
        fDebug = GetProfileIntA("Debug", MODNAME, FALSE);

    if (!fDebug)
        return;

    va_start(va, szFormat);
#ifdef WIN32
    n = sprintf(ach, MODNAME ": (tid %x) ", GetCurrentThreadId());
    n += vsprintf(ach+n, szFormat, va);
#else
    lstrcpy(ach, MODNAME ": ");
    n = lstrlen(ach);
    n += wvsprintf(ach+n, szFormat, va);
#endif
    va_end(va);
    ach[n++] = '\r';
    ach[n++] = '\n';
    ach[n] = 0;
    OutputDebugStringA(ach);
}
#endif
