gdiplus: Add a fallback method for getting HDC's from Graphics objects.
authorVincent Povirk <vincent@codeweavers.com>
Mon, 16 Aug 2010 21:31:18 +0000 (16:31 -0500)
committerAlexandre Julliard <julliard@winehq.org>
Wed, 18 Aug 2010 07:42:58 +0000 (09:42 +0200)
Native does this in more cases (at least for all bitmap graphics
objects), but using gdi32 objects when we can should perform better.

dlls/gdiplus/gdiplus_private.h
dlls/gdiplus/graphics.c

index f3393d4325b4ecb8c1819c51d1f181399f2da16a..b78e459a3cead2a57324529010a80d0a5878c332 100644 (file)
@@ -152,6 +152,12 @@ struct GpGraphics{
     UINT textcontrast; /* not used yet. get/set only */
     struct list containers;
     GraphicsContainer contid; /* last-issued container ID */
+    /* For giving the caller an HDC when we technically can't: */
+    HBITMAP temp_hbitmap;
+    int temp_hbitmap_width;
+    int temp_hbitmap_height;
+    BYTE *temp_bits;
+    HDC temp_hdc;
 };
 
 struct GpBrush{
index 885011085ad07732f7974f9952c7ef6a9ab898d6..8e203f35b7b1c9f14f6fa849094b010e04a6e709 100644 (file)
@@ -4663,6 +4663,9 @@ GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST G
     return ret;
 }
 
+/* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
+static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
+
 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
 {
     TRACE("(%p, %p)\n", graphics, hdc);
@@ -4673,14 +4676,61 @@ GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
     if(graphics->busy)
         return ObjectBusy;
 
-    if (!graphics->hdc)
+    if (!graphics->hdc ||
+        (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
     {
-        WARN("no HDC for this graphics\n");
-        *hdc = NULL;
-        return GenericError;
+        /* Create a fake HDC and fill it with a constant color. */
+        HDC temp_hdc;
+        HBITMAP hbitmap;
+        GpStatus stat;
+        GpRectF bounds;
+        BITMAPINFOHEADER bmih;
+        int i;
+
+        stat = get_graphics_bounds(graphics, &bounds);
+        if (stat != Ok)
+            return stat;
+
+        graphics->temp_hbitmap_width = bounds.Width;
+        graphics->temp_hbitmap_height = bounds.Height;
+
+        bmih.biSize = sizeof(bmih);
+        bmih.biWidth = graphics->temp_hbitmap_width;
+        bmih.biHeight = -graphics->temp_hbitmap_height;
+        bmih.biPlanes = 1;
+        bmih.biBitCount = 32;
+        bmih.biCompression = BI_RGB;
+        bmih.biSizeImage = 0;
+        bmih.biXPelsPerMeter = 0;
+        bmih.biYPelsPerMeter = 0;
+        bmih.biClrUsed = 0;
+        bmih.biClrImportant = 0;
+
+        hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
+            (void**)&graphics->temp_bits, NULL, 0);
+        if (!hbitmap)
+            return GenericError;
+
+        temp_hdc = CreateCompatibleDC(0);
+        if (!temp_hdc)
+        {
+            DeleteObject(hbitmap);
+            return GenericError;
+        }
+
+        for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
+            ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
+
+        SelectObject(temp_hdc, hbitmap);
+
+        graphics->temp_hbitmap = hbitmap;
+        *hdc = graphics->temp_hdc = temp_hdc;
+    }
+    else
+    {
+        *hdc = graphics->hdc;
     }
 
-    *hdc = graphics->hdc;
     graphics->busy = TRUE;
 
     return Ok;
@@ -4690,12 +4740,40 @@ GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
 {
     TRACE("(%p, %p)\n", graphics, hdc);
 
-    if(!graphics)
+    if(!graphics || !hdc)
         return InvalidParameter;
 
-    if(graphics->hdc != hdc || !(graphics->busy))
+    if((graphics->hdc != hdc && graphics->temp_hdc != hdc) || !(graphics->busy))
         return InvalidParameter;
 
+    if (graphics->temp_hdc == hdc)
+    {
+        DWORD* pos;
+        int i;
+
+        /* Find the pixels that have changed, and mark them as opaque. */
+        pos = (DWORD*)graphics->temp_bits;
+        for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
+        {
+            if (*pos != DC_BACKGROUND_KEY)
+            {
+                *pos |= 0xff000000;
+            }
+            pos++;
+        }
+
+        /* Write the changed pixels to the real target. */
+        alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
+            graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
+            graphics->temp_hbitmap_width * 4);
+
+        /* Clean up. */
+        DeleteDC(graphics->temp_hdc);
+        DeleteObject(graphics->temp_hbitmap);
+        graphics->temp_hdc = NULL;
+        graphics->temp_hbitmap = NULL;
+    }
+
     graphics->busy = FALSE;
 
     return Ok;