[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