changeset:   42954:7881873b2b5d
user:        Robert O'Callahan <robert@ocallahan.org>
date:        Tue Jun 01 11:19:45 2010 +1200
summary:     Bug 552537. Cache the CGImageRef that we create for a CGBitmapContext so that we can take advantage of Quartz caching optimizations. r=jrmuizel

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