wined3d: Implement IWineD3DSurface::GetDC and IWineD3DSurface::ReleaseDC.
diff --git a/dlls/wined3d/surface.c b/dlls/wined3d/surface.c
index 9bb0916..bb5d070 100644
--- a/dlls/wined3d/surface.c
+++ b/dlls/wined3d/surface.c
@@ -1,6 +1,7 @@
 /*
  * IWineD3DSurface Implementation
  *
+ * Copyright 2000-2001 TransGaming Technologies Inc.
  * Copyright 2002-2005 Jason Edmeades
  * Copyright 2002-2003 Raphael Junqueira
  * Copyright 2004 Christian Costa
@@ -70,6 +71,17 @@
             glDeleteTextures(1, &This->glDescription.textureName);
             LEAVE_GL();
         }
+
+        if(This->Flags & SFLAG_DIBSECTION) {
+            /* Release the DC */
+            SelectObject(This->hDC, This->dib.holdbitmap);
+            DeleteDC(This->hDC);
+            /* Release the DIB section */
+            DeleteObject(This->dib.DIBsection);
+            This->dib.bitmap_data = NULL;
+            This->resource.allocatedMemory = NULL;
+        }
+
         IWineD3DResourceImpl_CleanUp((IWineD3DResource *)iface);
 
         TRACE("(%p) Released\n", This);
@@ -344,12 +356,15 @@
         These resources may be POOL_SYSTEMMEM, so they must not access the device */
         TRACE("locking an ordinarary surface\n");
         /* Check to see if memory has already been allocated from the surface*/
