win32: Let cairo create DIBs for GdkPixmap

The win32 code for GdkPixmap dib creation workes as such, but
when creating a cairo surface for it with cairo_win32_surface_create
from the HDC it fails for any bitmap format than RGB24, due to
assumptions in cairo.

In order to create a cairo surface for e.g. A1 formats we need
to let cairo create the surface via cairo_win32_surface_create_with_dib.
Additionally, we must then make sure to use this surface in
gdk_drawable_ref_cairo_surface, and to not create a new HDC for
it but reuse the cairo one (as only one HDC can write to a bitmap).

Even with this fixed there are some issues with current cairo, as
cairo A1 format isn't quite the same as win32 monochrome bitmaps.
Fixes for cairo will be submitted.
This commit is contained in:
Alexander Larsson 2011-11-09 13:25:02 +01:00
parent 36f2f822f2
commit 2ae574ab6d

View File

@ -35,6 +35,7 @@
#include "gdkscreen.h"
#include "gdkprivate-win32.h"
#include <cairo-win32.h>
static void gdk_pixmap_impl_win32_get_size (GdkDrawable *drawable,
gint *width,
@ -109,9 +110,10 @@ gdk_pixmap_impl_win32_finalize (GObject *object)
GDK_NOTE (PIXMAP, g_print ("gdk_pixmap_impl_win32_finalize: %p\n",
GDK_PIXMAP_HBITMAP (wrapper)));
_gdk_win32_drawable_finish (GDK_DRAWABLE (object));
if (!impl->is_foreign)
GDK_DRAWABLE_IMPL_WIN32 (impl)->hdc_count--;
GDI_CALL (DeleteObject, (GDK_PIXMAP_HBITMAP (wrapper)));
_gdk_win32_drawable_finish (GDK_DRAWABLE (object));
gdk_win32_handle_table_remove (GDK_PIXMAP_HBITMAP (wrapper));
@ -135,26 +137,16 @@ _gdk_pixmap_new (GdkDrawable *drawable,
gint height,
gint depth)
{
struct {
BITMAPINFOHEADER bmiHeader;
union {
WORD bmiIndices[256];
DWORD bmiMasks[3];
RGBQUAD bmiColors[256];
} u;
} bmi;
UINT iUsage;
HDC hdc;
HWND hwnd;
HPALETTE holdpal = NULL;
HBITMAP hbitmap;
GdkPixmap *pixmap;
GdkDrawableImplWin32 *drawable_impl;
GdkPixmapImplWin32 *pixmap_impl;
GdkColormap *cmap;
guchar *bits;
gint i;
gint window_depth;
cairo_surface_t *dib_surface, *image_surface;
cairo_format_t format;
g_return_val_if_fail (drawable == NULL || GDK_IS_DRAWABLE (drawable), NULL);
g_return_val_if_fail ((drawable != NULL) || (depth != -1), NULL);
@ -173,6 +165,27 @@ _gdk_pixmap_new (GdkDrawable *drawable,
GDK_NOTE (PIXMAP, g_print ("gdk_pixmap_new: %dx%dx%d drawable=%p\n",
width, height, depth, drawable));
switch (depth)
{
case 1:
format = CAIRO_FORMAT_A1;
break;
case 8:
format = CAIRO_FORMAT_A8;
break;
case 24:
case 32:
format = CAIRO_FORMAT_RGB24;
break;
default:
g_warning ("gdk_win32_pixmap_new: depth = %d not supported", depth);
return NULL;
break;
}
pixmap = g_object_new (gdk_pixmap_get_type (), NULL);
drawable_impl = GDK_DRAWABLE_IMPL_WIN32 (GDK_PIXMAP_OBJECT (pixmap)->impl);
pixmap_impl = GDK_PIXMAP_IMPL_WIN32 (GDK_PIXMAP_OBJECT (pixmap)->impl);
@ -189,123 +202,32 @@ _gdk_pixmap_new (GdkDrawable *drawable,
if (cmap)
gdk_drawable_set_colormap (pixmap, cmap);
}
if (GDK_IS_WINDOW (drawable))
hwnd = GDK_WINDOW_HWND (drawable);
else
hwnd = GetDesktopWindow ();
if ((hdc = GetDC (hwnd)) == NULL)
dib_surface = cairo_win32_surface_create_with_dib (format, width, height);
if (dib_surface == NULL)
{
WIN32_GDI_FAILED ("GetDC");
g_object_unref ((GObject *) pixmap);
return NULL;
}
bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -height;
bmi.bmiHeader.biPlanes = 1;
switch (depth)
{
case 1:
case 24:
case 32:
bmi.bmiHeader.biBitCount = _gdk_windowing_get_bits_for_depth (gdk_display_get_default (), depth);
break;
/* We need to have cairo create the dibsection for us, because
creating a cairo surface from a hdc only works for rgb24 format */
hdc = cairo_win32_surface_get_dc (dib_surface);
case 4:
bmi.bmiHeader.biBitCount = 4;
break;
case 5:
case 6:
case 7:
case 8:
bmi.bmiHeader.biBitCount = 8;
break;
case 15:
case 16:
bmi.bmiHeader.biBitCount = 16;
break;
/* We need to use the same hdc, because only one hdc
can render to the same bitmap */
drawable_impl->hdc = hdc;
drawable_impl->hdc_count = 1; /* Ensure we never free the cairo surface HDC */
default:
g_warning ("gdk_win32_pixmap_new: depth = %d", depth);
g_assert_not_reached ();
}
if (bmi.bmiHeader.biBitCount == 16)
bmi.bmiHeader.biCompression = BI_BITFIELDS;
else
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0;
bmi.bmiHeader.biXPelsPerMeter =
bmi.bmiHeader.biYPelsPerMeter = 0;
bmi.bmiHeader.biClrUsed = 0;
bmi.bmiHeader.biClrImportant = 0;
iUsage = DIB_RGB_COLORS;
if (depth == 1)
{
bmi.u.bmiColors[0].rgbBlue =
bmi.u.bmiColors[0].rgbGreen =
bmi.u.bmiColors[0].rgbRed = 0x00;
bmi.u.bmiColors[0].rgbReserved = 0x00;
bmi.u.bmiColors[1].rgbBlue =
bmi.u.bmiColors[1].rgbGreen =
bmi.u.bmiColors[1].rgbRed = 0xFF;
bmi.u.bmiColors[1].rgbReserved = 0x00;
}
else
{
if (depth <= 8 && drawable_impl->colormap != NULL)
{
GdkColormapPrivateWin32 *cmapp =
GDK_WIN32_COLORMAP_DATA (drawable_impl->colormap);
gint k;
if ((holdpal = SelectPalette (hdc, cmapp->hpal, FALSE)) == NULL)
WIN32_GDI_FAILED ("SelectPalette");
else if ((k = RealizePalette (hdc)) == GDI_ERROR)
WIN32_GDI_FAILED ("RealizePalette");
else if (k > 0)
GDK_NOTE (PIXMAP_OR_COLORMAP, g_print ("_gdk_win32_pixmap_new: realized %p: %d colors\n",
cmapp->hpal, k));
iUsage = DIB_PAL_COLORS;
for (i = 0; i < 256; i++)
bmi.u.bmiIndices[i] = i;
}
else if (bmi.bmiHeader.biBitCount == 16)
{
GdkVisual *visual = gdk_visual_get_system ();
bmi.u.bmiMasks[0] = visual->red_mask;
bmi.u.bmiMasks[1] = visual->green_mask;
bmi.u.bmiMasks[2] = visual->blue_mask;
}
}
hbitmap = CreateDIBSection (hdc, (BITMAPINFO *) &bmi,
iUsage, (PVOID *) &bits, NULL, 0);
if (holdpal != NULL)
SelectPalette (hdc, holdpal, FALSE);
GDI_CALL (ReleaseDC, (hwnd, hdc));
GDK_NOTE (PIXMAP, g_print ("... =%p bits=%p pixmap=%p\n", hbitmap, bits, pixmap));
if (hbitmap == NULL)
{
WIN32_GDI_FAILED ("CreateDIBSection");
g_object_unref ((GObject *) pixmap);
return NULL;
}
/* No need to create a new surface when needed, as we have one already */
drawable_impl->cairo_surface = dib_surface;
/* Get the bitmap from the cairo hdc */
hbitmap = GetCurrentObject (hdc, OBJ_BITMAP);
drawable_impl->handle = hbitmap;
pixmap_impl->bits = bits;
image_surface = cairo_win32_surface_get_image (dib_surface);
pixmap_impl->bits = cairo_image_surface_get_data (image_surface);
gdk_win32_handle_table_insert (&GDK_PIXMAP_HBITMAP (pixmap), pixmap);