From 3679e3522bbc500572704811a51b424701d87a42 Mon Sep 17 00:00:00 2001 From: Vladimir Vukicevic Date: Sun, 9 Nov 2008 15:40:55 -0800 Subject: [PATCH] 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 | 2 + gfx/cairo/cairo/src/cairo-win32-private.h | 3 + gfx/cairo/cairo/src/cairo-win32-surface.c | 119 ++++++++++++++ gfx/cairo/cairo/src/cairo-win32.h | 6 + gfx/cairo/win32-ddb-dib.patch | 182 ++++++++++++++++++++++ 5 files changed, 312 insertions(+) create mode 100644 gfx/cairo/win32-ddb-dib.patch diff --git a/gfx/cairo/README b/gfx/cairo/README index 3a09edf8e1d..1a3110e8665 100644 --- a/gfx/cairo/README +++ b/gfx/cairo/README @@ -30,6 +30,8 @@ tmpfile_wince.patch: Make Windows CE use tmpfile() on windows mobile due to the 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 diff --git a/gfx/cairo/cairo/src/cairo-win32-private.h b/gfx/cairo/cairo/src/cairo-win32-private.h index a87287efb5e..8e859ef495e 100644 --- a/gfx/cairo/cairo/src/cairo-win32-private.h +++ b/gfx/cairo/cairo/src/cairo-win32-private.h @@ -117,6 +117,9 @@ enum { /* 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 index a0031a06044..ccfaa1ddb9f 100644 --- a/gfx/cairo/cairo/src/cairo-win32-surface.c +++ b/gfx/cairo/cairo/src/cairo-win32-surface.c @@ -560,6 +560,56 @@ _cairo_win32_surface_get_subimage (cairo_win32_surface_t *surface, 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, @@ -569,6 +619,17 @@ _cairo_win32_surface_acquire_source_image (void *abstract_sur 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; @@ -2133,3 +2194,61 @@ _cairo_win32_debug_dump_hrgn (HRGN rgn, char *header) 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 index cb517bc87bd..c22ef8abd55 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -74,6 +74,12 @@ cairo_win32_surface_get_image (cairo_surface_t *surface); * 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); diff --git a/gfx/cairo/win32-ddb-dib.patch b/gfx/cairo/win32-ddb-dib.patch new file mode 100644 index 00000000000..05a203f98eb --- /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); +