-        if (NULL == This->resource.allocatedMemory) { /* TODO: check to see if an update has been performed on the surface (an update could just clobber allocatedMemory */
+        if ((NULL == This->resource.allocatedMemory) ||
+            (This->Flags & SFLAG_NEWDC) ){ /* TODO: check to see if an update has been performed on the surface (an update could just clobber allocatedMemory */
             /* Non-system memory surfaces */
 
             /*Surface has no memory currently allocated to it!*/
             TRACE("(%p) Locking rect\n" , This);
-            This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->pow2Size);
+            if(!This->resource.allocatedMemory) {
+                This->resource.allocatedMemory = HeapAlloc(GetProcessHeap() ,0 , This->pow2Size);
+            }
             if (0 != This->glDescription.textureName) {
                 /* Now I have to copy thing bits back */
                 This->Flags |= SFLAG_ACTIVELOCK; /* When this flag is set to true, loading the surface again won't free THis->resource.allocatedMemory */
@@ -704,6 +719,22 @@
             GLint  prev_draw;
             GLint  prev_rasterpos[4];
 
+            /* Some drivers(radeon dri, others?) don't like exceptions during
+             * glDrawPixels. If the surface is a DIB section, it might be in GDIMode
+             * after ReleaseDC. Reading it will cause an exception, which x11drv will
+             * catch to put the dib section in InSync mode, which leads to a crash
+             * and a blocked x server on my radeon card.
+             *
+             * The following lines read the dib section so it is put in inSync mode
+             * before glDrawPixels is called and the crash is prevented. There won't
+             * be any interfering gdi accesses, because UnlockRect is called from
+             * ReleaseDC, and the app won't use the dc any more afterwards.
+             */
+            if(This->Flags & SFLAG_DIBSECTION) {
+                volatile BYTE read;
+                read = This->resource.allocatedMemory[0];
+            }
+
             ENTER_GL();
 
             glFlush();
@@ -906,14 +937,216 @@
 
 HRESULT WINAPI IWineD3DSurfaceImpl_GetDC(IWineD3DSurface *iface, HDC *pHDC) {
     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
-    FIXME("No support for GetDC yet for surface %p\n", This);
-    return WINED3DERR_INVALIDCALL;
+    WINED3DLOCKED_RECT lock;
+    UINT usage;
+    BITMAPINFO* b_info;
+    HDC ddc;
+    DWORD *masks;
+    HRESULT hr;
+
+    TRACE("(%p)->(%p)\n",This,pHDC);
+
+    /* Give more detailed info for ddraw */
+    if (This->Flags & SFLAG_DCINUSE)
+        return DDERR_DCALREADYCREATED;
+
+    /* Can't GetDC if the surface is locked */
+    if (This->Flags & SFLAG_LOCKED)
+        return WINED3DERR_INVALIDCALL;
+
+    memset(&lock, 0, sizeof(lock)); /* To be sure */
+
+    /* Create a DIB section if there isn't a hdc yet */
+    if(!This->hDC)
+    {
+        RGBQUAD col[256];
+
+        if(This->Flags & SFLAG_ACTIVELOCK)
+        {
+            ERR("Creating a DIB section while a lock is active. Uncertain consequences\n");
+        }
+
+        switch (This->bytesPerPixel)
+        {
+            case 2:
+            case 4:
+                /* Allocate extra space to store the RGB bit masks. */
+                b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD));
+                break;
+
+            case 3:
+                b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BITMAPINFOHEADER));
+                break;
+
+            default:
+                /* Allocate extra space for a palette. */
+                b_info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+                                  sizeof(BITMAPINFOHEADER)
+                                  + sizeof(RGBQUAD)
+                                  * (1 << (This->bytesPerPixel * 8)));
+                break;
+        }
+
+        b_info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+        b_info->bmiHeader.biWidth = This->pow2Width;
+        b_info->bmiHeader.biHeight = -This->pow2Height;
+        b_info->bmiHeader.biPlanes = 1;
+        b_info->bmiHeader.biBitCount = This->bytesPerPixel * 8;
+
+        b_info->bmiHeader.biSizeImage = This->resource.size;
+
+        b_info->bmiHeader.biXPelsPerMeter = 0;
+        b_info->bmiHeader.biYPelsPerMeter = 0;
+        b_info->bmiHeader.biClrUsed = 0;
+        b_info->bmiHeader.biClrImportant = 0;
+
+        /* Get the bit masks */
+        masks = (DWORD *) &(b_info->bmiColors);
+        switch (This->resource.format)
+        {
+            case WINED3DFMT_R8G8B8:
+                usage = DIB_RGB_COLORS;
+                b_info->bmiHeader.biCompression = BI_RGB;
+                break;
+
+            case WINED3DFMT_X1R5G5B5:
+            case WINED3DFMT_A1R5G5B5:
+            case WINED3DFMT_A4R4G4B4:
+            case WINED3DFMT_X4R4G4B4:
+            case WINED3DFMT_R3G3B2:
+            case WINED3DFMT_A8R3G3B2:
+            case WINED3DFMT_A2B10G10R10:
+            case WINED3DFMT_A8B8G8R8:
+            case WINED3DFMT_X8B8G8R8:
+            case WINED3DFMT_A2R10G10B10:
+            case WINED3DFMT_R5G6B5:
+            case WINED3DFMT_A16B16G16R16:
+                usage = 0;
+                b_info->bmiHeader.biCompression = BI_BITFIELDS;
+                masks[0] = get_bitmask_red(This->resource.format);
+                masks[1] = get_bitmask_green(This->resource.format);
+                masks[2] = get_bitmask_blue(This->resource.format);
+                break;
+
+            default:
+                /* Don't know palette */
+                b_info->bmiHeader.biCompression = BI_RGB;
+                usage = 0;
+                break;
+        }
+
+        ddc = CreateDCA("DISPLAY", NULL, NULL, NULL);
+        if (ddc == 0)
+        {
+            HeapFree(GetProcessHeap(), 0, b_info);
+            return HRESULT_FROM_WIN32(GetLastError());
+        }
+
+        TRACE("Creating a DIB section with size %ldx%ldx%d, size=%ld\n", b_info->bmiHeader.biWidth, b_info->bmiHeader.biHeight, b_info->bmiHeader.biBitCount, b_info->bmiHeader.biSizeImage);
+        This->dib.DIBsection = CreateDIBSection(ddc, b_info, usage, &This->dib.bitmap_data, 0 /* Handle */, 0 /* Offset */);
+        DeleteDC(ddc);
+
+        if (!This->dib.DIBsection)
+        {
+            ERR("CreateDIBSection failed!\n");
+            return HRESULT_FROM_WIN32(GetLastError());
+        }
+        HeapFree(GetProcessHeap(), 0, b_info);
+
+        TRACE("DIBSection at : %p\n", This->dib.bitmap_data);
+
+        /* copy the existing surface to the dib section */
+        if(This->resource.allocatedMemory)
+        {
+            memcpy(This->dib.bitmap_data, This->resource.allocatedMemory, This->resource.size);
+            /* We won't need that any more */
+            HeapFree(GetProcessHeap(), 0, This->resource.allocatedMemory);
+        }
+
+        /* Use the dib section from now on */
+        This->resource.allocatedMemory = This->dib.bitmap_data;
+
+        /* Now allocate a HDC */
+        This->hDC = CreateCompatibleDC(0);
+        This->dib.holdbitmap = SelectObject(This->hDC, This->dib.DIBsection);
+        TRACE("using wined3d palette %p\n", This->palette);
+        SelectPalette(This->hDC,
+                      This->palette ? This->palette->hpal : 0,
+                      FALSE);
+
+        if(This->resource.format == WINED3DFMT_P8 ||
+           This->resource.format == WINED3DFMT_A8P8)
+        {
+            unsigned int n;
+            if(This->palette)
+            {
+                PALETTEENTRY ent[256];
+
+                GetPaletteEntries(This->palette->hpal, 0, 256, ent);
+                for (n=0; n<256; n++)
+                {
+                    col[n].rgbRed   = ent[n].peRed;
+                    col[n].rgbGreen = ent[n].peGreen;
+                    col[n].rgbBlue  = ent[n].peBlue;
+                    col[n].rgbReserved = 0;
+                }
+            }
+            else
+            {
+                IWineD3DDeviceImpl *device = This->resource.wineD3DDevice;
+
+                for (n=0; n<256; n++)
+                {
+                    col[n].rgbRed   = device->palettes[device->currentPalette][n].peRed;
+                    col[n].rgbGreen = device->palettes[device->currentPalette][n].peGreen;
+                    col[n].rgbBlue  = device->palettes[device->currentPalette][n].peBlue;
+                    col[n].rgbReserved = 0;
+                }
+
+            }
+            SetDIBColorTable(This->hDC, 0, 256, col);
+        }
+
+        /* This is to make LockRect read the gl Texture although memory is allocated */
+        This->Flags |= SFLAG_NEWDC;
+
+        This->Flags |= SFLAG_DIBSECTION;
+    }
+
+    /* Lock the surface */
+    hr = IWineD3DSurface_LockRect(iface,
+                                  &lock,
+                                  NULL,
+                                  0);
+    This->Flags &= ~SFLAG_NEWDC;
+    if(FAILED(hr))
+    {
+        ERR("IWineD3DSurface_LockRect failed with hr = %08lx\n", hr);
+        /* keep the dib section */
+        return hr;
+    }
+
+    *pHDC = This->hDC;
+    TRACE("returning %p\n",*pHDC);
+    This->Flags |= SFLAG_DCINUSE;
+
+    return WINED3D_OK;
 }
 
 HRESULT WINAPI IWineD3DSurfaceImpl_ReleaseDC(IWineD3DSurface *iface, HDC hDC) {
     IWineD3DSurfaceImpl *This = (IWineD3DSurfaceImpl *)iface;
-    FIXME("No support for ReleaseDC yet for surface %p\n", This);
-    return WINED3DERR_INVALIDCALL;
+
+    TRACE("(%p)->(%p)\n",This,hDC);
+
+    if (!(This->Flags & SFLAG_DCINUSE))
+        return D3DERR_INVALIDCALL;
+
+    /* we locked first, so unlock now */
+    IWineD3DSurface_UnlockRect(iface);
+
+    This->Flags &= ~SFLAG_DCINUSE;
+
+    return WINED3D_OK;
 }
 
 /* ******************************************************
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index ca76a93..e419c7c 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -2061,3 +2061,184 @@
         default: return WINED3DFMT_UNKNOWN;
     }
 }
+
+LONG get_bitmask_red(WINED3DFORMAT fmt)
+{
+    switch (fmt) {
+      case WINED3DFMT_R8G8B8:
+      case WINED3DFMT_A8R8G8B8:
+      case WINED3DFMT_X8R8G8B8:
+          return 0x00ff0000;
+
+      case WINED3DFMT_X1R5G5B5:
+      case WINED3DFMT_A1R5G5B5:
+          return 0x7C00;
+
+      case WINED3DFMT_A4R4G4B4:
+      case WINED3DFMT_X4R4G4B4:
+          return 0xF00;
+
+      case WINED3DFMT_R3G3B2:
+      case WINED3DFMT_A8R3G3B2:
+          return 0xE0;
+
+      case WINED3DFMT_A2R10G10B10:
+          return 0x3F0000;
+          break;
+
+      case WINED3DFMT_A2B10G10R10:
+          return 0x3FF;
+
+      case WINED3DFMT_A8B8G8R8:
+      case WINED3DFMT_X8B8G8R8:
+          return 0xff;
+
+      case WINED3DFMT_R5G6B5:
+          return 0xF800;
+
+      case WINED3DFMT_P8:
+          /* No fixed mask for this format */
+          return 0;
+
+#if 0
+      case WINED3DFMT_A16B16G16R16:
+          return 0x00000000ffff;
+          break;
+#endif
+
+      default:
+          ERR("Unknown bitmask for format %d\n", fmt);
+          return 0;
+    }
+}
+
+LONG get_bitmask_green(WINED3DFORMAT fmt)
+{
+    switch (fmt) {
+      case WINED3DFMT_R8G8B8:
+      case WINED3DFMT_A8R8G8B8:
+      case WINED3DFMT_X8R8G8B8:
+          return 0x0000ff00;
+
+      case WINED3DFMT_X1R5G5B5:
+      case WINED3DFMT_A1R5G5B5:
+          return 0x3E0;
+
+      case WINED3DFMT_A4R4G4B4:
+      case WINED3DFMT_X4R4G4B4:
+          return 0xF0;
+
+      case WINED3DFMT_R3G3B2:
+      case WINED3DFMT_A8R3G3B2:
+          return 0x1C;
+
+      case WINED3DFMT_A2B10G10R10:
+          return 0xFFC00;
+
+      case WINED3DFMT_A8B8G8R8:
+      case WINED3DFMT_X8B8G8R8:
+          return 0xFF00;
+          break;
+
+      case WINED3DFMT_A2R10G10B10:
+          return 0xFFC00;
+          break;
+
+      case WINED3DFMT_R5G6B5:
+          return 0x7E0;
+
+      case WINED3DFMT_P8:
+          /* No fixed mask for this format */
+          return 0;
+
+#if 0
+      case WINED3DFMT_A16B16G16R16:
+          return 0x0000ffff0000;
+          break;
+#endif
+
+      default:
+          ERR("Unknown bitmask for format %d\n", fmt);
+          return 0;
+    }
+}
+
+LONG get_bitmask_blue(WINED3DFORMAT fmt)
+{
+    switch (fmt) {
+      case WINED3DFMT_R8G8B8:
+      case WINED3DFMT_A8R8G8B8:
+      case WINED3DFMT_X8R8G8B8:
+          return 0x000000ff;
+
+      case WINED3DFMT_X1R5G5B5:
+      case WINED3DFMT_A1R5G5B5:
+          return 0x1f;
+
+      case WINED3DFMT_A4R4G4B4:
+      case WINED3DFMT_X4R4G4B4:
+          return 0xF;
+
+      case WINED3DFMT_R3G3B2:
+      case WINED3DFMT_A8R3G3B2:
+          return 0x3;
+
+      case WINED3DFMT_A2B10G10R10:
+          return 0x3F0000;
+
+      case WINED3DFMT_A8B8G8R8:
+      case WINED3DFMT_X8B8G8R8:
+          return 0xFF0000;
+
+      case WINED3DFMT_A2R10G10B10:
+          return 0x3FF;
+
+      case WINED3DFMT_R5G6B5:
+          return 0x1F;
+
+      case WINED3DFMT_P8:
+          /* No fixed mask for this format */
+          return 0;
+
+#if 0
+      case WINED3DFMT_A16B16G16R16:
+          return 0xffff00000000;
+          break;
+#endif
+
+      default:
+          ERR("Unknown bitmask for format %d\n", fmt);
+          return 0;
+
+    }
+}
+
+LONG get_bitmask_alpha(WINED3DFORMAT fmt)
+{
+    switch (fmt) {
+      case WINED3DFMT_A8R8G8B8:
+          return 0xff000000;
+
+      case WINED3DFMT_A1R5G5B5:
+          return 0x8000;
+
+      case WINED3DFMT_A4R4G4B4:
+          return 0xF000;
+
+      case WINED3DFMT_A8R3G3B2:
+          return 0xff00;
+
+      case WINED3DFMT_A2B10G10R10:
+          return 0xb0000000;
+
+      case WINED3DFMT_A8B8G8R8:
+          return 0xFF000000;
+
+      case WINED3DFMT_A2R10G10B10:
+          return 0xb0000000;
+
+      default:
+          return 0;
+
+    }
+}
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 0150820..3997b9c 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -786,6 +786,16 @@
 } WINED3DSURFACET_DESC;
 
 /*****************************************************************************
+ * Structure for DIB Surfaces (GetDC and GDI surfaces)
+ */
+typedef struct wineD3DSurface_DIB {
+    HBITMAP DIBsection;
+    void* bitmap_data;
+    HGDIOBJ holdbitmap;
+    BOOL client_memory;
+} wineD3DSurface_DIB;
+
+/*****************************************************************************
  * IWineD3DSurface implementation structure
  */
 struct IWineD3DSurfaceImpl
