[cairo] Problem with text rendering on Win32+ClearType, and possible fix

Carl Worth cworth at redhat.com
Wed Oct 4 11:04:37 PDT 2006


I don't know anything about the win32 text API being discussed here,
but hopefully I can make some useful comments about using git to
explore the code history.

On Wed, 04 Oct 2006 11:35:38 -0400, Owen Taylor wrote:
> 
> I'm not really sure how to extract the diff and commit message
> for the 1.0.2 => 1.0.4 change.

You can see all commit messages between 1.0.2 and 1.0.4 with:

	git log 1.0.2..1.0.4

and with the patch content as well with:

	git log -p 1.0.2..1.0.4

Now, I know that you're only interested in cairo-win32-surface.c
changes here, so you can restrict the view to that with either of the
following:

	git log 1.0.2..1.0.4 -- src/cairo-win32-surface.c

	git log -p 1.0.2..1.0.4 -- src/cairo-win32-surface.c

Beyond that, you can also narrow down with the -S option
(aka. "pickaxe", though don't ask me why it's -S for pickaxe) looking
for a patch that mentions a specific chunk of code. From the context
here, I would assume that's as follows:

	git log -p -SCAIRO_FORMAT_RGB24 1.0.2..1.0.4 -- src/cairo-win32-surface.c

That does result in a single commit, (and it's the same one Tor
referenced in his reply).

> [ Not sure what is up with the || dst->format == CAIRO_FORMAT_ARGB32

Also easy to look for with the -S option:

	git log -p -S'dst->format == CAIRO_FORMAT_ARGB32' -- src/cairo-win32-surface.c

That one finds two commits, and unfortunately, the first one is none
too small.

I've included the results of these last two commands below, (look for
'$' in the first column to find the beginning of each).

-Carl

$ git log -p -SCAIRO_FORMAT_RGB24 1.0.2..1.0.4 -- src/cairo-win32-surface.c

commit ec4b006c162292ea3b2719dc18a4a3bd40a971ab
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Fri Feb 17 23:34:51 2006 -0800

    Win32: Set surface format based on device caps
    
    If the DC is a display DC, inspect its depth and set out local format
    appropriately.  If it's not a display DC, assume RGB24.
    (cherry picked from 6dd0a70d271f93df95f4bcaff5073b9bf90cecb6 commit)
    (cherry picked from 2d784815ffac1ca8c10dac12525f2e8d0b412c1a commit)

diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 66fc1c4..84105e5 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -31,6 +31,8 @@
  *
  * Contributor(s):
  *	Owen Taylor <otaylor at redhat.com>
+ *	Stuart Parmenter <stuart at mozilla.com>
+ *	Vladimir Vukicevic <vladimir at pobox.com>
  */
 
 #include <stdio.h>
@@ -973,6 +975,8 @@ cairo_win32_surface_create (HDC hdc)
 {
     cairo_win32_surface_t *surface;
     RECT rect;
+    int depth;
+    cairo_format_t format;
 
     /* Try to figure out the drawing bounds for the Device context
      */
@@ -982,7 +986,26 @@ cairo_win32_surface_create (HDC hdc)
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
 	return &_cairo_surface_nil;
     }
-    
+
+    if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY) {
+	depth = GetDeviceCaps(hdc, BITSPIXEL);
+	if (depth == 32)
+	    format = CAIRO_FORMAT_ARGB32;
+	else if (depth == 24)
+	    format = CAIRO_FORMAT_RGB24;
+	else if (depth == 8)
+	    format = CAIRO_FORMAT_A8;
+	else if (depth == 1)
+	    format = CAIRO_FORMAT_A1;
+	else {
+	    _cairo_win32_print_gdi_error("cairo_win32_surface_create(bad BITSPIXEL)");
+	    _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    return &_cairo_surface_nil;
+	}
+    } else {
+	format = CAIRO_FORMAT_RGB24;
+    }
+
     surface = malloc (sizeof (cairo_win32_surface_t));
     if (surface == NULL) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
@@ -990,7 +1013,7 @@ cairo_win32_surface_create (HDC hdc)
     }
 
     surface->image = NULL;
-    surface->format = CAIRO_FORMAT_RGB24;
+    surface->format = format;
     
     surface->dc = hdc;
     surface->bitmap = NULL;

$ git log -p -S'dst->format == CAIRO_FORMAT_ARGB32' -- src/cairo-win32-surface.c

