b=455513; add optional flag to allow converting a DDB to a DIB internally, if the surface is every used as a source; r=jmuizelaar
authorVladimir Vukicevic <vladimir@pobox.com>
Sun, 09 Nov 2008 15:40:55 -0800
changeset 21539 5975189375dac77d390bb0f45ab347b924b349d9
parent 21538 7ecd487ffe0c6fe1c4094b4048dc9221963226e6
child 21540 d1baec088ee009f91aafdfb9f5e0c5d43c665db1
push idunknown
push userunknown
push dateunknown
reviewersjmuizelaar
bugs455513
milestone1.9.1b2pre
b=455513; add optional flag to allow converting a DDB to a DIB internally, if the surface is every used as a source; r=jmuizelaar If a DDB is used as a source for an operation that can't be handled natively by GDI, we end up needing to take a really slow path (creating a temporary surface for acquire_source) for each operation. If we convert the DDB to a DIB, we then end up having a real image buffer and can hand things off to pixman directly. This isn't the default mode because I'm not sure if there are cases where a DDB is explicitly needed (e.g. for printing), and it would change current cairo behaviour. It might become the default at some point in the future.
gfx/cairo/README
gfx/cairo/cairo/src/cairo-win32-private.h
gfx/cairo/cairo/src/cairo-win32-surface.c
gfx/cairo/cairo/src/cairo-win32.h
gfx/cairo/win32-ddb-dib.patch
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -25,15 +25,17 @@ win32-logical-font-scale.patch: set CAIR
 nonfatal-assertions.patch: Make assertions non-fatal
 
 buggy-repeat.patch: Unconditionally turn on buggy-repeat handling to bandaid bug 413583.
 
 tmpfile_wince.patch: Make Windows CE use tmpfile() on windows mobile due to the lack of _open_osfhandle and no fs permissions.
 
 cairo-version-fixes.patch: fix up cairo-version.c/cairo-version.h for in-place builds
 
+win32-ddb-dib.patch: fix for bug 455513; not upstream yet pending feebdack
+
 ==== pixman patches ====
 
 endian.patch: include cairo-platform.h for endian macros
 
 ==== disable printing patch ====
 
 disable-printing.patch:  allows us to use NS_PRINTING to disable printing.
--- a/gfx/cairo/cairo/src/cairo-win32-private.h
+++ b/gfx/cairo/cairo/src/cairo-win32-private.h
@@ -112,16 +112,19 @@ enum {
     /* Whether we can use StretchBlt with this surface */
     CAIRO_WIN32_SURFACE_CAN_STRETCHBLT = (1<<4),
 
     /* Whether we can use StretchDIBits with this surface */
     CAIRO_WIN32_SURFACE_CAN_STRETCHDIB = (1<<5),
 
     /* Whether we can use GradientFill rectangles with this surface */
     CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6),
+
+    /* if this DDB surface can be converted to a DIB if necessary */
+    CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<7),
 };
 
 cairo_status_t
 _cairo_win32_print_gdi_error (const char *context);
 
 cairo_bool_t
 _cairo_surface_is_win32 (cairo_surface_t *surface);
 
--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -555,25 +555,86 @@ static cairo_status_t
 	FillRect(local->dc, &r, (HBRUSH)GetStockObject(WHITE_BRUSH));
     }
 
     *local_out = local;
 
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
+{
+    cairo_win32_surface_t *new_surface;
+    int width = surface->extents.width;
+    int height = surface->extents.height;
+
+    BOOL ok;
+    HBITMAP oldbitmap;
+
+    new_surface = (cairo_win32_surface_t*)
+	_cairo_win32_surface_create_for_dc (surface->dc,
+					    surface->format,
+					    width,
+					    height);
+
+    if (new_surface->base.status)
+	return;
+
+    /* DDB can't be 32bpp, so BitBlt is safe */
+    ok = BitBlt (new_surface->dc,
+		 0, 0, width, height,
+		 surface->dc,
+		 0, 0, SRCCOPY);
+
+    if (!ok)
+	goto out;
+
+    /* Now swap around new_surface and surface's internal bitmap
+     * pointers. */
+    DeleteDC (new_surface->dc);
+    new_surface->dc = NULL;
+
+    oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
+    DeleteObject (oldbitmap);
+
+    surface->image = new_surface->image;
+    surface->is_dib = new_surface->is_dib;
+    surface->bitmap = new_surface->bitmap;
+
+    new_surface->bitmap = NULL;
+    new_surface->image = NULL;
+
+    /* Finally update flags */
+    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
+
+  out:
+    cairo_surface_destroy ((cairo_surface_t*)new_surface);
+}
+
 static cairo_status_t
 _cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
 					   cairo_image_surface_t  **image_out,
 					   void                   **image_extra)
 {
     cairo_win32_surface_t *surface = abstract_surface;
     cairo_win32_surface_t *local = NULL;
     cairo_status_t status;
 
+    if (!surface->image && !surface->is_dib && surface->bitmap &&
+	(surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
+    {
+	/* This is a DDB, and we're being asked to use it as a source for
+	 * something that we couldn't support natively.  So turn it into
+	 * a DIB, so that we have an equivalent image surface, as long
+	 * as we're allowed to via flags.
+	 */
+	_cairo_win32_convert_ddb_to_dib (surface);
+    }
+
     if (surface->image) {
 	*image_out = (cairo_image_surface_t *)surface->image;
 	*image_extra = NULL;
 
 	return CAIRO_STATUS_SUCCESS;
     }
 
     status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
@@ -2128,8 +2189,66 @@ void
     for (z = 0; z < rd->rdh.nCount; z++) {
 	RECT r = ((RECT*)rd->Buffer)[z];
 	fprintf (stderr, " [%d]: [%d %d %d %d]\n", z, r.left, r.top, r.right - r.left, r.bottom - r.top);
     }
 
     free(rd);
     fflush (stderr);
 }
+
+/**
+ * cairo_win32_surface_set_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t indicating whether this surface can
+ *               be coverted to a DIB if necessary
+ *
+ * A DDB surface with this flag set can be converted to a DIB if it's
+ * used as a source in a way that GDI can't natively handle; for
+ * example, drawing a RGB24 DDB onto an ARGB32 DIB.  Doing this
+ * conversion results in a significant speed optimization, because we
+ * can call on pixman to perform the operation natively, instead of
+ * reading the data from the DC each time.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * changed, or an error otherwise.
+ * 
+ */
+cairo_status_t
+cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
+{
+    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    if (surface->bitmap) {
+	if (can_convert)
+	    surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+	else
+	    surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/**
+ * cairo_win32_surface_get_can_convert_to_dib
+ * @surface: a #cairo_surface_t
+ * @can_convert: a #cairo_bool_t* that receives the return value
+ *
+ * Returns the value of the flag indicating whether the surface can be
+ * converted to a DIB if necessary, as set by
+ * cairo_win32_surface_set_can_convert_to_dib.
+ *
+ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
+ * retreived, or an error otherwise.
+ * 
+ */
+cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
+{
+    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
+    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
+	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
+
+    *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
+    return CAIRO_STATUS_SUCCESS;
+}
--- a/gfx/cairo/cairo/src/cairo-win32.h
+++ b/gfx/cairo/cairo/src/cairo-win32.h
@@ -69,16 +69,22 @@ cairo_public cairo_surface_t *
 cairo_win32_surface_get_image (cairo_surface_t *surface);
 
 #if CAIRO_HAS_WIN32_FONT
 
 /*
  * Win32 font support
  */
 
+cairo_public cairo_status_t
+cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
+
+cairo_public cairo_status_t
+cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
+
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont);
 
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_hfont (HFONT font);
 
 cairo_public cairo_font_face_t *
 cairo_win32_font_face_create_for_logfontw_hfont (LOGFONTW *logfont, HFONT font);
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/win32-ddb-dib.patch
@@ -0,0 +1,182 @@
+b=455513; add optional flag to allow converting a DDB to a DIB internally, if the surface is every used as a source; r=jmuizelaar
+
+If a DDB is used as a source for an operation that can't be handled
+natively by GDI, we end up needing to take a really slow path (creating a
+temporary surface for acquire_source) for each operation.  If we convert
+the DDB to a DIB, we then end up having a real image buffer and can hand
+things off to pixman directly.
+
+This isn't the default mode because I'm not sure if there are cases where a
+DDB is explicitly needed (e.g. for printing), and it would change
+current cairo behaviour.  It might become the default at some point in the
+future.
+
+diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h
+--- a/gfx/cairo/cairo/src/cairo-win32-private.h
++++ b/gfx/cairo/cairo/src/cairo-win32-private.h
+@@ -117,6 +117,9 @@
+ 
+     /* Whether we can use GradientFill rectangles with this surface */
+     CAIRO_WIN32_SURFACE_CAN_RECT_GRADIENT = (1<<6),
++
++    /* if this DDB surface can be converted to a DIB if necessary */
++    CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB = (1<<7),
+ };
+ 
+ cairo_status_t
+diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
+@@ -560,6 +560,56 @@
+     return CAIRO_STATUS_SUCCESS;
+ }
+ 
++static void
++_cairo_win32_convert_ddb_to_dib (cairo_win32_surface_t *surface)
++{
++    cairo_win32_surface_t *new_surface;
++    int width = surface->extents.width;
++    int height = surface->extents.height;
++
++    BOOL ok;
++    HBITMAP oldbitmap;
++
++    new_surface = (cairo_win32_surface_t*)
++	_cairo_win32_surface_create_for_dc (surface->dc,
++					    surface->format,
++					    width,
++					    height);
++
++    if (new_surface->base.status)
++	return;
++
++    /* DDB can't be 32bpp, so BitBlt is safe */
++    ok = BitBlt (new_surface->dc,
++		 0, 0, width, height,
++		 surface->dc,
++		 0, 0, SRCCOPY);
++
++    if (!ok)
++	goto out;
++
++    /* Now swap around new_surface and surface's internal bitmap
++     * pointers. */
++    DeleteDC (new_surface->dc);
++    new_surface->dc = NULL;
++
++    oldbitmap = SelectObject (surface->dc, new_surface->bitmap);
++    DeleteObject (oldbitmap);
++
++    surface->image = new_surface->image;
++    surface->is_dib = new_surface->is_dib;
++    surface->bitmap = new_surface->bitmap;
++
++    new_surface->bitmap = NULL;
++    new_surface->image = NULL;
++
++    /* Finally update flags */
++    surface->flags = _cairo_win32_flags_for_dc (surface->dc);
++
++  out:
++    cairo_surface_destroy ((cairo_surface_t*)new_surface);
++}
++
+ static cairo_status_t
+ _cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
+ 					   cairo_image_surface_t  **image_out,
+@@ -568,6 +618,17 @@
+     cairo_win32_surface_t *surface = abstract_surface;
+     cairo_win32_surface_t *local = NULL;
+     cairo_status_t status;
++
++    if (!surface->image && !surface->is_dib && surface->bitmap &&
++	(surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0)
++    {
++	/* This is a DDB, and we're being asked to use it as a source for
++	 * something that we couldn't support natively.  So turn it into
++	 * a DIB, so that we have an equivalent image surface, as long
++	 * as we're allowed to via flags.
++	 */
++	_cairo_win32_convert_ddb_to_dib (surface);
++    }
+ 
+     if (surface->image) {
+ 	*image_out = (cairo_image_surface_t *)surface->image;
+@@ -2133,3 +2194,61 @@
+     free(rd);
+     fflush (stderr);
+ }
++
++/**
++ * cairo_win32_surface_set_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t indicating whether this surface can
++ *               be coverted to a DIB if necessary
++ *
++ * A DDB surface with this flag set can be converted to a DIB if it's
++ * used as a source in a way that GDI can't natively handle; for
++ * example, drawing a RGB24 DDB onto an ARGB32 DIB.  Doing this
++ * conversion results in a significant speed optimization, because we
++ * can call on pixman to perform the operation natively, instead of
++ * reading the data from the DC each time.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * changed, or an error otherwise.
++ * 
++ */
++cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t can_convert)
++{
++    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++    if (surface->bitmap) {
++	if (can_convert)
++	    surface->flags |= CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++	else
++	    surface->flags &= ~CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB;
++    }
++
++    return CAIRO_STATUS_SUCCESS;
++}
++
++/**
++ * cairo_win32_surface_get_can_convert_to_dib
++ * @surface: a #cairo_surface_t
++ * @can_convert: a #cairo_bool_t* that receives the return value
++ *
++ * Returns the value of the flag indicating whether the surface can be
++ * converted to a DIB if necessary, as set by
++ * cairo_win32_surface_set_can_convert_to_dib.
++ *
++ * Return value: %CAIRO_STATUS_SUCCESS if the flag was successfully
++ * retreived, or an error otherwise.
++ * 
++ */
++cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *asurface, cairo_bool_t *can_convert)
++{
++    cairo_win32_surface_t *surface = (cairo_win32_surface_t*) asurface;
++    if (surface->base.type != CAIRO_SURFACE_TYPE_WIN32)
++	return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
++
++    *can_convert = ((surface->flags & CAIRO_WIN32_SURFACE_CAN_CONVERT_TO_DIB) != 0);
++    return CAIRO_STATUS_SUCCESS;
++}
+diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h
+--- a/gfx/cairo/cairo/src/cairo-win32.h
++++ b/gfx/cairo/cairo/src/cairo-win32.h
+@@ -74,6 +74,12 @@
+  * Win32 font support
+  */
+ 
++cairo_public cairo_status_t
++cairo_win32_surface_set_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t can_convert);
++
++cairo_public cairo_status_t
++cairo_win32_surface_get_can_convert_to_dib (cairo_surface_t *surface, cairo_bool_t *can_convert);
++
+ cairo_public cairo_font_face_t *
+ cairo_win32_font_face_create_for_logfontw (LOGFONTW *logfont);
+