@@ -819,6 +829,10 @@
     RECT                      dirtyRect;
 
     glDescriptor              glDescription;
+
+    /* For GetDC */
+    wineD3DSurface_DIB        dib;
+    HDC                       hDC;
 };
 
 extern const IWineD3DSurfaceVtbl IWineD3DSurface_Vtbl;
@@ -837,6 +851,8 @@
 #define SFLAG_NONPOW2     0x0400 /* Surface sizes are not a power of 2 */
 #define SFLAG_DYNLOCK     0x0800 /* Surface is often locked by the app */
 #define SFLAG_DYNCHANGE   0x1800 /* Surface contents are changed very often, implies DYNLOCK */
+#define SFLAG_DCINUSE     0x2000 /* Set between GetDC and ReleaseDC calls */
+#define SFLAG_NEWDC       0x4000 /* To inform LockRect about a new dc */
 
 /* In some conditions the surface memory must not be freed:
  * SFLAG_OVERSIZE: Not all data can be kept in GL
@@ -1299,5 +1315,9 @@
 
 /* DirectDraw utility functions */
 extern WINED3DFORMAT pixelformat_for_depth(DWORD depth);
+LONG get_bitmask_red(WINED3DFORMAT fmt);
+LONG get_bitmask_green(WINED3DFORMAT fmt);
+LONG get_bitmask_blue(WINED3DFORMAT fmt);
+LONG get_bitmask_alpha(WINED3DFORMAT fmt);
 
 #endif