michael@0: changeset: 42954:7881873b2b5d michael@0: user: Robert O'Callahan michael@0: date: Tue Jun 01 11:19:45 2010 +1200 michael@0: summary: Bug 552537. Cache the CGImageRef that we create for a CGBitmapContext so that we can take advantage of Quartz caching optimizations. r=jrmuizel michael@0: michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: @@ -49,16 +49,24 @@ typedef struct cairo_quartz_surface { michael@0: michael@0: CGContextRef cgContext; michael@0: CGAffineTransform cgContextBaseCTM; michael@0: michael@0: void *imageData; michael@0: cairo_surface_t *imageSurfaceEquiv; michael@0: michael@0: cairo_surface_clipper_t clipper; michael@0: + michael@0: + /** michael@0: + * If non-null, this is a CGImage representing the contents of the surface. michael@0: + * We clear this out before any painting into the surface, so that we michael@0: + * don't force a copy to be created. michael@0: + */ michael@0: + CGImageRef bitmapContextImage; michael@0: + michael@0: cairo_rectangle_int_t extents; michael@0: } cairo_quartz_surface_t; michael@0: michael@0: typedef struct cairo_quartz_image_surface { michael@0: cairo_surface_t base; michael@0: michael@0: cairo_rectangle_int_t extents; michael@0: michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: @@ -1134,19 +1134,24 @@ _cairo_surface_to_cgimage (cairo_surface michael@0: if (stype == CAIRO_SURFACE_TYPE_QUARTZ) { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source; michael@0: if (IS_EMPTY(surface)) { michael@0: *image_out = NULL; michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) { michael@0: - *image_out = CGBitmapContextCreateImage (surface->cgContext); michael@0: - if (*image_out) michael@0: - return CAIRO_STATUS_SUCCESS; michael@0: + if (!surface->bitmapContextImage) { michael@0: + surface->bitmapContextImage = michael@0: + CGBitmapContextCreateImage (surface->cgContext); michael@0: + } michael@0: + if (surface->bitmapContextImage) { michael@0: + *image_out = CGImageRetain (surface->bitmapContextImage); michael@0: + return CAIRO_STATUS_SUCCESS; michael@0: + } michael@0: } michael@0: } michael@0: michael@0: if (stype != CAIRO_SURFACE_TYPE_IMAGE) { michael@0: status = _cairo_surface_acquire_source_image (source, michael@0: &isurf, &image_extra); michael@0: if (status) michael@0: return status; michael@0: @@ -1589,16 +1594,29 @@ _cairo_quartz_setup_radial_source (cairo michael@0: michael@0: CGColorSpaceRelease(rgb); michael@0: CGFunctionRelease(gradFunc); michael@0: michael@0: state->action = DO_SHADING; michael@0: } michael@0: michael@0: /** michael@0: + * Call this before any operation that can modify the contents of a michael@0: + * cairo_quartz_surface_t. michael@0: + */ michael@0: +static void michael@0: +_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface) michael@0: +{ michael@0: + if (surface->bitmapContextImage) { michael@0: + CGImageRelease (surface->bitmapContextImage); michael@0: + surface->bitmapContextImage = NULL; michael@0: + } michael@0: +} michael@0: + michael@0: +/** michael@0: * Sets up internal state to be used to draw the source mask, stored in michael@0: * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on michael@0: * surface->cgContext. michael@0: */ michael@0: static cairo_quartz_drawing_state_t michael@0: _cairo_quartz_setup_state (cairo_quartz_surface_t *surface, michael@0: const cairo_pattern_t *source, michael@0: cairo_operator_t op, michael@0: @@ -1609,16 +1627,18 @@ _cairo_quartz_setup_state (cairo_quartz_ michael@0: cairo_status_t status; michael@0: michael@0: state.context = context; michael@0: state.image = NULL; michael@0: state.imageSurface = NULL; michael@0: state.shading = NULL; michael@0: state.pattern = NULL; michael@0: michael@0: + _cairo_quartz_surface_will_change (surface); michael@0: + michael@0: // Save before we change the pattern, colorspace, etc. so that michael@0: // we can restore and make sure that quartz releases our michael@0: // pattern (which may be stack allocated) michael@0: CGContextSaveGState(context); michael@0: michael@0: CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter)); michael@0: michael@0: status = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: @@ -1936,16 +1956,21 @@ _cairo_quartz_surface_finish (void *abst michael@0: /* Restore our saved gstate that we use to reset clipping */ michael@0: CGContextRestoreGState (surface->cgContext); michael@0: _cairo_surface_clipper_reset (&surface->clipper); michael@0: michael@0: CGContextRelease (surface->cgContext); michael@0: michael@0: surface->cgContext = NULL; michael@0: michael@0: + if (surface->bitmapContextImage) { michael@0: + CGImageRelease (surface->bitmapContextImage); michael@0: + surface->bitmapContextImage = NULL; michael@0: + } michael@0: + michael@0: if (surface->imageSurfaceEquiv) { michael@0: cairo_surface_destroy (surface->imageSurfaceEquiv); michael@0: surface->imageSurfaceEquiv = NULL; michael@0: } michael@0: michael@0: if (surface->imageData) { michael@0: free (surface->imageData); michael@0: surface->imageData = NULL; michael@0: @@ -2006,16 +2031,18 @@ _cairo_quartz_surface_acquire_dest_image michael@0: cairo_rectangle_int_t *image_rect, michael@0: void **image_extra) michael@0: { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_int_status_t status; michael@0: michael@0: ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface)); michael@0: michael@0: + _cairo_quartz_surface_will_change (surface); michael@0: + michael@0: status = _cairo_quartz_get_image (surface, image_out); michael@0: if (status) michael@0: return _cairo_error (CAIRO_STATUS_NO_MEMORY); michael@0: michael@0: *image_rect = surface->extents; michael@0: *image_extra = NULL; michael@0: michael@0: return CAIRO_STATUS_SUCCESS; michael@0: @@ -2939,16 +2966,17 @@ _cairo_quartz_surface_create_internal (C michael@0: */ michael@0: CGContextSaveGState (cgContext); michael@0: michael@0: surface->cgContext = cgContext; michael@0: surface->cgContextBaseCTM = CGContextGetCTM (cgContext); michael@0: michael@0: surface->imageData = NULL; michael@0: surface->imageSurfaceEquiv = NULL; michael@0: + surface->bitmapContextImage = NULL; michael@0: michael@0: return surface; michael@0: } michael@0: michael@0: /** michael@0: * cairo_quartz_surface_create_for_cg_context michael@0: * @cgContext: the existing CGContext for which to create the surface michael@0: * @width: width of the surface, in pixels michael@0: