/*----------------------------------------------------------------------------*\
 *  GDIHELP.C  - GDI TOOLHELP
 *
 *  a bunch of GDI utility functions that are usefull for walking
 *  all GDI objects and dinking with them.
 *
 *  ToddLa
 *
\*----------------------------------------------------------------------------*/

#ifdef IS_16
#define DIRECT_DRAW
#endif

#ifdef DIRECT_DRAW
#include "ddraw16.h"
#else
#include <windows.h>
#include "gdihelp.h"
#include "dibeng.inc"
#ifdef DEBUG
#include <toolhelp.h>
#endif
#endif

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
#undef DPF
#ifdef DEBUG
static void CDECL DPF(char *sz, ...)
{
    char ach[128];
    lstrcpy(ach,"QuickRes: ");
    wvsprintf(ach+10, sz, (LPVOID)(&sz+1));
#ifdef DIRECT_DRAW
    dprintf(2, ach);
#else
    lstrcat(ach, "\r\n");
    OutputDebugString(ach);
#endif
}
static void NEAR PASCAL __Assert(char *exp, char *file, int line)
{
    DPF("Assert(%s) failed at %s line %d.", (LPSTR)exp, (LPSTR)file, line);
    DebugBreak();
}
#define Assert(exp)  ( (exp) ? (void)0 : __Assert(#exp,__FILE__,__LINE__) )

#else
#define Assert(f)
#define DPF ; / ## /
#endif

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

extern     HMODULE WINAPI GetExePtr(HANDLE h);
extern     HANDLE  WINAPI SetObjectOwner(HGDIOBJ, HANDLE);
extern     BOOL    WINAPI MakeObjectPrivate(HANDLE hObj, BOOL bPrivate);
extern     int     WINAPI GDISelectPalette(HDC, HPALETTE, BOOL);

#define PresDC(hdc) GetSystemPaletteUse(hdc)

void SaveDCFix(HGDIOBJ h, LPARAM lParam);
void SaveDCReSelectObjects(HGDIOBJ h, LPARAM lParam);
void ReRealizeObject(HGDIOBJ h, LPARAM lParam);
void ReSelectObjects(HGDIOBJ h, LPARAM lParam);

typedef struct {
    BITMAPINFOHEADER bi;
    DWORD            ct[16];
}   DIB4;

typedef struct {
    BITMAPINFOHEADER bi;
    DWORD            ct[256];
}   DIB8;

typedef struct {
    HGDIOBJ h;
    UINT    type;
}   GDIOBJECT, NEAR *GDIOBJECTLIST;

GDIOBJECTLIST GdiObjectList;

WORD GetW(HGDIOBJ h, UINT off);
WORD SetW(HGDIOBJ h, UINT off, WORD w);

/*----------------------------------------------------------------------------*\
 * StockBitmap
 * return the stock 1x1x1 bitmap, windows should have a GetStockObject for
 * this but it does not.
\*----------------------------------------------------------------------------*/

HBITMAP StockBitmap()
{
    HBITMAP hbm = CreateBitmap(0,0,1,1,NULL);
    SetObjectOwner(hbm, 0);
    return hbm;
}

/*----------------------------------------------------------------------------*\
 * SafeSelectObject
 *
 * call SelectObject, but make sure USER does not RIP because we are using
 * a DC without calling GetDC.
\*----------------------------------------------------------------------------*/

HGDIOBJ SafeSelectObject(HDC hdc, HGDIOBJ h)
{
    UINT hf;

    // this prevents USER from RIPing because we are using
    // DCs in the cache without calling GetDC()
    hf = SetHookFlags(hdc, DCHF_VALIDATEVISRGN);
    h = SelectObject(hdc, h);
    SetHookFlags(hdc, hf);

    return h;
}

/*----------------------------------------------------------------------------*\
 * IsMemoryDC
 *
 * return TRUE if the passed DC is a memory DC.  we do this seeing if we
 * can select the stock bitmap into it.
\*----------------------------------------------------------------------------*/

BOOL IsMemoryDC(HDC hdc)
{
    HBITMAP hbm;

    if (hbm = (HBITMAP)SafeSelectObject(hdc, StockBitmap()))
        SafeSelectObject(hdc, hbm);

    return hbm != NULL;
}

/*----------------------------------------------------------------------------*\
 * IsScreenDC
 *
 * return TRUE for a non-memory DC
\*----------------------------------------------------------------------------*/

BOOL IsScreenDC(HDC hdc)
{
    return (!IsMemoryDC(hdc) && GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY);
}

/*----------------------------------------------------------------------------*\
 * GetObjectOwner
 * return the owner of a GDI object
\*----------------------------------------------------------------------------*/

HANDLE GetObjectOwner(HGDIOBJ h)
{
    HANDLE owner;
    owner = SetObjectOwner(h, 0);
    SetObjectOwner(h, owner);
    return owner;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

BOOL IsObjectPrivate(HGDIOBJ h)
{
    BOOL IsPrivate;
    IsPrivate = MakeObjectPrivate(h, 0);
    MakeObjectPrivate(h, IsPrivate);
    return IsPrivate;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

BOOL IsObjectStock(HGDIOBJ h)
{
    int n;

    for (n=0; n<=17; n++)
        if (GetStockObject(n) == h)
            return TRUE;

    if (StockBitmap() == h)
        return TRUE;

    return FALSE;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
#pragma optimize("", off)
UINT GetGdiDS()
{
    UINT result;

    IsGDIObject((HGDIOBJ)1);
    _asm mov ax,es
    _asm mov result,ax
#ifdef DEBUG
    {
    SYSHEAPINFO shi = {sizeof(shi)};
    SystemHeapInfo(&shi);
    Assert((UINT)shi.hGDISegment == result);
    }
#endif
    return result;
}
#pragma optimize("", on)

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

GDIOBJECTLIST BuildGdiObjectList(void)
{
    int i;
    int count;
    GDIOBJECTLIST list;
    UINT type;
    UINT hgdi = GetGdiDS();

#ifdef DEBUG
    UINT ObjHist[OBJ_MAX+1];
    for (i=0; i<=OBJ_MAX; i++) ObjHist[i] = 0;
#endif

    DPF("BEGIN BuildGdiObjectList...");

    i=0;
    count=0;
    list=NULL;
again:
    {
        WORD FAR *pw;
        UINT cnt;
        HANDLE h;

        // get pointer to local heap info (stored at offset 6 in DGROUP)
        pw  = MAKELP(hgdi, 6);
        pw  = MAKELP(hgdi, *pw);

        // get pointer to first handle table (stored at offset 0x14 in HeapInfo)
        pw  = MAKELP(hgdi, pw[0x14/2]);

        //
        // a handle table starts with a WORD count of entries, followed
        // by the entries (each is a DWORD) last WORD is a pointer to
        // the next handle table or 0.
        //
        // each handle entry is a WORD ptr, followed by flags (WORD)
        // the HIBYTE of the flags is realy the lock count.
        // if the flags are 0xFFFF the handle is free.
        // for the GDI heap if 0x10 is set in the flags the
        // handle is a GDI object handle.
        //
        while (OFFSETOF(pw) != 0)
        {
            cnt = *pw++;        // get handle table count

            while (cnt-- > 0)
            {
                h = (HANDLE)OFFSETOF(pw);

                // is the handle free? yes skip
                if (pw[1] != 0xFFFF)
                {
                    // is the handle a GDI object?
                    if (pw[1] & 0x0010)
                    {
                        type = (UINT)IsGDIObject(h);

                        if (type)
                        {
                            if (list)
                            {
                                Assert(i >= 0 && i < count);
                                list[i].h    = (HGDIOBJ)h;
                                list[i].type = type;
                                i++;
                            }
                            else
                            {
                                count++;
#ifdef DEBUG
                                Assert(type > 0 && type <= OBJ_MAX);
                                ObjHist[type]++;
#endif
                            }
                        }
                    }
                    // not a gdi object, might be a SaveDC
                    else
                    {
                        if ((UINT)IsGDIObject(h) == OBJ_DC)
                        {
                            if (list)
                            {
                                Assert(i >= 0 && i < count);
                                list[i].h    = (HGDIOBJ)h;
                                list[i].type = OBJ_SAVEDC;
                                i++;
                            }
                            else
                            {
                                count++;
#ifdef DEBUG
                                ObjHist[OBJ_SAVEDC]++;
#endif
                            }
                        }
                    }
                }

                pw += 2;    // next handle
            }

            // get next handle table.
            pw = MAKELP(hgdi,*pw);
        }
    }

    if (list == NULL)
    {
        list = (GDIOBJECTLIST)LocalAlloc(LPTR, sizeof(GDIOBJECT) * (count+1));

        if (list == NULL)
        {
            Assert(0);
            return NULL;
        }

        goto again;
    }

    Assert(i == count);
    list[i].h    = NULL;   // NULL terminate list
    list[i].type = 0;      // NULL terminate list

    DPF("END BuildGdiObjectList %d objects.", count);
    DPF("    DC:     %d", ObjHist[OBJ_DC]);
    DPF("    SaveDC: %d", ObjHist[OBJ_SAVEDC]);
    DPF("    Bitmap: %d", ObjHist[OBJ_BITMAP]);
    DPF("    Pen:    %d", ObjHist[OBJ_PEN]);
    DPF("    Palette:%d", ObjHist[OBJ_PALETTE]);
    DPF("    Brush:  %d", ObjHist[OBJ_BRUSH]);
    DPF("    Total:  %d", count);

    return list;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

BOOL BeginGdiSnapshot(void)
{
    if (GdiObjectList != NULL)
        return TRUE;

    GdiObjectList = BuildGdiObjectList();

    return GdiObjectList != NULL;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

void EndGdiSnapshot(void)
{
    if (GdiObjectList != NULL)
    {
        LocalFree((HLOCAL)GdiObjectList);
        GdiObjectList = NULL;
    }
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

void EnumGdiObjects(UINT type, EnumGdiObjectsCallback callback, LPARAM lParam)
{
    int i;

    Assert(GdiObjectList != NULL);

    if (GdiObjectList == NULL)
        return;

    for (i=0; GdiObjectList[i].h; i++)
    {
        if (GdiObjectList[i].type == type)
        {
            (*callback)(GdiObjectList[i].h, lParam);
        }
    }
}

#ifdef DEBUG
/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

LPCSTR GetObjectOwnerName(HGDIOBJ hgdi)
{
    int i;
    HMODULE hModule;
    HANDLE h = GetObjectOwner(hgdi);
    static char ach[80];

    if (h == 0)
        return "System";
    else if (h == (HANDLE)1)
        return "Orphan";
    else if (hModule = (HMODULE)GetExePtr(h))
    {
        GetModuleFileName(hModule, ach, sizeof(ach));
        for (i=lstrlen(ach); i>0 && ach[i-1]!='\\'; i--)
            ;
        return ach+i;
    }
    else
    {
        wsprintf(ach, "#%04X", h);
        return ach;
    }
}
#endif

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

HBITMAP CurrentBitmap(HDC hdc)
{
    HBITMAP hbm;
    if (hbm = SafeSelectObject(hdc, StockBitmap()))
        SafeSelectObject(hdc, hbm);
    return hbm;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

HBRUSH CurrentBrush(HDC hdc)
{
    HBRUSH hbr;
    if (hbr = SafeSelectObject(hdc, GetStockObject(BLACK_BRUSH)))
        SafeSelectObject(hdc, hbr);
    return hbr;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

HPEN CurrentPen(HDC hdc)
{
    HPEN pen;
    if (pen = SafeSelectObject(hdc, GetStockObject(BLACK_PEN)))
        SafeSelectObject(hdc, pen);
    return pen;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

HPALETTE CurrentPalette(HDC hdc)
{
    HPALETTE hpal;
    if (hpal = SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE))
        SelectPalette(hdc, hpal, FALSE);
    return hpal;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

HDC GetBitmapDC(HBITMAP hbm)
{
    int i;
    HDC hdc;
    HBITMAP hbmT;

    Assert(GdiObjectList != NULL);

    hdc = CreateCompatibleDC(NULL);
    hbmT = SelectObject(hdc, hbm);
    DeleteDC(hdc);

    //
    // if we can select this bitmap into a memDC, it is not selected.
    // into any other DC
    //
    if (hbmT != NULL)
        return NULL;

    if (GdiObjectList == NULL)
        return NULL;

    for (i=0; GdiObjectList[i].h; i++)
    {
        if (GdiObjectList[i].type == OBJ_DC)
        {
            if (CurrentBitmap((HDC)GdiObjectList[i].h) == hbm)
                return GdiObjectList[i].h;
        }
    }

    return NULL;
}

/*----------------------------------------------------------------------------*\
 * GetObjectPalette
\*----------------------------------------------------------------------------*/

HPALETTE GetObjectPalette(HGDIOBJ h)
{
    HANDLE owner = GetObjectOwner(h);
    HPALETTE hpal;
    HPALETTE hpal20=NULL;
    HPALETTE hpal256=NULL;
    HPALETTE hpalDef = GetStockObject(DEFAULT_PALETTE);
    int i;
    int count20;
    int count256;

    Assert(GdiObjectList != NULL);

    //
    // look at all the palettes owned by the app
    // mabey if we are lucky there will only be one.
    //
    for (i=count20=count256=0; GdiObjectList[i].h; i++)
    {
        if (GdiObjectList[i].type == OBJ_PALETTE)
        {
            hpal=(HPALETTE)GdiObjectList[i].h;

            if (hpal == hpalDef)
                continue;

            if (GetObjectOwner(hpal) == owner)
            {
                int n = 0;
                GetObject(hpal, sizeof(n), &n);

                if (n > 20)
                {
                    count256++;
                    hpal256 = hpal;
                }
                else
                {
                    count20++;
                    hpal20 = hpal;
                }
            }
        }
    }

    if (count256 == 1)
    {
        DPF("got palette (%04X) because app (%s) only has one palette", hpal256, GetObjectOwnerName(h));
        return hpal256;
    }

    if (count256 == 2 && count20 == 0)
    {
        DPF("got palette (%04X) because app (%s) only has two palettes", hpal256, GetObjectOwnerName(h));
        return hpal256;
    }

    if (count20 == 1 && count256 == 0)
    {
        DPF("got palette (%04X) because app (%s) only has one palette", hpal20, GetObjectOwnerName(h));
        return hpal20;
    }

    if (count20 == 0 && count256 == 0)
    {
        DPF("no palette for (%04X) because app (%s) has none.", h, GetObjectOwnerName(h));
        return GetStockObject(DEFAULT_PALETTE);
    }

    DPF("**** cant find palette for (%04X) ****", h);
    return NULL;
}

/*----------------------------------------------------------------------------*\
 * GetBitmapPalette
 *
 * try to find out the palette that the given DDB uses, this is done be a series
 * of hacks and it only works some of the time.
 *
\*----------------------------------------------------------------------------*/

HPALETTE GetBitmapPalette(HBITMAP hbm)
{
    BITMAP      bm;
    DWORD       dw;
    HDC         hdc;
    HPALETTE    hpal;
    HPALETTE    hpalClip=NULL;
    HBITMAP     hbmClip=NULL;

    Assert(GdiObjectList != NULL);

    //
    // get the bitmap info, if it is not a bitmap palette is NULL
    //
    if (GetObject(hbm, sizeof(bm), &bm) == 0)
        return NULL;

    //
    // DIBSections dont have or need palettes
    //
    if (bm.bmBits != NULL)
        return NULL;

    //
    // 8 bit DDBs are the only bitmaps that care about palettes
    //
    if (bm.bmBitsPixel != 8 || bm.bmPlanes != 1)
        return NULL;

    //
    //  with a new DIBENG it will give us the palette
    //  in the bitmap dimension, what a hack
    //
    dw = GetBitmapDimension(hbm);

    if (dw && IsGDIObject((HGDIOBJ)HIWORD(dw)) == OBJ_PALETTE &&
        HIWORD(dw) != (UINT)GetStockObject(DEFAULT_PALETTE))
    {
        DPF("got palette (%04X) from the DIBENG", HIWORD(dw), hbm);
        return (HPALETTE)HIWORD(dw);
    }

    //
    // if the bitmap is on the clipboard we know what palette to use
    //
    if (IsClipboardFormatAvailable(CF_PALETTE))
    {
        if (OpenClipboard(NULL))
        {
            hpalClip = GetClipboardData(CF_PALETTE);
            hbmClip = GetClipboardData(CF_BITMAP);
            CloseClipboard();
	}

        if (hbm == hbmClip)
        {
            DPF("got palette (%04X) from the clipboard", hpalClip);
            return hpalClip;
        }
    }

    //
    // try to find a palette by looking at palettes owned by the app.
    //
    hpal = GetObjectPalette(hbm);

    //
    // we can figure out the palette of the app, return it
    //
    if (hpal)
    {
        if (hpal == GetStockObject(DEFAULT_PALETTE))
            return NULL;
        else
            return hpal;
    }

    //
    // if the bitmap is selected into a memoryDC check to see if
    // the memoryDC has a palette.
    //
    if ((hdc = GetBitmapDC(hbm)) && (hpal = CurrentPalette(hdc)))
    {
        if (hpal != GetStockObject(DEFAULT_PALETTE))
        {
            DPF("got palette (%04X) from memDC (%04X)", hpal, hdc);
            return hpal;
        }
    }

    DPF("**** cant find palette for bitmap (%04X) ****", hbm);
    return NULL;
}

/*----------------------------------------------------------------------------*\
 * ConvertDDBtoDS
 *
 * converts a DDB to a DIBSection
 * the conversion is done in place so the handle does not change.
\*----------------------------------------------------------------------------*/

HBITMAP ConvertDDBtoDS(HBITMAP hbm)
{
    BITMAP bm;
    HBITMAP hbmT;
    HDC hdc;
    HDC hdcSel;
    HPALETTE hpal;
    LPVOID lpBits;
    HANDLE owner;
    BOOL IsPrivate;
    int i;
    DWORD size;
    DIB8 dib;
    UINT SelCount;
    DWORD dw;

    if (GetObject(hbm, sizeof(bm), &bm) == 0)
        return NULL;

    if (bm.bmBits)
        return NULL;

    if (bm.bmPlanes == 1 && bm.bmBitsPixel == 1)
        return NULL;

    dw = GetBitmapDimension(hbm);

    owner = GetObjectOwner(hbm);

//  if (owner == 0)
//      return NULL;

    hpal = GetBitmapPalette(hbm);

    hdc = GetDC(NULL);

    if (hpal)
    {
        SelectPalette(hdc, hpal, TRUE);
        RealizePalette(hdc);
    }

    dib.bi.biSize = sizeof(BITMAPINFOHEADER);
    dib.bi.biBitCount = 0;
    GetDIBits(hdc, hbm, 0, 1, NULL, (LPBITMAPINFO)&dib.bi, DIB_RGB_COLORS);
    GetDIBits(hdc, hbm, 0, 1, NULL, (LPBITMAPINFO)&dib.bi, DIB_RGB_COLORS);

    dib.bi.biXPelsPerMeter = 0x42424242;    // special flag marking a DDB
    dib.bi.biHeight = -bm.bmHeight;         // top-down DIB

    if (hpal)
        SelectPalette(hdc, (HPALETTE)GetStockObject(DEFAULT_PALETTE), TRUE);

    // we dont have a palette, best guess is the system palette
    if (hpal == NULL && (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE))
    {
        DPF("Converting DDB(%04X) to DS for %s (using syspal)", hbm, GetObjectOwnerName(hbm));
        GetSystemPaletteEntries(hdc, 0, 256, (LPPALETTEENTRY)dib.ct);
        for (i=0; i<256; i++)
            dib.ct[i] = RGB(GetBValue(dib.ct[i]), GetGValue(dib.ct[i]), GetRValue(dib.ct[i]));
    }
    else if (hpal)
    {
        DPF("Converting DDB(%04X) to DS for %s, using palette (%04X)", hbm, GetObjectOwnerName(hbm), hpal);
    }
    else
    {
        DPF("Converting DDB(%04X) to DS for %s", hbm, GetObjectOwnerName(hbm));
    }

    ReleaseDC(NULL, hdc);

    size = (DWORD)bm.bmWidthBytes * bm.bmHeight;
    lpBits = GlobalAllocPtr(GHND, size);
    Assert(lpBits != NULL);

    if (lpBits == NULL)
        return NULL;

    GetBitmapBits(hbm, size, lpBits);

    IsPrivate = MakeObjectPrivate(hbm, FALSE);

    hdcSel = GetBitmapDC(hbm);

    if (hdcSel)
        SelectObject(hdcSel, StockBitmap());

    SelCount = GetW(hbm, 16);

    if (SelCount != 0)
    {
        DPF("***** bitmap %04X select count is %d, must be in a SaveDC block!", hbm, SelCount);
        SetW(hbm, 16, 0);
    }

    DeleteBitmap(hbm);

    if (IsGDIObject(hbm))
    {
        DPF("***** UNABLE TO DELETE bitmap %04X *****", hbm);
        Assert(0);
    }
    else
    {
        hbmT = CreateDIBSection(NULL, (LPBITMAPINFO)&dib.bi, DIB_RGB_COLORS, NULL, NULL, 0);
        Assert(hbmT == hbm);
        SetBitmapBits(hbm, size, lpBits);
    }
    GlobalFreePtr(lpBits);

    if (SelCount)
        SetW(hbm, 16, SelCount);

    SetObjectOwner(hbm, owner);
    MakeObjectPrivate(hbm, IsPrivate);

    if (hdcSel)
        SelectObject(hdcSel, hbm);

    SetBitmapDimension(hbm, LOWORD(dw), HIWORD(dw));

    return hbm;
}

/*----------------------------------------------------------------------------*\
 * Convert DStoDDB
 *
 * convert a DIBSection back to a DDB, we only do this if the DIBSection
 * came from a DDB (ConvertDDBtoDS puts a magic value in biXPelsPerMeter)
 * the conversion is done in place so the handle does not change.
\*----------------------------------------------------------------------------*/

HBITMAP ConvertDStoDDB(HBITMAP hbm, BOOL fForceConvert)
{
    struct {
        BITMAP bm;
        BITMAPINFOHEADER bi;
        DWORD ct[256];
    }   ds;
    HDC hdcSel;
    HDC hdc;
    HBITMAP hbmT;
    HANDLE owner;
    BOOL IsPrivate;
    LPVOID lpBits;
    DWORD size;
    int planes,bpp,rc;
    UINT SelCount;
    DWORD dw;

    hdc = GetDC(NULL);
    bpp = GetDeviceCaps(hdc, BITSPIXEL);
    planes = GetDeviceCaps(hdc, PLANES);
    rc = GetDeviceCaps(hdc, RASTERCAPS);
    ReleaseDC(NULL, hdc);

    if (GetObject(hbm, sizeof(ds), &ds) == 0)
        return NULL;

    if (ds.bm.bmBits == NULL)
        return NULL;

    if (ds.bm.bmBitsPixel == 1)
	return NULL;

    if (ds.bi.biXPelsPerMeter != 0x42424242)
        return NULL;

    if (ds.bi.biHeight >= 0)
        return NULL;

    //
    //	HACK we want to convert bitmaps that are exactly 8x8
    //	back to DDBs always. Win95 GDI does not support
    //	Creating a pattern brush from a DIBSection so
    //	we must do this.
    //
    if (ds.bm.bmWidth == 8 && ds.bm.bmHeight == 8)
    {
	DPF("Converting 8x8 DS(%04X) back to DDB for %s", hbm, GetObjectOwnerName(hbm));
	fForceConvert = TRUE;
    }

    //
    // unless force convert is TRUE we only want to be here in 8bpp mode.
    //
    if (!fForceConvert && !(rc & RC_PALETTE))
	return NULL;

    if (!fForceConvert && (ds.bm.bmPlanes != planes || ds.bm.bmBitsPixel != bpp))
	return NULL;

    dw = GetBitmapDimension(hbm);

    owner = GetObjectOwner(hbm);

//  if (owner == 0)
//      return NULL;

    DPF("Converting DS(%04X) %dx%dx%d to DDB for %s", hbm, ds.bm.bmWidth, ds.bm.bmHeight, ds.bm.bmBitsPixel, GetObjectOwnerName(hbm));

    hdcSel = GetBitmapDC(hbm);

    size = (DWORD)ds.bm.bmWidthBytes * ds.bm.bmHeight;
    lpBits = GlobalAllocPtr(GHND, size);
    Assert(lpBits != NULL);

    if (lpBits == NULL)
        return NULL;

    IsPrivate = MakeObjectPrivate(hbm, FALSE);

    if (hdcSel)
        SelectObject(hdcSel, StockBitmap());

    hdc = GetDC(NULL);

    if (ds.bm.bmPlanes == planes && ds.bm.bmBitsPixel == bpp)
        GetBitmapBits(hbm, size, lpBits);
    else
        GetDIBits(hdc, hbm, 0, ds.bm.bmHeight, lpBits, (LPBITMAPINFO)&ds.bi, DIB_RGB_COLORS);

    SelCount = GetW(hbm, 16);

    if (SelCount != 0)
    {
        DPF("bitmap %04X select count is %d, must be in a SaveDC block!", hbm, SelCount);
        SetW(hbm, 16, 0);
    }

    DeleteBitmap(hbm);
    if (IsGDIObject(hbm))
    {
        DPF("***** UNABLE TO DELETE bitmap %04X *****", hbm);
        Assert(0);
    }
    else
    {
        hbmT = CreateCompatibleBitmap(hdc,ds.bm.bmWidth,ds.bm.bmHeight);
        Assert(hbmT == hbm);

        if (ds.bm.bmPlanes == planes && ds.bm.bmBitsPixel == bpp)
            SetBitmapBits(hbm, size, lpBits);
        else
            SetDIBits(hdc, hbm, 0, ds.bm.bmHeight, lpBits, (LPBITMAPINFO)&ds.bi, DIB_RGB_COLORS);
    }
    ReleaseDC(NULL, hdc);

    GlobalFreePtr(lpBits);

    if (SelCount)
        SetW(hbm, 16, SelCount);

    SetObjectOwner(hbm, owner);
    MakeObjectPrivate(hbm, IsPrivate);

    if (hdcSel)
        SelectObject(hdcSel, hbm);

    SetBitmapDimension(hbm, LOWORD(dw), HIWORD(dw));

    return hbm;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
void FlushGdiXlatCache()
{
    DIB8    dib;
    HDC     hdc;
    HBITMAP hbm;

    if (hbm = CreateBitmap(1,1,1,1,NULL))
    {
        if (hdc = CreateCompatibleDC(NULL))
        {
            SelectBitmap(hdc, hbm);

            dib.bi.biSize           = sizeof(BITMAPINFOHEADER);
            dib.bi.biWidth          = 1;
            dib.bi.biHeight         = 1;
            dib.bi.biPlanes         = 1;
            dib.bi.biCompression    = 0;
            dib.bi.biSizeImage      = 0;
            dib.bi.biXPelsPerMeter  = 0;
            dib.bi.biYPelsPerMeter  = 0;
            dib.bi.biClrUsed        = 2;
            dib.bi.biClrImportant   = 0;
            dib.ct[0]               = RGB(1,1,1);
            dib.ct[2]               = RGB(2,2,2);

            for (dib.bi.biBitCount  = 1;
                 dib.bi.biBitCount <= 8;
                 dib.bi.biBitCount  = (dib.bi.biBitCount + 4) & ~1)
            {
                SetDIBits(hdc, hbm, 0, 1, (LPVOID)&dib.bi,
                    (LPBITMAPINFO)&dib.bi, DIB_PAL_COLORS);
            }

            DeleteDC(hdc);
        }

        DeleteBitmap(hbm);
    }
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
void ReSelectObjects(HGDIOBJ h, LPARAM lParam)
{
    COLORREF rgb;
    UINT hf;
    HDC hdc = (HDC)h;

////DPF("ReSelecting objects for DC %04X", h);

    // this prevents USER from RIPing because we are using
    // DCs in the cache without calling GetDC()
    hf = SetHookFlags(hdc, DCHF_VALIDATEVISRGN);

    SelectObject(hdc, SelectObject(hdc, GetStockObject(BLACK_BRUSH)));
    SelectObject(hdc, SelectObject(hdc, GetStockObject(BLACK_PEN)));
    GDISelectPalette(hdc, GDISelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), TRUE), TRUE);

    rgb = GetTextColor(hdc);
    SetTextColor(hdc, rgb ^ 0xFFFFFF);
    SetTextColor(hdc, rgb);

    rgb = GetBkColor(hdc);
    SetBkColor(hdc, rgb ^ 0xFFFFFF);
    SetBkColor(hdc, rgb);

    SetHookFlags(hdc, hf);
}

/////////////////////////////////////////////////////////////////////////////
//
// ReRealizeObjects
//
// calls ReRealizeObject for every pen/brush in the system, this makes sure
// all pens/brushs will be rerealized next time they are used.
//
// call ReSelectObjects() to make sure the current pen/brush/text colors
// are correct in all DCs
//
/////////////////////////////////////////////////////////////////////////////

void ReRealizeObjects()
{
    BeginGdiSnapshot();

    FlushGdiXlatCache();

    EnumGdiObjects(OBJ_BRUSH, ReRealizeObject, 0);
    EnumGdiObjects(OBJ_PEN,   ReRealizeObject, 0);
    EnumGdiObjects(OBJ_DC,    ReSelectObjects, 0);

    EnumGdiObjects(OBJ_SAVEDC,SaveDCFix, 0);
    EnumGdiObjects(OBJ_SAVEDC,SaveDCReSelectObjects, 0);

    EndGdiSnapshot();
}

/////////////////////////////////////////////////////////////////////////////
//
// ConvertObjects
//
// convert all DDBs to DIBSections
// convert all color pattern brush's to DIBPattern brushs
// convert all 8bpp icons to 4bpp icons.
//
/////////////////////////////////////////////////////////////////////////////

void ConvertBitmapCB(HGDIOBJ h, LPARAM lParam)
{
    ConvertDDBtoDS(h);
}

void ConvertBrushCB(HGDIOBJ h, LPARAM lParam)
{
    ConvertPatternBrush(h);
}

void ConvertObjects()
{
    BeginGdiSnapshot();
    EnumGdiObjects(OBJ_BITMAP, ConvertBitmapCB, 0);
    EnumGdiObjects(OBJ_BRUSH,  ConvertBrushCB, 0);
    EndGdiSnapshot();
}

/////////////////////////////////////////////////////////////////////////////
//
// ConvertBitmapsBack
//
// convert all DIBSections to DDBs
//
/////////////////////////////////////////////////////////////////////////////

void ConvertBitmapBackCB(HGDIOBJ h, LPARAM lParam)
{
    ConvertDStoDDB(h, (BOOL)lParam);
}

void ConvertBitmapsBack(BOOL fForceConvert)
{
    BeginGdiSnapshot();
    EnumGdiObjects(OBJ_BITMAP, ConvertBitmapBackCB, fForceConvert);
    EndGdiSnapshot();
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// BEGIN EVIL
//
// the next few functions mess directly with GDI code/data
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

LPWORD LockObj(HGDIOBJ h, UINT off)
{
    WORD FAR *pw;
    UINT hGDI = GetGdiDS();

    pw = MAKELP(hGDI, h);

    if (IsBadReadPtr(pw, 2))
        return NULL;

    pw = MAKELP(hGDI, *pw + off);

    if (IsBadReadPtr(pw, 2))
        return NULL;

    return pw;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

WORD GetW(HGDIOBJ h, UINT off)
{
    WORD FAR *pw;

    if (pw = LockObj(h, off))
        return *pw;
    else
        return 0;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

WORD SetW(HGDIOBJ h, UINT off, WORD w)
{
    WORD FAR *pw;
    WORD ret = 0;

    if (pw = LockObj(h, off))
    {
        ret = *pw;
        *pw = w;
    }
    return ret;
}

/*----------------------------------------------------------------------------*\
 * ReRealizeObject
 *
 * delete all physical objects associated with the given GDI object
 * this will guarentee the next time the brush/pen is selected we will
 * have the device driver rerealize the object.
 *
 * there are a few ways to do this....
 *
 * method #1
 *     call SetSolidBrush()
 *     this only works for private/solid(not stock) brushes, not pens
 *     we need to save/restore the stock object bit.
 *     we need to save/restore the private bit.
 *
 * method #2
 *     delete the object and recreate it getting the same handle
 *     we need to patch the SelCount because we cant delete a selected obj
 *     we need to save/restore the stock object bit.
 *     we need to save/restore the private bit.
 *     we need to save/restore the owner.
 *
 * method #3
 *     create a temp object, move the physchain from the given object
 *     to the new object, delete the temp object.
 *     we need to patch phys chain of the objects.
 *
 * after deleting all the physical objects ReSelectObjects() should be
 * called to clean up all the objects currently selected in all DCs
 *
 * SaveDCs are a pain in the neck, ReSelectObjects() does not deal with
 * the SaveDC blocks floating around GDIs heap. we need to fix this
 * in the general case, the system savedc's just have the white_brush
 * and black_pen.
 *
 * currently using method #3
 *
\*----------------------------------------------------------------------------*/

void ReRealizeObject(HGDIOBJ h, LPARAM lParam)
{
    HGDIOBJ hTemp;
    UINT type;

    type = IsGDIObject(h);

    //
    // if the object does not have a physchain we have no work to do!
    //
    if (GetW(h, 0) == 0)
        return;

    //
    // create a temp pen/brush so we can delete it and trick
    // GDI into disposing all the phys objects.
    //

    if (type == OBJ_BRUSH)
        hTemp = CreateSolidBrush(RGB(1,1,1));
    else if (type == OBJ_PEN)
        hTemp = CreatePen(PS_SOLID, 0, RGB(1,1,1));
    else
        return;

    Assert(hTemp != NULL);
    Assert(GetW(hTemp, 0) == 0);

    if (type == OBJ_BRUSH)
        DPF("ReRealize Brush %04X for %s", h, GetObjectOwnerName(h));
    else
        DPF("ReRealize Pen %04X for %s", h, GetObjectOwnerName(h));

    //
    // copy the phys chain from the passed in object to the
    // temp object then call DeleteObject to free them.
    //
    SetW(hTemp, 0, GetW(h, 0));
    SetW(h, 0, 0);

    DeleteObject(hTemp);
    return;
}

/*----------------------------------------------------------------------------*\
 * ConvertPatternBrush
 *
 * convert a BS_PATTERN brush to a BS_DIBPATTERN brush.
 * we only convert non-mono pattern brushes
\*----------------------------------------------------------------------------*/

HBRUSH ConvertPatternBrush(HBRUSH hbr)
{
    LOGBRUSH lb;
    HBITMAP hbm;
    COLORREF c0, c1;
    HDC hdc;

    if (GetObject(hbr, sizeof(lb), &lb) == 0)
        return NULL;

    if (lb.lbStyle != BS_PATTERN)
        return NULL;

    hdc = GetDC(NULL);
    hbm = CreateCompatibleBitmap(hdc, 8, 8);
    ReleaseDC(NULL, hdc);

    hdc = CreateCompatibleDC(NULL);
    SelectObject(hdc, hbm);
    SelectObject(hdc, hbr);

    SetTextColor(hdc, 0x000000);
    SetBkColor(hdc, 0x000000);
    PatBlt(hdc, 0, 0, 8, 8, PATCOPY);
    c0 = GetPixel(hdc, 0, 0);

    SetTextColor(hdc, 0xFFFFFF);
    SetBkColor(hdc, 0xFFFFFF);
    PatBlt(hdc, 0, 0, 8, 8, PATCOPY);
    c1 = GetPixel(hdc, 0, 0);

    //
    // if the brush is a mono pattern brush dont convert it
    //
    if (c0 == c1)
    {
        HANDLE h;
        LPBITMAPINFOHEADER lpbi;
        HBRUSH hbrT;
        HANDLE owner;
        BOOL IsPrivate;
        WORD Flags;
        HPALETTE hpal=NULL;

        if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)
        {
            hpal = GetObjectPalette(hbr);

            if (hpal == GetStockObject(DEFAULT_PALETTE))
                hpal = NULL;
        }

        if (hpal)
        {
            SelectPalette(hdc, hpal, TRUE);
            RealizePalette(hdc);
            PatBlt(hdc, 0, 0, 8, 8, PATCOPY);

            DPF("Converting pattern brush %04X for %s (using hpal=%04X)", hbr, GetObjectOwnerName(hbr), hpal);
        }
        else
        {
            DPF("Converting pattern brush %04X for %s", hbr, GetObjectOwnerName(hbr));
        }

        h = GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER) + 256*4 + 8*8*4);

        Assert(h != NULL);
        if (h == NULL)
            return hbr;

        lpbi = (LPBITMAPINFOHEADER)GlobalLock(h);

        lpbi->biSize = sizeof(BITMAPINFOHEADER);
        lpbi->biBitCount = 0;
        GetDIBits(hdc, hbm, 0, 1, NULL, (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);

        if (lpbi->biClrUsed == 0 && lpbi->biCompression == BI_BITFIELDS)
            lpbi->biClrUsed = 3;

        if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
            lpbi->biClrUsed = (1 << lpbi->biBitCount);

        GetDIBits(hdc, hbm, 0, (int)lpbi->biHeight,
            (LPBYTE)lpbi + lpbi->biSize + lpbi->biClrUsed*sizeof(RGBQUAD),
            (LPBITMAPINFO)lpbi, DIB_RGB_COLORS);

        owner = SetObjectOwner(hbr, 0);
        IsPrivate = MakeObjectPrivate(hbr, FALSE);
        Flags = SetW(hbr, 10, 0);

        DeleteObject(hbr);

        if (IsGDIObject(hbr))
        {
            DPF("***** UNABLE TO DELETE brush %04X *****", hbr);
            Assert(0);
        }
        else
        {
            hbrT = CreateDIBPatternBrush(h, DIB_RGB_COLORS);
            Assert(hbrT == hbr);
        }

        GlobalFree(h);

        SetW(hbr, 10, Flags);
        MakeObjectPrivate(hbr, IsPrivate);
        SetObjectOwner(hbr, owner);

        if (hpal)
        {
            SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), TRUE);
        }
    }

    DeleteDC(hdc);
    DeleteObject(hbm);
    return hbr;
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

LPVOID GetPDevice(HDC hdc)
{
    DWORD FAR *pdw;

    Assert(IsGDIObject(hdc) == OBJ_DC);

    if (IsGDIObject(hdc) != OBJ_DC)
        return NULL;

    PresDC(hdc);

    if (pdw = (DWORD FAR *)LockObj(hdc, 0x30))
        return (LPVOID)*pdw;
    else
        return NULL;

////return MAKELP(GetW(hdc, 0x32), GetW(hdc, 0x30));
}

/*----------------------------------------------------------------------------*\
 *
 * get the "internal" version of a GDI api
 * we need to do this so we can call GDI APIs like SelectObject and
 * SetTextColor on SaveDC blocks.
 *
 * we only need to do this on SaveDC blocks, not every DC
 *
 * the code must look like this or we fail:
 *
 * RealProc:
 *     .....
 *     JMP  ####   <== Internal version of RealProc
 *     mov  dh,80  (optinal)
 *     RETF NumParams
 * NextProc:
 *
\*----------------------------------------------------------------------------*/

FARPROC GetInternalProc(FARPROC RealProc, FARPROC NextProc, UINT NumParams)
{
    LPBYTE pb = (LPBYTE)NextProc;

    if ((DWORD)NextProc == 0 ||
        (DWORD)RealProc == 0 ||
        LOWORD(RealProc) <= 6 ||
        (DWORD)NextProc <= (DWORD)RealProc ||
        ((DWORD)NextProc - (DWORD)RealProc) > 80)
    {
        Assert(0);
        return RealProc;
    }

    if (pb[-6] == 0xE9 && pb[-3] == 0xCA && pb[-2] == NumParams && pb[-1] == 0x00)
    {
        return (FARPROC)MAKELP(SELECTOROF(pb), OFFSETOF(pb)-3+*(WORD FAR *)(pb-5));
    }

    if (pb[-8] == 0xE9 && pb[-5] == 0xB6 && pb[-4] == 0x80 &&
        pb[-3] == 0xCA && pb[-2] == NumParams && pb[-1] == 0x00)
    {
        return (FARPROC)MAKELP(SELECTOROF(pb), OFFSETOF(pb)-5+*(WORD FAR *)(pb-7));
    }

    Assert(0);
    return RealProc;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/

#define DCisMem         0x01    // DC is to a memory bitmap
#define DCisDisplay     0x02    // DC is to the screen device
#define DC_DIB          0x80
#define BITMAP_DIB      0x04
#define ChkDispPal      0x0200

BOOL IsValidSaveDC(HGDIOBJ h)
{
    HBITMAP         hbm;
    DIBENGINE FAR * pde;
    UINT            dcFlags;

    if (IsGDIObject(h) != OBJ_DC)
    {
        DPF("*** invalid SaveDC (%04X)", h);
        return FALSE;
    }

    dcFlags = GetW(h, 0x0E);

    if (!(dcFlags & DCisDisplay))
    {
        DPF("*** SaveDC (%04X) not a display DC", h);
        return FALSE;
    }

    hbm = (HBITMAP)GetW(h, 0x1E);

    if (IsGDIObject(hbm) != OBJ_BITMAP)
    {
        DPF("*** SaveDC (%04X) has invalid bitmap (%04X)", h, hbm);
        return FALSE;
    }

    pde = (DIBENGINE FAR *)MAKELP(GetW(h, 0x32), GetW(h, 0x30));

    if (IsBadReadPtr(pde, sizeof(DIBENGINE)))
    {
        DPF("*** SaveDC (%04X) has bad lpPDevice (%04X:%04X)", h, HIWORD(pde), LOWORD(pde));
        return FALSE;
    }

    if (pde->deType != TYPE_DIBENG)
    {
        DPF("*** SaveDC (%04X) not a DIBENG PDevice (%04X:%04X)", h, HIWORD(pde), LOWORD(pde));
        return FALSE;
    }

    return TRUE;
}

/*----------------------------------------------------------------------------*\
\*----------------------------------------------------------------------------*/
void SaveDCReSelectObjects(HGDIOBJ h, LPARAM lParam)
{
    COLORREF rgb;
    HDC hdc = (HDC)h;

    static HGDIOBJ  (WINAPI *ISelectObject)(HDC hdc, HGDIOBJ h);
    static COLORREF (WINAPI *ISetTextColor)(HDC hdc, COLORREF rgb);
    static COLORREF (WINAPI *ISetBkColor)(HDC hdc, COLORREF rgb);

    if (ISelectObject == NULL)
    {
        (FARPROC)ISelectObject = GetInternalProc((FARPROC)SelectObject, (FARPROC)SetTextColor, 4);
        (FARPROC)ISetTextColor = GetInternalProc((FARPROC)SetTextColor, (FARPROC)SetBkColor, 6);
        (FARPROC)ISetBkColor   = GetInternalProc((FARPROC)SetBkColor,   (FARPROC)SetBkMode, 6);
    }

    if (IsValidSaveDC(h))
    {
        DPF("ReSelecting objects for SaveDC %04X", h);

        ISelectObject(hdc, ISelectObject(hdc, GetStockObject(BLACK_BRUSH)));
        ISelectObject(hdc, ISelectObject(hdc, GetStockObject(BLACK_PEN)));

        rgb = ISetTextColor(hdc, 0x000000);
        ISetTextColor(hdc, 0xFFFFFF);
        ISetTextColor(hdc, rgb);

        rgb = ISetBkColor(hdc, 0x000000);
        ISetBkColor(hdc, 0xFFFFFF);
        ISetBkColor(hdc, rgb);
    }
}

/*----------------------------------------------------------------------------*\
 *
 *  SaveDCFix
 *
 *  make sure the dcPlanes and dcBitsPixel are patched right in SaveDC blocks.
 *
\*----------------------------------------------------------------------------*/

void SaveDCFix(HGDIOBJ h, LPARAM lParam)
{
    HBITMAP         hbm;
    DIBENGINE FAR * pde;
    UINT            dcFlags;
    UINT            dcPlanesBitsPixel;
    UINT            dePlanesBitsPixel;

    if (!IsValidSaveDC(h))
    {
        return;
    }

    dcPlanesBitsPixel = GetW(h, 0x9C);
    dcFlags = GetW(h, 0x0E);

    if (dcPlanesBitsPixel == 0x0101)
    {
        DPF("not Patching dcBitsPixel for SaveDC %04X (mono)", h);
        return;
    }

    if (LOBYTE(dcPlanesBitsPixel) != 1)
    {
        DPF("not Patching dcBitsPixel for SaveDC %04X (planes!=1)", h);
        Assert(0);
        return;
    }

    if (dcFlags & ChkDispPal)
    {
        DPF("clearing ChkDispPal flag for SaveDC %04X", h);
        SetW(h, 0x0E, dcFlags & ~ChkDispPal);
    }

    if ((dcFlags & DCisMem) && (hbm = (HBITMAP)GetW(h, 0x1E)) != StockBitmap())
    {
        HDC  hdcSel;
        HDC  hdc;

        hdcSel = GetBitmapDC(hbm);

        if (hdcSel)
        {
            DPF("*******************************************");
            DPF("*** SaveDC (%04X) has non-stock bitmap. ***", h);
            DPF("*******************************************");
            hdc = hdcSel;
        }
        else
        {
            DPF("**********************************************");
            DPF("*** SaveDC (%04X) has non-selected bitmap. ***", h);
            DPF("*** restoring bitmap to STOCK bitmap.      ***");
            DPF("**********************************************");
            hdc = CreateCompatibleDC(NULL);
        }

        //
        //  copy over the important stuff from the RealDC to the SaveDC
        //
        if (hdc)
        {
            PresDC(hdc);

            SetW(h, 0x0F, GetW(hdc, 0x0F));      // DCFlags2

            SetW(h, 0x26, GetW(hdc, 0x26));      // hPDeviceBlock
            SetW(h, 0x38, GetW(hdc, 0x38));      // pPDeviceBlock

            SetW(h, 0x22, GetW(hdc, 0x22));      // hLDevice
            SetW(h, 0x34, GetW(hdc, 0x34));      // pLDevice

            SetW(h, 0x16, GetW(hdc, 0x16));      // hPDevice
            SetW(h, 0x30, GetW(hdc, 0x30));      // lpPDevice.off
            SetW(h, 0x32, GetW(hdc, 0x32));      // lpPDevice.sel
            SetW(h, 0x36, GetW(hdc, 0x36));      // hBitBits

            SetW(h, 0x9C, GetW(hdc, 0x9C));      // dcPlanes + dcBitsPixel
        }

        if (hdc && hdcSel == NULL)
        {
            DeleteDC(hdc);
        }

        return;

#if 0 // broken code
        SetW(h, 0x30, 0);                       // lpPDevice.off
        SetW(h, 0x32, GetW(hbm, 0x0E));         // lpPDevice.sel
        SetW(h, 0x36, GetW(hbm, 0x0E));         // hBitBits

        w = GetW(h, 0x0F);                      // DCFlags2

        if (GetW(hbm, 0x1E) & BITMAP_DIB)       // bmFlags
            w |= DC_DIB;
        else
            w &= ~DC_DIB;

        SetW(h, 0x0F, w);                       // DCFlags2
#endif
    }

    pde = (DIBENGINE FAR *)MAKELP(GetW(h, 0x32), GetW(h, 0x30));
    Assert(!IsBadReadPtr(pde, sizeof(DIBENGINE)) && pde->deType == TYPE_DIBENG);

    dePlanesBitsPixel = *(WORD FAR *)&pde->dePlanes;

    if (dePlanesBitsPixel != dcPlanesBitsPixel)
    {
        DPF("Patching dcBitsPixel for SaveDC %04X %04X=>%04X", h, dcPlanesBitsPixel, dePlanesBitsPixel);
        SetW(h,0x9C,dePlanesBitsPixel);
    }
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// END EVIL
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

#ifdef DIRECT_DRAW
#undef DPF
#ifdef DEBUG
#define DPF DPF2

static void CDECL DPF2(char *sz, ...)
{
    char ach[128];
    wvsprintf(ach, sz, (LPVOID)(&sz+1));
#ifdef DIRECT_DRAW
    dprintf(2, ach);
#else
    lstrcat(ach, "\r\n");
    OutputDebugString(ach);
#endif
}

static void CDECL DPF5(char *sz, ...)
{
    char ach[128];
    wvsprintf(ach, sz, (LPVOID)(&sz+1));
#ifdef DIRECT_DRAW
    dprintf(5, ach);
#else
    lstrcat(ach, "\r\n");
    OutputDebugString(ach);
#endif
}

static void CDECL DPF7(char *sz, ...)
{
    char ach[128];
    wvsprintf(ach, sz, (LPVOID)(&sz+1));
#ifdef DIRECT_DRAW
    dprintf(7, ach);
#else
    lstrcat(ach, "\r\n");
    OutputDebugString(ach);
#endif
}

#else
#define DPF ; / ## /
#define DPF5 ; / ## /
#define DPF7 ; / ## /
#endif

// Utility for dumping information about ColorTables
#ifdef DEBUG_PAL
void DPF_PALETTE( BITMAPINFO *pbmi )
{
    DWORD i;
    DWORD *prgb = (DWORD *)(((BYTE *)pbmi)+pbmi->bmiHeader.biSize);
    DWORD cEntries = pbmi->bmiHeader.biClrUsed;

    if (pbmi->bmiHeader.biBitCount > 8)
	return;
    if (cEntries == 0)
	cEntries = 1 << (pbmi->bmiHeader.biBitCount);

    DPF7("Dumping Color table (0xFFRRGGBB) with %d entries", cEntries);
    for (i = 0; i < cEntries; i++)
    {
	DPF7("0x%lx", prgb[i]);
    }
}
#else
#define DPF_PALETTE(x)
#endif

// Utility for Dumping information about Bitmap Infos
#ifdef DEBUG_BMI
void DPF_PBMI( BITMAPINFO * pbmi )
{
    char *szT;
    DPF5("Dumping a BitmapInfo struct");
    DPF5("\t\tdeBitmapInfo->bmiHeader.biSize = %ld",pbmi->bmiHeader.biSize);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biWidth = %ld",pbmi->bmiHeader.biWidth);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biHeight = %ld",pbmi->bmiHeader.biHeight);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biPlanes = %d",pbmi->bmiHeader.biPlanes);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biBitCount = %d",pbmi->bmiHeader.biBitCount);
    szT = ((pbmi->bmiHeader.biCompression == BI_RGB) ? "BI_RGB" : "**UNKNOWN**");
    DPF5("\t\tdeBitmapInfo->bmiHeader.biCompression = 0x%lx(%s)",pbmi->bmiHeader.biCompression, szT);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biSizeImage = %ld",pbmi->bmiHeader.biSizeImage);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biXPelsPerMeter = 0x%lx",pbmi->bmiHeader.biXPelsPerMeter);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biYPelsPerMeter = 0x%lx",pbmi->bmiHeader.biYPelsPerMeter);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biClrUsed = %ld",pbmi->bmiHeader.biClrUsed);
    DPF5("\t\tdeBitmapInfo->bmiHeader.biClrImportant = %ld",pbmi->bmiHeader.biClrImportant);
    DPF_PALETTE(pbmi);
}
#else
#define DPF_PBMI(x)
#endif

// Utility for Dumping information about PDEs
#ifdef DEBUG_PDE
void DPF_PDE( DIBENGINE *pde )
{
    DPF5("Dumping a DIBENGINE struct.");
    DPF5("\tdeType = 0x%x(%s)",pde->deType,(pde->deType == TYPE_DIBENG ? "TYPE_DIBENG" : "**UNKNOWN**"));
    DPF5("\tdeWidth = %d",pde->deWidth);
    DPF5("\tdeHeight = %d",pde->deHeight);
    DPF5("\tdeWidthBytes = %d",pde->deWidthBytes);
    DPF5("\tdePlanes = %d",pde->dePlanes);
    DPF5("\tdeBitsPixel = %d",pde->deBitsPixel);
    DPF5("\tdeReserved1 = 0x%lx",pde->deReserved1);
    DPF5("\tdeDeltaScan = %ld",pde->deDeltaScan);
    DPF5("\tdelpPDevice = 0x%x",pde->delpPDevice);
    DPF5("\tdeBitsOffset = 0x%lx",pde->deBitsOffset);
    DPF5("\tdeBitsSelector = 0x%x",pde->deBitsSelector);
    DPF5("\tdeFlags = 0x%x(%s)",pde->deFlags,(pde->deFlags == SELECTEDDIB ? "SELECTEDDIB" : "**UNKNOWN**"));
    DPF5("\tdeVersion = %d(%s)",pde->deVersion,(pde->deVersion == VER_DIBENG ? "VER_DIBENG" : "**UNKNOWN**"));
    DPF5("\tdeBeginAccess = 0x%x",pde->deBeginAccess);
    DPF5("\tdeEndAccess = 0x%x",pde->deEndAccess);
    DPF5("\tdeDriverReserved = 0x%lx",pde->deDriverReserved);

    DPF_PBMI(pde->deBitmapInfo);
}
#else
#define DPF_PDE(x)
#endif



/////////////////////////////////////////////////////////////////////////////
//
//  DC stuff
//
/////////////////////////////////////////////////////////////////////////////
       DIBENGINE FAR *pdeDisplay;
       UINT FlatSel;
static HRGN hVisRgn;
static HDC hdcCache;
static BOOL bCache565;
static int in_use;
static int save_level;
static DWORD cacheBPP;

extern HINSTANCE hInstApp;

extern void FAR PASCAL SelectVisRgn(HDC, HRGN);
extern HDC  FAR PASCAL GetDCState(HDC);
extern void FAR PASCAL SetDCState(HDC,HDC);

BOOL DPMISetSelectorLimit(UINT selector, DWORD dwLimit);
extern DWORD PASCAL MapLS( LPVOID );	// flat -> 16:16
extern void PASCAL UnMapLS( DWORD ); // unmap 16:16

/////////////////////////////////////////////////////////////////////////////
//
//  SetDC
//	NOTE: all calls to SetDC must be matched with SetDC(hdc,0,0,0);
//
/////////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL SetDC(HDC hdc, HDC hdcDevice, LPDDSURFACEDESC pddsd, LPPALETTEENTRY lpPalette)
{
    DIBENGINE FAR *pde;
    int  width;
    int  height;
    int  bpp;
    UINT flags;
    DWORD p16Surface;

    pde = GetPDevice(hdc);

    if (pde == NULL)
        return FALSE;

    Assert(pde->deType == 0x5250);
    Assert(pdeDisplay && pdeDisplay->deType == 0x5250);

    if (pddsd == 0)
    {
        pde->deFlags       |= BUSY;
        pde->deBitsOffset   = 0;
        pde->deBitsSelector = 0;


	if( pde->deBitmapInfo->bmiHeader.biXPelsPerMeter == 0 )
	{
	    DPF("SetDC NULL called on a DC that was never cooked by DDraw.");
	    Assert(0);
	    return TRUE;
	}

	// This code "should be done" but it causes
	// us to SelectVisRgn more often then necessary (and more
	// often than we did in DX1-4). This is safer.
	// pde->deBitmapInfo->bmiHeader.biWidth = 1;
	// pde->deBitmapInfo->bmiHeader.biHeight = -1;
	// pde->deBitmapInfo->bmiHeader.biSizeImage = 4;

	// We need to unmap the selector we allocated below
	Assert(pde->deReserved1 != 0);
	UnMapLS(pde->deReserved1);

	// Basically, we just want to restore the flags
	// to what they were when we got DC originally
	DPF5("Restore pde->deReserved1 to 0x%lx", pde->deBitmapInfo->bmiHeader.biXPelsPerMeter);
	pde->deReserved1 = pde->deBitmapInfo->bmiHeader.biXPelsPerMeter;
	pde->deBitmapInfo->bmiHeader.biXPelsPerMeter = 0;

	Assert(pde->deReserved1 != 0);
	pde->deBitsSelector = (WORD)((DWORD)pde->deReserved1 >> 16);

        return TRUE;
    }

    // Allocate a selector
    p16Surface = MapLS(pddsd->lpSurface);
    if( !p16Surface )
    {
	DPF("Couldn't allocate selector; Out of selectors.");
	return FALSE;
    }
    if( (WORD)p16Surface != 0 )
    {
	DPF("MapLS didn't return a 16:0 pointer!");
	Assert(0);
	return FALSE;
    }

    // Set the selector limit for this chunk of memory
    Assert(pddsd->dwHeight > 0);
    Assert(pddsd->lPitch > 0);
    if( !DPMISetSelectorLimit( (UINT)(p16Surface>>16), (pddsd->dwHeight*pddsd->lPitch) - 1 ) )
    {
	DPF("Couldn't update selector; Out of selectors.");
	UnMapLS(p16Surface);
	return FALSE;
    }

    DPF5("SetDC: Details of PDE from initial hdc.");
    DPF_PDE(pde);

    width =  (int)pddsd->dwWidth,
    height = (int)pddsd->dwHeight,
    bpp =    (int)pddsd->ddpfPixelFormat.dwRGBBitCount,
    flags =  (UINT)pddsd->ddpfPixelFormat.dwRBitMask == 0xf800 ? FIVE6FIVE : 0;

    pde->deFlags       &= ~BUSY;
    // Also, make sure we set all if any banked bits are set in the driver
    // to encourage the DIBENG to avoid screen to screen blts (which are apparently buggy).
    // Only do this for BankSwitched VRAM surfaces.
    // ATTENTION: MULTIMON pdeDisplay is the primary; we should check
    // the hdcDevice instead
    if ((pddsd->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) &&
	(pdeDisplay->deFlags & (NON64KBANK|BANKEDVRAM|BANKEDSCAN)))
    {
	pde->deFlags |= (NON64KBANK|BANKEDVRAM|BANKEDSCAN);
    }
    else
    {
	pde->deFlags &= ~(NON64KBANK|BANKEDVRAM|BANKEDSCAN);
    }


    pde->deDeltaScan	= (DWORD)pddsd->lPitch;
    pde->deWidthBytes	= (WORD)pddsd->lPitch;

    // We use the selector we just allocated instead of the
    // flatsel + offset because it is a little safer if
    // something bad happens and someone goes off the end.
    pde->deBitsOffset	= 0;
    pde->deBitsSelector = (WORD)(p16Surface >> 16);

    pde->deBitmapInfo->bmiHeader.biXPelsPerMeter = pde->deReserved1;
    pde->deReserved1	= (DWORD)p16Surface;

    //
    // for a 8bit surface we want to color table to be the same as the
    // display (so it acts like a DDB not a DIB)
    //
    // For non-8bit surfaces; we don't need to do anything w.r.t. color table.
    //
    if (bpp == 8)
    {
        DWORD FAR *pdw;
	int i;
	RGBQUAD rgbT = {0,0,0,0};

	// Use our palette if it is explicitly set on the surface
	if (lpPalette)
	{
	    DPF( "Need a DC for an 8 bit surface with palette" );

	    Assert(pde->deBitmapInfo->bmiHeader.biBitCount == (DWORD)bpp);

	    // We use Pitch instead of Width because the "pitch" of
	    // dibsection is assumed to be the width rounded up to the next
	    // DWORD
            pde->deBitmapInfo->bmiHeader.biWidth = (DWORD)pddsd->lPitch;
	    pde->deBitmapInfo->bmiHeader.biHeight = -height; // negative height for top-down DIB
	    pde->deBitmapInfo->bmiHeader.biSizeImage = 0;
	    pde->deBitmapInfo->bmiHeader.biClrImportant = 256;

	    // We call this because it sets a magic number which
	    // has the effect of resetting any cached color translation
	    // tables that GDI may have set up for us.
	    SetDIBColorTable(hdc, 0, 1, &rgbT);

	    pdw = (DWORD FAR *)pde->deBitmapInfo;
	    pdw = (DWORD FAR *)((BYTE FAR *)pdw + pdw[0]);     // + biSize

	    for (i=0; i<256; i++)
		pdw[i] = RGB(lpPalette[i].peBlue,lpPalette[i].peGreen,lpPalette[i].peRed);
	}
	else
	{
	    DWORD FAR *pdwSrc;
	    DIBENGINE FAR *pdeDevice;
	    if (hdcDevice)
		pdeDevice = GetPDevice(hdcDevice);
	    else
		pdeDevice = pdeDisplay;

	    // This needs to be checked sooner.
	    Assert(pdeDevice && pdeDevice->deType == 0x5250);
	    Assert(pdeDevice->deBitsPixel == 8);
	    // In DX5, we will just modify our own bitmap info
	    // by copying the colors from the primary. In DX3, we
	    // pointed out bitmap info to the primary's; but that
	    // relies on the potentially bad assumption that our bitmap
	    // info will have a shorter life span to the primary's mode.
	    //
	    // It also doesn't work because the biWidth/biHeight fields
	    // of the device's primary don't match our own width/height
	    //

	    pdwSrc = (DWORD FAR *)(pdeDevice->deBitmapInfo);
	    pdwSrc = (DWORD FAR *)((BYTE FAR *)pdwSrc + pdwSrc[0]);	   // + biSize

	    pdw = (DWORD FAR *)pde->deBitmapInfo;
	    pdw = (DWORD FAR *)((BYTE FAR *)pdw + pdw[0]);	   // + biSize

	    // We call this because it sets a magic number which
	    // has the effect of resetting any cached color translation
	    // tables that GDI may have set up for us.
	    SetDIBColorTable(hdc, 0, 1, &rgbT);

	    // Copy all the colors to our color table
	    // We also clear all the special flags in our copy
	    for (i=0; i<256; i++)
		pdw[i] = (pdwSrc[i] & 0x00FFFFFF);

	    // Fixup the rest of the bitmapinfo

	    // We use Pitch instead of Width because the "pitch" of
	    // dibsection is assumed to be the width rounded up to the next
	    // DWORD
            pde->deBitmapInfo->bmiHeader.biWidth = (DWORD)pddsd->lPitch;
	    pde->deBitmapInfo->bmiHeader.biHeight = -height; // negative height for top-down DIB
	    pde->deBitmapInfo->bmiHeader.biSizeImage = 0;
	    pde->deBitmapInfo->bmiHeader.biClrImportant = 256;
	}
    }
    else
    {
	// We need to convert Pitch into the number of whole
	// pixels per scanline. There may be round-down errors
	// however, since GDI assumes that Pitches must be multiples
	// of 4; they round-up.
        DWORD pitch = (DWORD)pddsd->lPitch;
        if (bpp == 16)
            pitch = pitch / 2;
        else if (bpp == 24)
            pitch = pitch / 3;
        else if (bpp == 32)
            pitch = pitch / 4;
        else if (bpp == 4)
            pitch = pitch * 2;
        else if (bpp == 2)
            pitch = pitch * 4;
        else if (bpp == 1)
            pitch = pitch * 8;
        else
            Assert(0); // unexpected bpp

        pde->deBitmapInfo->bmiHeader.biWidth = pitch;
	pde->deBitmapInfo->bmiHeader.biHeight = -height; // negative height for top-down DIB
	pde->deBitmapInfo->bmiHeader.biSizeImage = 0;

	Assert(pde->deBitmapInfo->bmiHeader.biBitCount == (DWORD)bpp);
    }

    //
    // if the width/height of the dc has changed we need to set
    // a new vis region
    //
    if (width != (int)pde->deWidth || height != (int)pde->deHeight)
    {
        pde->deWidth  = width;
        pde->deHeight = height;

        SetRectRgn(hVisRgn, 0, 0, width, height);
        SelectVisRgn(hdc, hVisRgn);
    }

    //
    // when the bpp changes dont forget to fix up the deFlags
    // and ReSelect all the objects so they match the new bitdepth
    //
    if (pde->deBitsPixel != bpp || ((pde->deFlags ^ flags) & FIVE6FIVE))
    {
        if (flags & FIVE6FIVE)
            pde->deFlags |= FIVE6FIVE;
        else
            pde->deFlags &= ~FIVE6FIVE;

        pde->deBitsPixel = bpp;
        ReSelectObjects(hdc, 0);
    }

    DPF5("SetDC: Details of PDE returned.");
    DPF_PDE(pde);
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  AllocFlatSel
//
/////////////////////////////////////////////////////////////////////////////

#pragma optimize("", off)
UINT NEAR PASCAL AllocFlatSel()
{
    if (FlatSel != 0)
        return FlatSel;

    FlatSel = AllocSelector(SELECTOROF((LPVOID)&FlatSel));

    if (FlatSel == 0)
        return 0;

    SetSelectorBase(FlatSel, 0);

    // SetSelectorLimit(FlatSel, -1);
    _asm    mov     ax,0008h            ; DPMI set limit
    _asm    mov     bx,FlatSel
    _asm    mov     dx,-1
    _asm    mov     cx,-1
    _asm    int     31h

    return FlatSel;
}

BOOL DPMISetSelectorLimit(UINT selector, DWORD dwLimit)
{
    BOOL bRetVal=TRUE;

    // If the limit is >= 1MB, we need to make the limit a mulitple
    // of the page size or DPMISetSelectorLimit will fail.
    if( dwLimit >= 0x100000 )
        dwLimit |= 0x0FFF;

    __asm
    {
	mov  ax, 0008h
	mov  bx, selector
	mov  cx, word ptr [dwLimit+2]
	mov  dx, word ptr [dwLimit]
	int  31h
	jnc  success
	mov  bRetVal, FALSE
    success:
    }
    return bRetVal;
}
#pragma optimize("", on)

/////////////////////////////////////////////////////////////////////////////
//
//  InitDC
//
/////////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL InitDC(void)
{
    HDC hdc;
    UINT rc;
    DIBENGINE FAR *pde;

    if (pdeDisplay != NULL)
    {
        return TRUE;
    }

    //
    // get the PDevice of the display we are going to need to copy
    // some info
    //
    if (pdeDisplay == NULL)
    {
        hdc = GetDC(NULL);
        rc = GetDeviceCaps(hdc, CAPS1);
        pde = GetPDevice(hdc);
        ReleaseDC(NULL, hdc);

        if (!(rc & C1_DIBENGINE) ||
            IsBadReadPtr(pde, 2) || pde->deType != 0x5250 ||
            GetProfileInt("DirectDraw", "DisableGetDC", 0))
        {
	    DPF("DD16_GetDC: GetDC is disabled");
            return FALSE;
        }

        pdeDisplay = pde;
    }

    if (FlatSel == 0)
    {
        AllocFlatSel();
    }

    if (hVisRgn == NULL)
    {
        hVisRgn = CreateRectRgn(0,0,0,0);
        SetObjectOwner(hVisRgn, hInstApp);
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  MakeDC
//
/////////////////////////////////////////////////////////////////////////////

HDC NEAR PASCAL MakeDC(DWORD bpp, BOOL f565)
{
    HDC hdc;
    HBITMAP hbm;
    DIBENGINE FAR *pde;
    DIB8 BitmapInfo = {sizeof(BITMAPINFOHEADER), 1, -1, 1, 8, BI_RGB, 0, 0, 0, 0, 0};

    if (pdeDisplay == NULL)
	return NULL;

    hdc = GetDC(NULL);

    if (bpp == 8)
    {
	BitmapInfo.ct[0] = RGB(0,0,0);
	BitmapInfo.ct[255] = RGB(255, 255, 255);
    }
    else if (bpp == 16)
    {
        if (f565)
        {
            BitmapInfo.bi.biCompression = BI_BITFIELDS;
            BitmapInfo.ct[0] = 0xf800;
            BitmapInfo.ct[1] = 0x07e0;
            BitmapInfo.ct[2] = 0x001f;
        }
    }

    BitmapInfo.bi.biBitCount = (UINT)bpp;
    hbm = CreateDIBSection(hdc, (BITMAPINFO FAR *)&BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);

    ReleaseDC(NULL, hdc);

    if (hbm == NULL)
        return NULL;

    hdc = CreateCompatibleDC(NULL);
    SelectObject(hdc, hbm);

    pde = GetPDevice(hdc);

    if (IsBadReadPtr(pde, 2) || pde->deType != 0x5250)
    {
        DeleteDC(hdc);
        DeleteObject(hbm);
        return NULL;
    }

    //
    //  ok we have the following:
    //
    //      pde        --> DIBSECTION (DIBENGINE)
    //      pdeDisplay --> DISPLAY PDevice (DIBENGINE)
    //
    //  make the  DIBSECTION be compatible with the display
    //  set the following fields from the DISPLAY PDevice:
    //
    //      deBitsPixel
    //      deFlags (FIVE6FIVE, PALETTIZED, MINIDRIVER, ...)
    //      deBitmapInfo
    //

    pde->deBeginAccess      = 0;
    pde->deEndAccess        = 0;
    // deDriverReserved has three states
    // 0 - Do Not Cache a translation table
    // 1 - Translation table is same as Screen
    // >1 - Unique ID indicating state of palette (to indicate when cached translation table is out of date)
    //
    // For 24 and 32bpp, it never makes sense to cache a translation table
    // because no translation table is built for our surface as the destination.
    // Win95 Gold DIBEngine has a bug which screws up when doing 8-to-24/32 blts
    // because it incorrectly tries to cache the table. So we set deDriverReserved
    // to 0 for 24/32 bpp.
    //
    // We have been setting deDriverReserved to 1; but we probably should not
    // be doing this anymore; we should be leaving it alone which means
    // that it gets the unique number given to each dibsection.
    //
    if (bpp == 16 || bpp == 24 || bpp == 32)
	pde->deDriverReserved = 0;
    else
	pde->deDriverReserved = 1; // ID for the screen
    pde->deBitsPixel        = 0; // set SetDC will see it has changed

//  pde->deFlags  = pdeDisplay->deFlags;
//  pde->deFlags &= ~(VRAM|NOT_FRAMEBUFFER|NON64KBANK|BANKEDVRAM|BANKEDSCAN|PALETTE_XLAT);
//  pde->deFlags |= OFFSCREEN;
//  pde->deFlags |= MINIDRIVER; need to clear SELECTEDDIB

    // if the main display is banked, make the DCs banked because they
    //may be used for video memory
    //
    // ATTENTION we should only do this for video memory
    // surfaces not memory surfaces. move this code to SetDC
    // Also, make sure we set all if any banked bits are set in the driver
    // to encourage the DIBENG to avoid screen to screen blts (which are apparently buggy).
    //
    if(pdeDisplay->deFlags & (NON64KBANK|BANKEDVRAM|BANKEDSCAN))
    {
	pde->deFlags |= (NON64KBANK|BANKEDVRAM|BANKEDSCAN);
    }

    // This bit should only ever be used in conjunction with VRAM
    // setting it can confuses drivers (such as the 765) into thinking that
    // the surface is in VRAM when it is not.
    //    pde->deFlags |= OFFSCREEN;
    pde->deFlags |= BUSY;

    SetObjectOwner(hdc, hInstApp);
    SetObjectOwner(hbm, hInstApp);

    return hdc;
}

/////////////////////////////////////////////////////////////////////////////
//
//  FreeDC
//
/////////////////////////////////////////////////////////////////////////////

BOOL NEAR PASCAL FreeDC(HDC hdc)
{
    if (hdc)
    {
        HBITMAP hbm;
        hbm = SelectObject(hdc, StockBitmap());
        DeleteDC(hdc);
        DeleteObject(hbm);
    }
    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  DD16_MakeObjectPrivate
//	This function makes sure that no DC that we need is
//  freed until we want it to be freed.
//
/////////////////////////////////////////////////////////////////////////////

WORD DDAPI DD16_MakeObjectPrivate(HDC hdc, BOOL fPrivate)
{
    BOOL fState;

    // Assert that parameter is good
    Assert(IsGDIObject(hdc) == OBJ_DC);

    fState = MakeObjectPrivate(hdc, fPrivate);

    if (fState)
    {
	return 1;
    }
    else
    {
	return 0;
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  DD16_GetDC
//
/////////////////////////////////////////////////////////////////////////////

HDC DDAPI DD16_GetDC(HDC hdcDevice, LPDDSURFACEDESC pddsd, LPPALETTEENTRY lpPalette)
{
    HDC hdc;
    BOOL f565;
    // Assert that parameter is good
    Assert(IsGDIObject(hdcDevice) == OBJ_DC);

    // must be a RGB format surface!
    //
    if (!(pddsd->ddpfPixelFormat.dwFlags & DDPF_RGB))
    {
        DPF("DD16_GetDC: must be a RGB surface");
        return NULL;
    }

    //
    // if the surface is 8bpp the display must also be 8bpp because we
    // share the color table. (Multi-mon: make sure we check the right display.)
    //
    // If a palette is explicitly passed in, then we won't need
    // the device's pde.
    //
    if( pddsd->ddpfPixelFormat.dwRGBBitCount == 8 && lpPalette == NULL )
    {
	DIBENGINE FAR *pdeDevice;
	if( hdcDevice )
	    pdeDevice = GetPDevice( hdcDevice );
	else
	    pdeDevice = pdeDisplay;

	// 3DFx isn't a real device DC
	if (pdeDevice->deType != 0x5250)
	{
	    DPF("Can't get DC on an 8bpp surface without a palette for this device");
	    return NULL;
	}

	if (pdeDevice->deBitsPixel != 8 )
	{
	    DPF("Can't get DC on an 8bpp surface without a palette when primary is not at 8bpp");
	    return NULL;
	}

    }

#ifdef DEBUG
    //
    // we assume the pixel format is not wacky
    //
    if (pddsd->ddpfPixelFormat.dwRGBBitCount == 8 )
    {
        /*
         * The Permedia driver actually reports bit masks for their 8bit palettized mode, so
         * we shouldn't assert here (as we used to) if any masks are non-zero.
         */
        if ( ( pddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8) ==0 )
        {
            DPF("Getting a DC on a non-palettized 8bit surface!");
            Assert(0);
        }
    }
    else if (   pddsd->ddpfPixelFormat.dwRGBBitCount == 4 ||
                pddsd->ddpfPixelFormat.dwRGBBitCount == 1)
    {
        /*
         * Assume these are OK
         */
    }
    else if (pddsd->ddpfPixelFormat.dwRGBBitCount == 16)
    {
        if (pddsd->ddpfPixelFormat.dwRBitMask == 0xf800 &&
            pddsd->ddpfPixelFormat.dwGBitMask == 0x07e0 &&
            pddsd->ddpfPixelFormat.dwBBitMask == 0x001f)
        {
            // 565
        }
        else if (
            pddsd->ddpfPixelFormat.dwRBitMask == 0x7c00 &&
            pddsd->ddpfPixelFormat.dwGBitMask == 0x03e0 &&
            pddsd->ddpfPixelFormat.dwBBitMask == 0x001f)
        {
            // 555
        }
        else
        {
            DPF("DD16_GetDC: not 555 or 565");
            Assert(0);
        }
    }
    else if (pddsd->ddpfPixelFormat.dwRGBBitCount == 24 )
    {
        if (pddsd->ddpfPixelFormat.dwBBitMask == 0x0000FF &&
            pddsd->ddpfPixelFormat.dwGBitMask == 0x00FF00 &&
            pddsd->ddpfPixelFormat.dwRBitMask == 0xFF0000)
        {
            // 888 BGR
        }
        else
        {
            DPF("DD16_GetDC: invalid bit masks");
            Assert(0);
        }
    }
    else if(pddsd->ddpfPixelFormat.dwRGBBitCount == 32)
    {
	if (pddsd->ddpfPixelFormat.dwRBitMask == 0xFF0000 &&
		 pddsd->ddpfPixelFormat.dwGBitMask == 0x00FF00 &&
		 pddsd->ddpfPixelFormat.dwBBitMask == 0x0000FF)

        {
	    // 888 RGB -- standard 32-bit format
	}
        else
        {
            DPF("DD16_GetDC: invalid bit masks");
            Assert(0);
        }
    }
    else
    {
        DPF("DD16_GetDC: invalid bit depth");
        Assert(0);

    }
#endif

    // is this a 565?
    f565 = FALSE;
    if (pddsd->ddpfPixelFormat.dwRGBBitCount == 16 &&
            pddsd->ddpfPixelFormat.dwRBitMask == 0xf800)
        f565 = TRUE;

    //
    // use the cacheDC if it is free, else make a new one.
    //

    if( in_use || ( pddsd->ddsCaps.dwCaps & DDSCAPS_OWNDC ) )
    {
        hdc = MakeDC( pddsd->ddpfPixelFormat.dwRGBBitCount, f565 );
    }
    else
    {
        if (cacheBPP != pddsd->ddpfPixelFormat.dwRGBBitCount || bCache565 != f565 )
	{
	    FreeDC(hdcCache);
            hdcCache = MakeDC(pddsd->ddpfPixelFormat.dwRGBBitCount, f565);
	    cacheBPP = pddsd->ddpfPixelFormat.dwRGBBitCount;
            bCache565 = f565;
	}

        hdc = hdcCache;
        in_use++;
    }

    //
    // now set the right bits pointer.
    //
    if (hdc)
    {
	BOOL fSuccess;
	// Set the DC with the right information based
	// on the surface. If a palette is passed in
	// then set that palette into the DC.
	fSuccess = SetDC(hdc, hdcDevice, pddsd, lpPalette);

	if( !fSuccess )
	{
	    DPF("SetDC Failed");

	    // We need to clean up; but we
	    // can't call ReleaseDC because our dc is only
	    // half-cooked.
	    if (hdc == hdcCache)
	    {
		Assert(in_use == 1);
		in_use = 0;
	    }
	    else
	    {
		FreeDC(hdc);
	    }
	    return NULL;
	}
    }

    if (hdc && hdc == hdcCache)
    {
        save_level = SaveDC(hdc);
    }

    return hdc;
}

/////////////////////////////////////////////////////////////////////////////
//
// DD16_ReleaseDC
//
/////////////////////////////////////////////////////////////////////////////

void DDAPI DD16_ReleaseDC(HDC hdc)
{
    if (hdc == NULL)
        return;

    if (hdc == hdcCache)
    {
        RestoreDC(hdc, save_level);
	SetDC(hdc, NULL, NULL, NULL);
        Assert(in_use == 1);
        in_use = 0;
    }
    else
    {
	SetDC(hdc, NULL, NULL, NULL);
        FreeDC(hdc);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  DD16_SafeMode
//
//  dynamic safe mode
//
/////////////////////////////////////////////////////////////////////////////

BOOL DDAPI DD16_SafeMode(HDC hdc, BOOL fSafeMode)
{
    extern void PatchDisplay(int oem, BOOL patch);   // dynares.c

    int i;

    for (i=0; i<35; i++)
    {
        PatchDisplay(i, fSafeMode);
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
//
//  DD16_Exclude
//  DD16_Unexclude
//
//  call the exclude or unexclude callbacks in the display driver
//
/////////////////////////////////////////////////////////////////////////////

typedef void (FAR PASCAL *BEGINACCESSPROC)(LPVOID lpPDevice, int left, int top, int right, int bottom, WORD flags);
typedef void (FAR PASCAL *ENDACCESSPROC)(LPVOID lpPDevice, WORD flags);

void DDAPI DD16_Exclude(DWORD dwPDevice, RECTL FAR *prcl)
{
    DIBENGINE FAR *pde = (DIBENGINE FAR *)dwPDevice;

    Assert(pde && pde->deType == 0x5250);
    Assert(prcl != NULL);
    Assert(pde->deFlags & BUSY);

    if (pde->deBeginAccess)
    {
        BEGINACCESSPROC OEMBeginAccess = (BEGINACCESSPROC)pde->deBeginAccess;

        //
        //  when DirectDraw calls us it has already taken the BUSY bit
        //  but BUSY needs to be clear for the cursor to be excluded.
        //  so release the BUSY bit while we call the driver, this is
        //  a ok thing to do because we have the Win16Lock.
        //
        pde->deFlags &= ~BUSY;
        OEMBeginAccess(pde, (int)prcl->left, (int)prcl->top,
            (int)prcl->right, (int)prcl->bottom, CURSOREXCLUDE);
        pde->deFlags |= BUSY;
    }
}

void DDAPI DD16_Unexclude(DWORD dwPDevice)
{
    DIBENGINE FAR *pde = (DIBENGINE FAR *)dwPDevice;

    Assert(pde && pde->deType == 0x5250);

    if (pde->deEndAccess)
    {
        ENDACCESSPROC OEMEndAccess = (ENDACCESSPROC)pde->deEndAccess;
        OEMEndAccess(pde, CURSOREXCLUDE);
    }
}

/*
 * DD16_AttemptGamma
 *
 * Total HACK!  The GetDeviceGammaRamp call can attempt to call a NULL
 * entry.  Since we can't fix Win95, instead we look at the entry that
 * it will call and suggest that they don't call it if it's NULL.
 */
BOOL DDAPI DD16_AttemptGamma( HDC hdc )
{
    WORD wLDevice;
    WORD FAR *pw;
    UINT hGDI = GetGdiDS();

    wLDevice = GetW(hdc, 0x34);
    if( wLDevice != 0 )
    {
        pw = MAKELP(hGDI, wLDevice);
        if (!IsBadReadPtr(pw, 0x80))
        {
            pw = MAKELP(hGDI, wLDevice + 0x7C);
            if (*pw != NULL)
            {
                return TRUE;
            }
        }
    }
    return FALSE;

} /* DD16_AttemptGamma */

/*
 * DD16_IsDeviceBusy
 *
 * Determines if the device represented by the HDC is
 * busy or not.
 */
BOOL DDAPI DD16_IsDeviceBusy( HDC hdc )
{
    DIBENGINE FAR *pde;

    pde = GetPDevice(hdc);
    if(pde == NULL)
        return FALSE;

    Assert(pde->deType==0x5250);
    return pde->deFlags & BUSY;
} /* DD16_IsDeviceBusy */

/////////////////////////////////////////////////////////////////////////////
//
//  DD16_Stretch
//
//  call the DIBENG to do a stretch.
//
/////////////////////////////////////////////////////////////////////////////

extern int FAR PASCAL DIB_Stretch(
    DIBENGINE FAR *dst, int, int, int, int,
    DIBENGINE FAR *src, int, int, int, int,
    DWORD Rop, LPVOID lpPBrush, LPVOID lpDrawMode, LPRECT lpClip);

extern int FAR PASCAL DIB_BitBlt(
    DIBENGINE FAR *dst, int xD, int yD,
    DIBENGINE FAR *src, int xS, int yS, int w, int h,
    DWORD Rop, LPVOID lpPBrush, LPVOID lpDrawMode);

typedef struct {
    short int	  Rop2;
    short int	  bkMode;
    unsigned long int bkColor;
    unsigned long int TextColor;
    short int	  TBreakExtra;
    short int	  BreakExtra;
    short int	  BreakErr;
    short int	  BreakRem;
    short int	  BreakCount;
    short int	  CharExtra;

    unsigned long int LbkColor;
    unsigned long int LTextColor;
    DWORD		  ICMCXform;
    short		  StretchBltMode;
    DWORD		  eMiterLimit;
} DRAWMODE;

int DDAPI DD16_Stretch(DWORD DstPtr, int DstPitch, UINT DstBPP, int DstX, int DstY, int DstDX, int DstDY,
                       DWORD SrcPtr, int SrcPitch, UINT SrcBPP, int SrcX, int SrcY, int SrcDX, int SrcDY)//, long Rop3)

{
    DIBENGINE   src;
    DIBENGINE	dst;
    DRAWMODE    dm;
    RECT        rc;
    static DIB8	bmiStretch = {sizeof(BITMAPINFOHEADER), 1, -1, 1, 8, BI_RGB, 0, 0, 0, 0, 0};

    //
    //	make sure we have a flat sel
    //
    if (FlatSel == 0)
        return -1;

    // Set the bitdepth on the bitmapinfo
    Assert( DstBPP == SrcBPP );
    bmiStretch.bi.biBitCount = DstBPP;

    //
    //	setup source DIBENG
    //
    if (SrcPtr)
    {
        src.deType          = TYPE_DIBENG;
        src.deWidth         = 10000;
        src.deHeight        = 10000;
        src.deWidthBytes    = SrcPitch;
        src.dePlanes        = 1;
        src.deBitsPixel     = SrcBPP;
        src.deReserved1     = 0;
        src.deDeltaScan     = SrcPitch;
        src.delpPDevice     = NULL;
        src.deBitsOffset    = SrcPtr;
        src.deBitsSelector  = FlatSel;
        src.deFlags         = SELECTEDDIB;
        src.deVersion       = VER_DIBENG;
        src.deBitmapInfo    = (BITMAPINFO *)&bmiStretch;
        src.deBeginAccess   = 0;
        src.deEndAccess     = 0;
        src.deDriverReserved= 0;
    }

    //
    //	setup dest DIBENG
    //
    dst.deType		 = TYPE_DIBENG;
    dst.deWidth          = 10000;
    dst.deHeight         = 10000;
    dst.deWidthBytes	 = DstPitch;
    dst.dePlanes	 = 1;
    dst.deBitsPixel	 = DstBPP;
    dst.deReserved1	 = 0;
    dst.deDeltaScan	 = DstPitch;
    dst.delpPDevice	 = NULL;
    dst.deBitsOffset	 = DstPtr;
    dst.deBitsSelector	 = FlatSel;
    dst.deFlags 	 = SELECTEDDIB;
    dst.deVersion	 = VER_DIBENG;
    dst.deBitmapInfo     = (BITMAPINFO *)&bmiStretch;
    dst.deBeginAccess	 = 0;
    dst.deEndAccess	 = 0;
    dst.deDriverReserved = 0;


    //
    //  this memory *might* be in VRAM so setup things to
    //  work right.
    //
    //  ATTENTION we should only do this for video memory
    //  surfaces not memory surfaces.
    //  If any are set, set all the bits to force the DIBENG to
    //  not do a screen to screen blit (which apparently has a bug).
    //
    if (pdeDisplay && (pdeDisplay->deFlags & (NON64KBANK|BANKEDVRAM|BANKEDSCAN)))
    {
        dst.deFlags |= (NON64KBANK|BANKEDVRAM|BANKEDSCAN);
        src.deFlags |= (NON64KBANK|BANKEDVRAM|BANKEDSCAN);
    }

    //
    //	now call the DIBENG
    //

    if(SrcPtr == (DWORD)NULL)
    {
        DPF("Blitting from Primary with HDC unsupported!");
        return FALSE;
    }
    else if ((DstDX == SrcDX) && (DstDY == SrcDY))
    {
	    //DPF("Calling DIB_BitBlt");
	    // NOTE: If the source and destination video memory pointers
	    // are the same then we simply pass the destination
	    // DIBENG for the source as this is how the blt code spots
	    // the fact that the source and destination surfaces are
	    // the same and so takes the necessary action to handle
	    // overlapping surfaces
	    #ifdef DEBUG
	    	if( DstPtr == SrcPtr)
		{
		    Assert(DstPitch == SrcPitch);
		    Assert(DstBPP   == SrcBPP);
		}
	    #endif
	    return DIB_BitBlt(&dst, DstX, DstY,
			      (DstPtr == SrcPtr) ? &dst : &src,
			      SrcX, SrcY, SrcDX, SrcDY, SRCCOPY, // Rop3,
			      NULL, &dm);
    }
    else
    {
        rc.left = DstX;
	    rc.top = DstY;
	    rc.right = DstX + DstDX;
	    rc.bottom = DstY + DstDY;

	    dm.StretchBltMode = STRETCH_DELETESCANS;

/*        DPF("Calling DIB_StretchBlt with:");
        DPF("\tdst.deType = 0x%x(%s)",dst.deType,(dst.deType == TYPE_DIBENG ? "TYPE_DIBENG" : "**UNKNOWN**"));
        DPF("\tdst.deWidth = %d",dst.deWidth);
        DPF("\tdst.deHeight = %d",dst.deHeight);
        DPF("\tdst.deWidthBytes = %d",dst.deWidthBytes);
        DPF("\tdst.dePlanes = %d",dst.dePlanes);
        DPF("\tdst.deBitsPixel = %d",dst.deBitsPixel);
        DPF("\tdst.deReserved1 = %ld",dst.deReserved1);
        DPF("\tdst.deDeltaScan = %ld",dst.deDeltaScan);
        DPF("\tdst.delpPDevice = 0x%x",dst.delpPDevice);
        DPF("\tdst.deBitsOffset = 0x%x",dst.deBitsOffset);
        DPF("\tdst.deBitsSelector = 0x%x",dst.deBitsSelector);
        DPF("\tdst.deFlags = 0x%x(%s)",dst.deFlags,(dst.deFlags == SELECTEDDIB ? "SELECTEDDIB" : "**UNKNOWN**"));
        DPF("\tdst.deVersion = %d(%s)",dst.deVersion,(dst.deVersion == VER_DIBENG ? "VER_DIBENG" : "**UNKNOWN**"));

        DPF("\t\tdst.deBitmapInfo->bmiHeader.biSize = %ld",dst.deBitmapInfo->bmiHeader.biSize);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biWidth = %ld",dst.deBitmapInfo->bmiHeader.biWidth);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biHeight = %ld",dst.deBitmapInfo->bmiHeader.biHeight);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biPlanes = %d",dst.deBitmapInfo->bmiHeader.biPlanes);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biBitCount = %d",dst.deBitmapInfo->bmiHeader.biBitCount);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biCompression = 0x%x(%s)",dst.deBitmapInfo->bmiHeader.biCompression,((dst.deBitmapInfo->bmiHeader.biCompression == BI_RGB) ? "BI_RGB" : "**UNKNOWN**"));
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biSizeImage = %ld",dst.deBitmapInfo->bmiHeader.biSizeImage);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biXPelsPerMeter = %ld",dst.deBitmapInfo->bmiHeader.biXPelsPerMeter);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biYPelsPerMeter = %ld",dst.deBitmapInfo->bmiHeader.biYPelsPerMeter);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biClrUsed = %ld",dst.deBitmapInfo->bmiHeader.biClrUsed);
        DPF("\t\tdst.deBitmapInfo->bmiHeader.biClrImportant = %ld",dst.deBitmapInfo->bmiHeader.biClrImportant);

        DPF("\tdst.deBeginAccess = 0x%x",dst.deBeginAccess);
        DPF("\tdst.deEndAccess = 0x%x",dst.deEndAccess);
        DPF("\tdst.deDriverReserved = 0x%x",dst.deDriverReserved);

        DPF("");
        DPF("\tDstX  = %d",DstX);
        DPF("\tDstY  = %d",DstY);
        DPF("\tDstDX = %d",DstDX);
        DPF("\tDstDY = %d",DstDY);

        DPF("");

        DPF("\tsrc.deType = 0x%x(%s)",src.deType,(src.deType == TYPE_DIBENG ? "TYPE_DIBENG" : "**UNKNOWN**"));
        DPF("\tsrc.deWidth = %d",src.deWidth);
        DPF("\tsrc.deHeight = %d",src.deHeight);
        DPF("\tsrc.deWidthBytes = %d",src.deWidthBytes);
        DPF("\tsrc.dePlanes = %d",src.dePlanes);
        DPF("\tsrc.deBitsPixel = %d",src.deBitsPixel);
        DPF("\tsrc.deReserved1 = %ld",src.deReserved1);
        DPF("\tsrc.deDeltaScan = %ld",src.deDeltaScan);
        DPF("\tsrc.delpPDevice = 0x%x",src.delpPDevice);
        DPF("\tsrc.deBitsOffset = 0x%x",src.deBitsOffset);
        DPF("\tsrc.deBitsSelector = 0x%x",src.deBitsSelector);
        DPF("\tsrc.deFlags = 0x%x(%s)",src.deFlags,(src.deFlags == SELECTEDDIB ? "SELECTEDDIB" : "**UNKNOWN**"));
        DPF("\tsrc.deVersion = %d(%s)",src.deVersion,(src.deVersion == VER_DIBENG ? "VER_DIBENG" : "**UNKNOWN**"));

        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biSize = %ld",src.deBitmapInfo->bmiHeader.biSize);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biWidth = %ld",src.deBitmapInfo->bmiHeader.biWidth);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biHeight = %ld",src.deBitmapInfo->bmiHeader.biHeight);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biPlanes = %d",src.deBitmapInfo->bmiHeader.biPlanes);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biBitCount = %d",src.deBitmapInfo->bmiHeader.biBitCount);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biCompression = 0x%x(%s)",src.deBitmapInfo->bmiHeader.biCompression,((src.deBitmapInfo->bmiHeader.biCompression == BI_RGB) ? "BI_RGB" : "**UNKNOWN**"));
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biSizeImage = %ld",src.deBitmapInfo->bmiHeader.biSizeImage);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biXPelsPerMeter = %ld",src.deBitmapInfo->bmiHeader.biXPelsPerMeter);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biYPelsPerMeter = %ld",src.deBitmapInfo->bmiHeader.biYPelsPerMeter);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biClrUsed = %ld",src.deBitmapInfo->bmiHeader.biClrUsed);
        DPF("\t\tsrc.deBitmapInfo->bmiHeader.biClrImportant = %ld",src.deBitmapInfo->bmiHeader.biClrImportant);

        DPF("\tsrc.deBeginAccess = 0x%x",src.deBeginAccess);
        DPF("\tsrc.deEndAccess = 0x%x",src.deEndAccess);
        DPF("\tsrc.deDriverReserved = 0x%x",src.deDriverReserved);

        DPF("");
        DPF("\tSrcX  = %d",SrcX);
        DPF("\tSrcY  = %d",SrcY);
        DPF("\tSrcDX = %d",SrcDX);
        DPF("\tSrcDY = %d",SrcDY);

        DPF("");

        DPF("\tdm.StretchBltMode = STRETCH_DELETESCANS");

        DPF("");

        DPF("\trc.left  = %d",rc.left);
        DPF("\trc.top  = %d",rc.top);
        DPF("\trc.right = %d",rc.right);
        DPF("\trc.bottom = %d",rc.bottom);

        DPF("");
*/

        return DIB_Stretch(&dst, DstX, DstY, DstDX, DstDY,
						    &src, SrcX, SrcY, SrcDX, SrcDY, SRCCOPY, // Rop3,
						    NULL, &dm, &rc);
    }
}



/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

void GdiHelpCleanUp()
{
    if (FlatSel)
    {
        SetSelectorLimit(FlatSel, 0);
        FreeSelector(FlatSel);
        FlatSel = 0;
    }

    if (hdcCache)
    {
        FreeDC(hdcCache);
        hdcCache = NULL;
    }

    if (hVisRgn)
    {
        DeleteObject(hVisRgn);
        hVisRgn = NULL;
    }

    if (pdeDisplay)
    {
        pdeDisplay = NULL;
    }
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

BOOL GdiHelpInit()
{
    InitDC();
    return FlatSel!=NULL && pdeDisplay!=NULL;
}

#endif // DirectDraw