commit 016653812640cddcc51d0500d62c5c65b33bdd04
Author: Vladimir Vukicevic <vladimir at pobox.com>
Date:   Tue Sep 12 16:08:40 2006 -0700

    [win32] Support for DDBs, AlphaBlend fix
    
    Add support for the win32 surface using DDBs for similar surfaces and the
    like when the orignal surface is created from a DC, or when a DDB is
    explicitly created.  A DIB is still created if alpha is required.
    
    Also fixes a case where blitting win32 RGB24 -> ARGB32 surfaces was causing
    alpha to leak into the ARGB32 surface instead of being set to fully opaque.

diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
old mode 100755
new mode 100644
index 27bdcfa..3b3cf25
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
 /* Cairo - a vector graphics library with display and print output
  *
  * Copyright © 2005 Red Hat, Inc.
@@ -48,6 +49,9 @@ #ifndef SB_NONE
 #define SB_NONE         0x00000000
 #endif
 
+#define PELS_72DPI  (72. / 0.0254)
+#define NIL_SURFACE ((cairo_surface_t*)&_cairo_surface_nil)
+
 static const cairo_surface_backend_t cairo_win32_surface_backend;
 
 /**
@@ -87,6 +91,40 @@ _cairo_win32_print_gdi_error (const char
     return CAIRO_STATUS_NO_MEMORY;
 }
 
+static uint32_t
+_cairo_win32_flags_for_dc (HDC dc)
+{
+    uint32_t flags = 0;
+
+    if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
+	flags |= CAIRO_WIN32_SURFACE_IS_DISPLAY;
+
+	/* These will always be possible, but the actual GetDeviceCaps
+	 * calls will return whether they're accelerated or not.
+	 * We may want to use our own (pixman) routines sometimes
+	 * if they're eventually faster, but for now have GDI do
+	 * everything.
+	 */
+	flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+	flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+	flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+    } else {
+	int cap;
+
+	cap = GetDeviceCaps(dc, SHADEBLENDCAPS);
+	if (cap != SB_NONE)
+	    flags |= CAIRO_WIN32_SURFACE_CAN_ALPHABLEND;
+
+	cap = GetDeviceCaps(dc, RASTERCAPS);
+	if (cap & RC_BITBLT)
+	    flags |= CAIRO_WIN32_SURFACE_CAN_BITBLT;
+	if (cap & RC_STRETCHBLT)
+	    flags |= CAIRO_WIN32_SURFACE_CAN_STRETCHBLT;
+    }
+
+    return flags;
+}
+
 static cairo_status_t
 _create_dc_and_bitmap (cairo_win32_surface_t *surface,
 		       HDC                    original_dc,
@@ -110,6 +148,7 @@ _create_dc_and_bitmap (cairo_win32_surfa
 
     surface->dc = NULL;
     surface->bitmap = NULL;
+    surface->is_dib = FALSE;
 
     switch (format) {
     case CAIRO_FORMAT_ARGB32:
@@ -138,8 +177,8 @@ _create_dc_and_bitmap (cairo_win32_surfa
     bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
     bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
     bitmap_info->bmiHeader.biSizeImage = 0;
-    bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */
-    bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */
+    bitmap_info->bmiHeader.biXPelsPerMeter = PELS_72DPI; /* unused here */
+    bitmap_info->bmiHeader.biYPelsPerMeter = PELS_72DPI; /* unused here */
     bitmap_info->bmiHeader.biPlanes = 1;
 
     switch (format) {
@@ -147,6 +186,8 @@ _create_dc_and_bitmap (cairo_win32_surfa
      * break if we do, especially if we don't set up an image
      * fallback.  It could be a bug with using a 24bpp pixman image
      * (and creating one with masks).  So treat them like 32bpp.
+     * NOTE: This causes problems when using BitBlt/AlphaBlend/etc!
+     * see end of file.
      */
     case CAIRO_FORMAT_RGB24:
     case CAIRO_FORMAT_ARGB32:
@@ -198,6 +239,8 @@ _create_dc_and_bitmap (cairo_win32_surfa
     if (!surface->bitmap)
 	goto FAIL;
 
+    surface->is_dib = TRUE;
+
     GdiFlush();
 
     surface->saved_dc_bitmap = SelectObject (surface->dc,
@@ -229,6 +272,8 @@ _create_dc_and_bitmap (cairo_win32_surfa
 	}
     }
 
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
     return CAIRO_STATUS_SUCCESS;
 
  FAIL:
@@ -271,7 +316,7 @@ _cairo_win32_surface_create_for_dc (HDC 
     surface = malloc (sizeof (cairo_win32_surface_t));
     if (surface == NULL) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return &_cairo_surface_nil;
+	return NIL_SURFACE;
     }
 
     status = _create_dc_and_bitmap (surface, original_dc, format,
@@ -296,8 +341,8 @@ _cairo_win32_surface_create_for_dc (HDC 
 
     surface->saved_clip = CreateRectRgn (0, 0, 0, 0);
     if (GetClipRgn (surface->dc, surface->saved_clip) == 0) {
-        DeleteObject(surface->saved_clip);
-        surface->saved_clip = NULL;
+	DeleteObject(surface->saved_clip);
+	surface->saved_clip = NULL;
     }
 
     surface->extents = surface->clip_rect;
@@ -310,19 +355,56 @@ _cairo_win32_surface_create_for_dc (HDC 
  FAIL:
     if (surface->bitmap) {
 	SelectObject (surface->dc, surface->saved_dc_bitmap);
-  	DeleteObject (surface->bitmap);
-        DeleteDC (surface->dc);
+	DeleteObject (surface->bitmap);
+	DeleteDC (surface->dc);
     }
     if (surface)
 	free (surface);
 
     if (status == CAIRO_STATUS_NO_MEMORY) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return &_cairo_surface_nil;
+	return NIL_SURFACE;
     } else {
 	_cairo_error (status);
-	return &_cairo_surface_nil;
+	return NIL_SURFACE;
+    }
+}
+
+static cairo_surface_t *
+_cairo_win32_surface_create_similar_internal (void	    *abstract_src,
+					      cairo_content_t content,
+					      int	     width,
+					      int	     height,
+					      cairo_bool_t   force_dib)
+{
+    cairo_win32_surface_t *src = abstract_src;
+    cairo_format_t format = _cairo_format_from_content (content);
+    cairo_win32_surface_t *new_surf;
+
+    /* if the parent is a DIB or if we need alpha, then
+     * we have to create a dib */
+    if (force_dib || src->is_dib || (content & CAIRO_CONTENT_ALPHA)) {
+	new_surf = (cairo_win32_surface_t*)
+	    _cairo_win32_surface_create_for_dc (src->dc, format, width, height);
+    } else {
+	/* otherwise, create a ddb */
+	HBITMAP ddb = CreateCompatibleBitmap (src->dc, width, height);
+	HDC ddb_dc = CreateCompatibleDC (src->dc);
+	HRGN crgn = CreateRectRgn (0, 0, width, height);
+	HBITMAP saved_dc_bitmap;
+
+	saved_dc_bitmap = SelectObject (ddb_dc, ddb);
+	SelectClipRgn (ddb_dc, crgn);
+
+	DeleteObject (crgn);
+
+	new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc);
+	new_surf->bitmap = ddb;
+	new_surf->saved_dc_bitmap = saved_dc_bitmap;
+	new_surf->is_dib = FALSE;
     }
+
+    return (cairo_surface_t*) new_surf;
 }
 
 static cairo_surface_t *
@@ -331,10 +413,7 @@ _cairo_win32_surface_create_similar (voi
 				     int	     width,
 				     int	     height)
 {
-    cairo_win32_surface_t *src = abstract_src;
-    cairo_format_t format = _cairo_format_from_content (content);
-
-    return _cairo_win32_surface_create_for_dc (src->dc, format, width, height);
+    return _cairo_win32_surface_create_similar_internal (abstract_src, content, width, height, FALSE);
 }
 
 static cairo_status_t
@@ -351,8 +430,8 @@ _cairo_win32_surface_finish (void *abstr
     /* If we created the Bitmap and DC, destroy them */
     if (surface->bitmap) {
 	SelectObject (surface->dc, surface->saved_dc_bitmap);
-  	DeleteObject (surface->bitmap);
-        DeleteDC (surface->dc);
+	DeleteObject (surface->bitmap);
+	DeleteDC (surface->dc);
     }
 
     return CAIRO_STATUS_SUCCESS;
@@ -367,24 +446,31 @@ _cairo_win32_surface_get_subimage (cairo
 				   cairo_win32_surface_t **local_out)
 {
     cairo_win32_surface_t *local;
-    cairo_status_t status;
+    cairo_int_status_t status;
     cairo_content_t content = _cairo_content_from_format (surface->format);
 
     local =
-	(cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface,
-								       content,
-								       width,
-								       height);
+	(cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal
+	(surface, content, width, height, TRUE);
     if (local->base.status)
 	return CAIRO_STATUS_NO_MEMORY;
 
-    if (!BitBlt (local->dc,
-		 0, 0,
-		 width, height,
-		 surface->dc,
-		 x, y,
-		 SRCCOPY)) {
-	/* If we fail to BitBlt here, most likely the source is a printer.
+    status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if ((local->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT) &&
+	BitBlt (local->dc,
+		0, 0,
+		width, height,
+		surface->dc,
+		x, y,
+		SRCCOPY))
+    {
+	status = CAIRO_STATUS_SUCCESS;
+    }
+
+    if (status) {
+	/* If we failed here, most likely the source or dest doesn't
+	 * support BitBlt/AlphaBlend (e.g. a printer).
 	 * You can't reliably get bits from a printer DC, so just fill in
 	 * the surface as white (common case for printing).
 	 */
@@ -399,14 +485,6 @@ _cairo_win32_surface_get_subimage (cairo
     *local_out = local;
 
     return CAIRO_STATUS_SUCCESS;
-
- FAIL:
-    status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage");
-
-    if (local)
-	cairo_surface_destroy (&local->base);
-
-    return status;
 }
 
 static cairo_status_t
@@ -575,14 +653,18 @@ _composite_alpha_blend (cairo_win32_surf
 			int                    alpha,
 			int                    src_x,
 			int                    src_y,
+			int                    src_w,
+			int                    src_h,
 			int                    dst_x,
 			int                    dst_y,
-			int                    width,
-			int                    height)
+			int                    dst_w,
+			int                    dst_h)
 {
     static unsigned alpha_blend_checked = FALSE;
     static cairo_alpha_blend_func_t alpha_blend = NULL;
 
+    int oldstretchmode;
+    BOOL success;
     BLENDFUNCTION blend_function;
 
     /* Check for AlphaBlend dynamically to allow compiling on
@@ -611,22 +693,35 @@ _composite_alpha_blend (cairo_win32_surf
 
     if (alpha_blend == NULL)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
-    if (GetDeviceCaps(dst->dc, SHADEBLENDCAPS) == SB_NONE)
+    if (!(dst->flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (src->format == CAIRO_FORMAT_RGB24 && dst->format == CAIRO_FORMAT_ARGB32)
+    {
+	/* Both of these are represented as 32bpp internally, and AlphaBlend
+	 * DOES NOT throw away source alpha is AC_SRC_ALPHA is not specified,
+	 * it just multiplies it by the SourceConstantAlpha, along with the
+	 * R G B components.
+	 * XXX there has to be a way to do this!
+	 */
 	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
 
     blend_function.BlendOp = AC_SRC_OVER;
     blend_function.BlendFlags = 0;
     blend_function.SourceConstantAlpha = alpha;
-    blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0;
+    blend_function.AlphaFormat = (src->format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0;
 
+    /*oldstretchmode = SetStretchBltMode(dst->dc, HALFTONE);*/
     if (!alpha_blend (dst->dc,
 		      dst_x, dst_y,
-		      width, height,
+		      dst_w, dst_h,
 		      src->dc,
 		      src_x, src_y,
-		      width, height,
+		      src_w, src_h,
 		      blend_function))
 	return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite");
+    /*SetStretchBltMode(dst->dc, oldstretchmode);*/
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -649,11 +744,39 @@ _cairo_win32_surface_composite (cairo_op
     cairo_win32_surface_t *src;
     cairo_surface_pattern_t *src_surface_pattern;
     int alpha;
-    int integer_transform;
     int itx, ity;
+    double scalex, scaley;
+    cairo_fixed_t x0_fixed, y0_fixed;
+    int orig_dst_x = dst_x, orig_dst_y = dst_y;
+    int real_src_width, real_src_height;
+
+    int src_width, src_height;
+    int dst_width, dst_height;
+  
+    cairo_bool_t needs_alpha, needs_scale;
+    cairo_image_surface_t *src_image = NULL;
+
+#if 0
+    fprintf (stderr, "composite: %d %p %p %p [%d %d] [%d %d] [%d %d] %dx%d\n",
+	     op, pattern, mask_pattern, abstract_dst, src_x, src_y, mask_x, mask_y, dst_x, dst_y, width, height);
+#endif
 
-    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE ||
-	pattern->extend != CAIRO_EXTEND_NONE)
+    /* If the destination can't do any of these, then
+     * we may as well give up, since this is what we'll
+     * look to for optimization.
+     */
+    if (dst->flags & (CAIRO_WIN32_SURFACE_CAN_BITBLT |
+		      CAIRO_WIN32_SURFACE_CAN_ALPHABLEND |
+		      CAIRO_WIN32_SURFACE_CAN_STRETCHBLT)
+	== 0)
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (pattern->extend != CAIRO_EXTEND_NONE)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     if (mask_pattern) {
@@ -665,67 +788,188 @@ _cairo_win32_surface_composite (cairo_op
 
 	alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8;
     } else {
-        alpha = 255;
+	alpha = 255;
     }
 
     src_surface_pattern = (cairo_surface_pattern_t *)pattern;
     src = (cairo_win32_surface_t *)src_surface_pattern->surface;
 
-    if (src->base.backend != dst->base.backend)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    /* Disable this for now */
+#if 0
+    if (src->base.type == CAIRO_SURFACE_TYPE_IMAGE) {
+	src_image = (cairo_image_surface_t*) src;
 
-    integer_transform = _cairo_matrix_is_integer_translation (&pattern->matrix, &itx, &ity);
-    if (!integer_transform)
+	if (src_image->format != CAIRO_FORMAT_RGB24)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+    } else 
+#endif
+    if (src->base.backend != dst->base.backend) {
 	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+#if 0
+    fprintf (stderr, "Before check: (%d %d) [%d %d] -> [%d %d %d %d] {mat %f %f %f %f}\n",
+	     src->extents.width, src->extents.height,
+	     src_x, src_y,
+	     dst_x, dst_y, width, height,
+	     pattern->matrix.x0, pattern->matrix.y0, pattern->matrix.xx, pattern->matrix.yy);
+#endif
 
-    /* Fix up src coordinates; the src coords and size must be within the
-     * bounds of the source surface.
-     * XXX the region not covered should be appropriately rendered!
-     * - for OVER/SOURCE with RGB24 source -> opaque black
-     * - for SOURCE with ARGB32 source -> 100% transparent black
+    /* We can only use GDI functions if the source and destination rectangles
+     * are on integer pixel boundaries.  Figure that out here.
      */
+    x0_fixed = _cairo_fixed_from_double(pattern->matrix.x0 / pattern->matrix.xx);
+    y0_fixed = _cairo_fixed_from_double(pattern->matrix.y0 / pattern->matrix.yy);
+
+    if (pattern->matrix.yx != 0.0 ||
+	pattern->matrix.xy != 0.0 ||
+	!_cairo_fixed_is_integer(x0_fixed) ||
+	!_cairo_fixed_is_integer(y0_fixed))
+    {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    itx = _cairo_fixed_integer_part(x0_fixed);
+    ity = _cairo_fixed_integer_part(y0_fixed);
+
+    scalex = pattern->matrix.xx;
+    scaley = pattern->matrix.yy;
+
     src_x += itx;
     src_y += ity;
 
+    if (scalex <= 0.0 || scaley <= 0.0)
+	return CAIRO_STATUS_SUCCESS;
+
+    if (scalex != 1.0 || scaley != 1.0)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* If the src coordinates are outside of the source surface bounds,
+     * we have to fix them up, because this is an error for the GDI
+     * functions.
+     * XXX Make sure to correctly clear out the unpainted region.
+     */
+
     if (src_x < 0) {
-        width += src_x;
-        dst_x -= src_x;
-        src_x = 0;
+	width += src_x;
+	dst_x -= src_x;
+	src_x = 0;
     }
 
     if (src_y < 0) {
-        height += src_y;
-        dst_y -= src_y;
-        src_y = 0;
+	height += src_y;
+	dst_y -= src_y;
+	src_y = 0;
     }
 
     if (src_x + width > src->extents.width)
-        width = src->extents.width - src_x;
+	dst_width = src->extents.width - src_x;
+    else
+	dst_width = width;
 
     if (src_y + height > src->extents.height)
-        height = src->extents.height - src_y;
+	dst_height = src->extents.height - src_y;
+    else
+	dst_height = height;
 
-    if (alpha == 255 &&
-	(op == CAIRO_OPERATOR_SOURCE ||
-	 (src->format == CAIRO_FORMAT_RGB24 && op == CAIRO_OPERATOR_OVER))) {
+    src_width = dst_width;
+    src_height = dst_height;
 
-	if (!BitBlt (dst->dc,
-		     dst_x, dst_y,
-		     width, height,
-		     src->dc,
-		     src_x, src_y,
-		     SRCCOPY))
-	    return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite");
+    /*
+     * Figure out what action to take.
+     * XXX handle SOURCE with alpha != 255
+     */
+    if (alpha == 255 &&
+	(dst->format == src->format) &&
+	((op == CAIRO_OPERATOR_SOURCE && (dst->format == CAIRO_FORMAT_ARGB32 ||
+					  dst->format == CAIRO_FORMAT_RGB24)) ||
+	 (op == CAIRO_OPERATOR_OVER && dst->format == CAIRO_FORMAT_RGB24)))
+    {
+	needs_alpha = FALSE;
+    } else if ((op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_SOURCE) &&
+	       (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) &&
+	       (dst->format == CAIRO_FORMAT_RGB24 || dst->format == CAIRO_FORMAT_ARGB32))
+    {
+	needs_alpha = TRUE;
+    } else {
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+  
+    if (scalex == 1.0 && scaley == 1.0) {
+	needs_scale = FALSE;
+    } else {
+	needs_scale = TRUE;
+    }
 
-	return CAIRO_STATUS_SUCCESS;
+#if 0
+    fprintf (stderr, "action: [%d %d %d %d] -> [%d %d %d %d]\n",
+	     src_x, src_y, src_width, src_height,
+	     dst_x, dst_y, dst_width, dst_height);
+    fflush (stderr);
+#endif
 
-    } else if ((src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) &&
-	       (dst->format == CAIRO_FORMAT_RGB24 || dst->format == CAIRO_FORMAT_ARGB32) &&
-	       op == CAIRO_OPERATOR_OVER) {
+    /* Then do BitBlt, StretchDIBits, StretchBlt, AlphaBlend, or MaskBlt */
+    if (src_image) {
+	BITMAPINFO bi;
+	bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+	bi.bmiHeader.biWidth = src_image->width;
+	bi.bmiHeader.biHeight = src_image->height;
+	bi.bmiHeader.biSizeImage = 0;
+	bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+	bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+	bi.bmiHeader.biPlanes = 1;
+	bi.bmiHeader.biBitCount = 32;
+	bi.bmiHeader.biCompression = BI_RGB;
+	bi.bmiHeader.biClrUsed = 0;
+	bi.bmiHeader.biClrImportant = 0;
+
+	if (!StretchDIBits (dst->dc,
+			    dst_x, dst_y, dst_width, dst_height,
+			    src_x, src_y, src_width, src_height,
+			    src_image->data,
+			    &bi,
+			    DIB_RGB_COLORS,
+			    SRCCOPY))
+	    return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(StretchDIBits)");
 
-	return _composite_alpha_blend (dst, src, alpha,
-				       src_x, src_y,
-				       dst_x, dst_y, width, height);
+	return CAIRO_STATUS_SUCCESS;
+    } else if (!needs_alpha) {
+	/* BitBlt or StretchBlt? */
+	if (!needs_scale && (dst->flags & CAIRO_WIN32_SURFACE_CAN_BITBLT)) {
+	    if (!BitBlt (dst->dc,
+			 dst_x, dst_y,
+			 dst_width, dst_height,
+			 src->dc,
+			 src_x, src_y,
+			 SRCCOPY))
+		return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite(BitBlt)");
+
+	    return CAIRO_STATUS_SUCCESS;
+	} else if (dst->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHBLT) {
+	    /* StretchBlt? */
+	    /* XXX check if we want HALFTONE, based on the src filter */
+	    BOOL success;
+	    int oldmode = SetStretchBltMode(dst->dc, HALFTONE);
+	    success = StretchBlt(dst->dc,
+				 dst_x, dst_y,
+				 dst_width, dst_height,
+				 src->dc,
+				 src_x, src_y,
+				 src_width, src_height,
+				 SRCCOPY);
+	    SetStretchBltMode(dst->dc, oldmode);
+
+	    if (!success) {
+		_cairo_win32_print_gdi_error ("StretchBlt");
+		return CAIRO_INT_STATUS_UNSUPPORTED;
+	    }
+  
+	    return CAIRO_STATUS_SUCCESS;
+	}
+    } else if (needs_alpha && !needs_scale) {
+  	return _composite_alpha_blend (dst, src, alpha,
+				       src_x, src_y, src_width, src_height,
+				       dst_x, dst_y, dst_width, dst_height);
     }
 
     return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -1114,13 +1358,13 @@ cairo_win32_surface_create (HDC hdc)
 	_cairo_win32_print_gdi_error ("cairo_win32_surface_create");
 	/* XXX: Can we make a more reasonable guess at the error cause here? */
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return &_cairo_surface_nil;
+	return NIL_SURFACE;
     }
 
     if (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY) {
 	depth = GetDeviceCaps(hdc, BITSPIXEL);
 	if (depth == 32)
-	    format = CAIRO_FORMAT_ARGB32;
+	    format = CAIRO_FORMAT_RGB24;
 	else if (depth == 24)
 	    format = CAIRO_FORMAT_RGB24;
 	else if (depth == 16)
@@ -1132,7 +1376,7 @@ cairo_win32_surface_create (HDC hdc)
 	else {
 	    _cairo_win32_print_gdi_error("cairo_win32_surface_create(bad BITSPIXEL)");
 	    _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    return &_cairo_surface_nil;
+	    return NIL_SURFACE;
 	}
     } else {
 	format = CAIRO_FORMAT_RGB24;
@@ -1141,7 +1385,7 @@ cairo_win32_surface_create (HDC hdc)
     surface = malloc (sizeof (cairo_win32_surface_t));
     if (surface == NULL) {
 	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return &_cairo_surface_nil;
+	return NIL_SURFACE;
     }
 
     surface->image = NULL;
@@ -1149,6 +1393,7 @@ cairo_win32_surface_create (HDC hdc)
 
     surface->dc = hdc;
     surface->bitmap = NULL;
+    surface->is_dib = FALSE;
     surface->saved_dc_bitmap = NULL;
 
     surface->clip_rect.x = rect.left;
@@ -1157,19 +1402,21 @@ cairo_win32_surface_create (HDC hdc)
     surface->clip_rect.height = rect.bottom - rect.top;
 
     if (surface->clip_rect.width == 0 ||
-        surface->clip_rect.height == 0)
+	surface->clip_rect.height == 0)
     {
-        surface->saved_clip = NULL;
+	surface->saved_clip = NULL;
     } else {
-        surface->saved_clip = CreateRectRgn (0, 0, 0, 0);
-        if (GetClipRgn (hdc, surface->saved_clip) == 0) {
-            DeleteObject(surface->saved_clip);
-            surface->saved_clip = NULL;
-        }
+	surface->saved_clip = CreateRectRgn (0, 0, 0, 0);
+	if (GetClipRgn (hdc, surface->saved_clip) == 0) {
+	    DeleteObject(surface->saved_clip);
+	    surface->saved_clip = NULL;
+	}
     }
 
     surface->extents = surface->clip_rect;
 
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
     _cairo_surface_init (&surface->base, &cairo_win32_surface_backend,
 			 _cairo_content_from_format (format));
 
@@ -1192,13 +1439,73 @@ cairo_win32_surface_create (HDC hdc)
  **/
 cairo_surface_t *
 cairo_win32_surface_create_with_dib (cairo_format_t format,
-                                     int	    width,
-                                     int	    height)
+				     int	    width,
+				     int	    height)
 {
     return _cairo_win32_surface_create_for_dc (NULL, format, width, height);
 }
 
 /**
+ * cairo_win32_surface_create_with_ddb:
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a device-independent-bitmap surface not associated with
+ * any particular existing surface or device context. The created
+ * bitmap will be unititialized.
+ *
+ * Return value: the newly created surface
+ *
+ * Since: 1.4
+ **/
+cairo_surface_t *
+cairo_win32_surface_create_with_ddb (HDC hdc,
+				     cairo_format_t format,
+				     int width,
+				     int height)
+{
+    cairo_win32_surface_t *new_surf;
+    HBITMAP ddb;
+    HDC screen_dc, ddb_dc;
+    HRGN crgn;
+    HBITMAP saved_dc_bitmap;
+
+    if (format != CAIRO_FORMAT_RGB24)
+	return NULL;
+/* XXX handle these eventually
+	format != CAIRO_FORMAT_A8 ||
+	format != CAIRO_FORMAT_A1)
+*/
+
+    if (!hdc) {
+	screen_dc = GetDC (NULL);
+	hdc = screen_dc;
+    } else {
+	screen_dc = NULL;
+    }
+
+    ddb = CreateCompatibleBitmap (hdc, width, height);
+    ddb_dc = CreateCompatibleDC (hdc);
+
+    saved_dc_bitmap = SelectObject (ddb_dc, ddb);
+
+    crgn = CreateRectRgn (0, 0, width, height);
+    SelectClipRgn (ddb_dc, crgn);
+    DeleteObject (crgn);
+
+    new_surf = (cairo_win32_surface_t*) cairo_win32_surface_create (ddb_dc);
+    new_surf->bitmap = ddb;
+    new_surf->saved_dc_bitmap = saved_dc_bitmap;
+    new_surf->is_dib = FALSE;
+
+    if (screen_dc)
+	ReleaseDC (NULL, screen_dc);
+
+    return (cairo_surface_t*) new_surf;
+}
+
+/**
  * _cairo_surface_is_win32:
  * @surface: a #cairo_surface_t
  *
@@ -1239,6 +1546,28 @@ cairo_win32_surface_get_dc (cairo_surfac
     return winsurf->dc;
 }
 
+/**
+ * cario_win32_surface_get_image
+ * @surface: a #cairo_surface_t
+ *
+ * Returns a #cairo_surface_t image surface that refers to the same bits
+ * as the DIB of the Win32 surface.  If the passed-in win32 surface
+ * is not a DIB surface, NULL is returned.
+ *
+ * Return value: a #cairo_surface_t (owned by the win32 cairo_surface_t),
+ * or NULL if the win32 surface is not a DIB.
+ *
+ * Since: 1.4
+ */
+cairo_surface_t *
+cairo_win32_surface_get_image (cairo_surface_t *surface)
+{
+    if (!_cairo_surface_is_win32(surface))
+	return NULL;
+
+    return ((cairo_win32_surface_t*)surface)->image;
+}
+
 static const cairo_surface_backend_t cairo_win32_surface_backend = {
     CAIRO_SURFACE_TYPE_WIN32,
     _cairo_win32_surface_create_similar,
@@ -1289,7 +1618,7 @@ static int _cairo_win32_initialized = 0;
 void
 _cairo_win32_initialize () {
     if (_cairo_win32_initialized)
-        return;
+	return;
 
     /* every 'mutex' from CAIRO_MUTEX_DECALRE needs to be initialized here */
     InitializeCriticalSection (&cairo_toy_font_face_hash_table_mutex);
@@ -1320,3 +1649,17 @@ DllMain (HINSTANCE hinstDLL,
 }
 #endif
 #endif
+
+/* Notes:
+ *
+ * Win32 alpha-understanding functions
+ *
+ * BitBlt - will copy full 32 bits from a 32bpp DIB to result
+ *          (so it's safe to use for ARGB32->ARGB32 SOURCE blits)
+ *          (but not safe going RGB24->ARGB32, if RGB24 is also represented
+ *           as a 32bpp DIB, since the alpha isn't discarded!)
+ *
+ * AlphaBlend - if both the source and dest have alpha, even if AC_SRC_ALPHA isn't set,
+ *              it will still copy over the src alpha, because the SCA value (255) will be
+ *              multiplied by all the src components.
+ */

commit 9b84b3da58a62b25c129626f918713036e41cc88
Author: Vladimir Vukicevic <vladimir at cyclone.vlad1.com>
Date:   Fri Mar 17 00:06:21 2006 -0800

    [win32] GDI is nearly always faster than pixman; use it whenever possible
    
    Remove local image check from fill_rectangles and fix check for whether
    we can AlphaBlend or not (ARGB->ARGB AlphaBlend works fine)
    (cherry picked from f099783b3e7f895a59d4d4a67a8534f1d21d44e1 commit)

diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index 7e7d0ee..12486cd 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -143,8 +143,13 @@ _create_dc_and_bitmap (cairo_win32_surfa
     bitmap_info->bmiHeader.biPlanes = 1;
     
     switch (format) {
-    case CAIRO_FORMAT_ARGB32:
+    /* We can't create real RGB24 bitmaps because something seems to
+     * break if we do, especially if we don't set up an image
+     * fallback.  It could be a bug with using a 24bpp pixman image
+     * (and creating one with masks).  So treat them like 32bpp.
+     */
     case CAIRO_FORMAT_RGB24:
+    case CAIRO_FORMAT_ARGB32:
 	bitmap_info->bmiHeader.biBitCount = 32;
 	bitmap_info->bmiHeader.biCompression = BI_RGB;
 	bitmap_info->bmiHeader.biClrUsed = 0;	/* unused */
@@ -698,7 +703,6 @@ _cairo_win32_surface_composite (cairo_op
         height = src->extents.height - src_y;
 
     if (alpha == 255 &&
-	src->format == dst->format &&
 	(op == CAIRO_OPERATOR_SOURCE ||
 	 (src->format == CAIRO_FORMAT_RGB24 && op == CAIRO_OPERATOR_OVER))) {
 	
@@ -712,9 +716,8 @@ _cairo_win32_surface_composite (cairo_op
 
 	return CAIRO_STATUS_SUCCESS;
 	
-    } else if (integer_transform &&
-	       (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) &&
-	       dst->format == CAIRO_FORMAT_RGB24 &&
+    } else if ((src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) &&
+	       (dst->format == CAIRO_FORMAT_RGB24 || dst->format == CAIRO_FORMAT_ARGB32) &&
 	       op == CAIRO_OPERATOR_OVER) {
 
 	return _composite_alpha_blend (dst, src, alpha,
@@ -816,10 +819,11 @@ _cairo_win32_surface_fill_rectangles (vo
     HBRUSH new_brush;
     int i;
 
-    /* If we have a local image, use the fallback code; it will be as fast
-     * as calling out to GDI.
-     */
-    if (surface->image)
+    /* XXXperf If it's not RGB24, we need to do a little more checking
+     * to figure out when we can use GDI.  We don't have that checking
+     * anywhere at the moment, so just bail and use the fallback
+     * paths. */
+    if (surface->format != CAIRO_FORMAT_RGB24)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20061004/b71ca1d0/attachment-0001.pgp


More information about the cairo mailing list