Bug 568189. Add patches to list.
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 01 Jun 2010 14:09:44 +1200
changeset 42981 03a73eb7f3ec75d9a1234acba77656686ad25c8f
parent 42980 e0ead431e036974385635080b4e752e04d458cd1
child 42982 d1fd855f7ebe4dd07f505200b4e548ff6ac4214d
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs568189
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 568189. Add patches to list.
gfx/cairo/README
gfx/cairo/quartz-cglayers.patch
gfx/cairo/quartz-remove-snapshot.patch
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -47,32 +47,36 @@ wrap-source_image.patch: make sure we do
 zero-sized.patch: deal with zero sized surface in ways less likely to crash.
 
 text-path-filling-threshold.patch: use path filling instead of platform glyph rasterization at a smaller size threshold of 256 device pixels, if the backend supports native filling (which we assume will be fast).
 
 zombie-face.patch: bug 486974; leak and possible crash with @font-face{src:url()}.  Upstream commit: 0238fe2cafea2e1ed19bb222117bd73ee6898d4d
 
 win32-raster.patch: bug 498689; use scanline rasterizer on win32
 
-quartz-cache-CGImageRef.patch: cache CGImageRef for a CGBitmapContext; when we reuse it, Quartz will cache stuff, improving performance
-
 quartz-falback.patch: try to fix Quartz fallback-to-pixman path; possiby incorrect and obsoleted by Andrea Canciani patch
 
 quartz-repeating-radial-gradients.patch: use Quartz to render repeating radial gradients instead of falling back
 
 quartz-const-globals.patch: make some Quartz color function data const globals instead of local variables
 
 quartz-minimze-gradient-repeat.patch: reduce the number of gradient stop repetitions we use, to improve quality of Quartz's gradient rendering
 
 quartz-first-stop.patch: return the first stop for negative positions on the gradient line of a nonrepeating linear gradient
 
 quartz-glyph-extents.patch: bug 534260; work around incorrect glyph extents returned by quartz for anomalous empty glyphs
 
 quartz-state.patch: bug 522859; refactor cairo-quartz-surface so that state local to a drawing operation is stored in a cairo_quartz_drawing_state_t instead of the surface
 
+quartz-cache-CGImageRef.patch: cache CGImageRef for a CGBitmapContext; when we reuse it, Quartz will cache stuff, improving performance
+
+quartz-remove-snapshot.patch: remove broken implementation of backend snapshot
+
+quartz-cglayers.patch: add support for cairo surfaces backed by CGLayers
+
 premultiply-alpha-solid-gradients.patch: bug 539165; multiply the solid color by the alpha component before using it for a solid surface
 
 xlib-initialize-members.path: bug 548793; initialize XRender version if the server doesn't have the extension
 
 remove-comma: remove a comma from enum
 
 d2d.patch: add d2d support
 
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/quartz-cglayers.patch
@@ -0,0 +1,715 @@
+changeset:   42959:e1964291f8ff
+user:        Robert O'Callahan <robert@ocallahan.org>
+date:        Tue Jun 01 11:33:23 2010 +1200
+summary:     Bug 568189. Implement CGLayer-backed cairo-quartz surfaces. 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
+@@ -57,16 +57,21 @@ typedef struct cairo_quartz_surface {
+ 
+     /**
+      * 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;
+ 
++    /**
++     * If non-null, this is the CGLayer for the surface.
++     */
++    CGLayerRef cgLayer;
++
+     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
+@@ -1110,18 +1110,17 @@ CreateRepeatingRadialGradientFunction (c
+ static void
+ DataProviderReleaseCallback (void *info, const void *data, size_t size)
+ {
+     cairo_surface_t *surface = (cairo_surface_t *) info;
+     cairo_surface_destroy (surface);
+ }
+ 
+ static cairo_status_t
+-_cairo_surface_to_cgimage (cairo_surface_t *target,
+-			   cairo_surface_t *source,
++_cairo_surface_to_cgimage (cairo_surface_t *source,
+ 			   CGImageRef *image_out)
+ {
+     cairo_status_t status = CAIRO_STATUS_SUCCESS;
+     cairo_surface_type_t stype = cairo_surface_get_type (source);
+     cairo_image_surface_t *isurf;
+     CGImageRef image;
+     void *image_extra;
+ 
+@@ -1267,17 +1266,17 @@ _cairo_quartz_cairo_repeating_surface_pa
+ 	return CAIRO_INT_STATUS_UNSUPPORTED;
+ 
+     spattern = (cairo_surface_pattern_t *) apattern;
+     pat_surf = spattern->surface;
+ 
+     is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+     assert (is_bounded);
+ 
+-    status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
++    status = _cairo_surface_to_cgimage (pat_surf, &image);
+     if (status)
+ 	return status;
+     if (image == NULL)
+ 	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+ 
+     info = malloc(sizeof(SurfacePatternDrawInfo));
+     if (!info)
+ 	return CAIRO_STATUS_NO_MEMORY;
+@@ -1339,33 +1338,39 @@ _cairo_quartz_cairo_repeating_surface_pa
+ }
+ 
+ typedef enum {
+     DO_SOLID,
+     DO_SHADING,
+     DO_PATTERN,
+     DO_IMAGE,
+     DO_TILED_IMAGE,
++    DO_LAYER,
+     DO_UNSUPPORTED,
+     DO_NOTHING
+ } cairo_quartz_action_t;
+ 
+ /* State used during a drawing operation. */
+ typedef struct {
+     CGContextRef context;
+     cairo_quartz_action_t action;
+ 
+-    // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE
++    // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+     CGAffineTransform transform;
+ 
+     // Used with DO_IMAGE and DO_TILED_IMAGE
+     CGImageRef image;
+     cairo_surface_t *imageSurface;
++
++    // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+     CGRect imageRect;
+ 
++    // Used with DO_LAYER
++    CGLayerRef layer;
++
+     // Used with DO_SHADING
+     CGShadingRef shading;
+ 
+     // Used with DO_PATTERN
+     CGPatternRef pattern;
+ } cairo_quartz_drawing_state_t;
+ 
+ static void
+@@ -1423,17 +1428,17 @@ _cairo_quartz_setup_fallback_source (cai
+ 	_cairo_pattern_transform (&pattern.base,
+ 				  &fallback->device_transform_inverse);
+ 	status = _cairo_surface_paint (fallback,
+ 				       CAIRO_OPERATOR_SOURCE,
+ 				       &pattern.base, NULL);
+     }
+ #endif
+ 
+-    status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
++    status = _cairo_surface_to_cgimage (fallback, &img);
+     if (status) {
+         state->action = DO_UNSUPPORTED;
+ 	return;
+     }
+     if (img == NULL) {
+         state->action = DO_NOTHING;
+ 	return;
+     }
+@@ -1624,16 +1629,17 @@ _cairo_quartz_setup_state (cairo_quartz_
+ {
+     CGContextRef context = surface->cgContext;
+     cairo_quartz_drawing_state_t state;
+     cairo_status_t status;
+ 
+     state.context = context;
+     state.image = NULL;
+     state.imageSurface = NULL;
++    state.layer = 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)
+@@ -1689,33 +1695,43 @@ _cairo_quartz_setup_state (cairo_quartz_
+ 	CGImageRef img;
+ 	cairo_matrix_t m = spat->base.matrix;
+ 	cairo_rectangle_int_t extents;
+ 	CGAffineTransform xform;
+ 	CGRect srcRect;
+ 	cairo_fixed_t fw, fh;
+ 	cairo_bool_t is_bounded;
+ 
+-	status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
++        cairo_matrix_invert(&m);
++        _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
++
++        if (cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
++            cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
++            if (quartz_surf->cgLayer && source->extend == CAIRO_EXTEND_NONE) {
++         	state.imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
++                state.layer = quartz_surf->cgLayer;
++                state.action = DO_LAYER;
++                return state;
++            }
++        }
++
++	status = _cairo_surface_to_cgimage (pat_surf, &img);
+         if (status) {
+             state.action = DO_UNSUPPORTED;
+ 	    return state;
+         }
+         if (img == NULL) {
+             state.action = DO_NOTHING;
+ 	    return state;
+         }
+ 
+ 	CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+ 
+ 	state.image = img;
+ 
+-	cairo_matrix_invert(&m);
+-	_cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
+-
+ 	is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ 	assert (is_bounded);
+ 
+ 	if (source->extend == CAIRO_EXTEND_NONE) {
+ 	    state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
+ 	    state.action = DO_IMAGE;
+ 	    return state;
+ 	}
+@@ -1820,33 +1836,48 @@ _cairo_quartz_teardown_state (cairo_quar
+ 
+     CGContextRestoreGState(state->context);
+ }
+ 
+ 
+ static void
+ _cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+ {
+-    assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
++    assert (state &&
++            ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
++             (state->layer && state->action == DO_LAYER)));
+ 
+     CGContextConcatCTM (state->context, state->transform);
+     CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
+     CGContextScaleCTM (state->context, 1, -1);
+ 
+-    if (state->action == DO_IMAGE) {
+-	CGContextDrawImage (state->context, state->imageRect, state->image);
++    if (state->action == DO_TILED_IMAGE) {
++	CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
++	/* no need to worry about unbounded operators, since tiled images
++	   fill the entire clip region */
++    } else {
++        if (state->action == DO_LAYER) {
++            /* Note that according to Apple docs it's completely legal
++             * to draw a CGLayer to any CGContext, even one it wasn't
++             * created for.
++             */
++            CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
++                                       state->layer);
++        } else {
++            CGContextDrawImage (state->context, state->imageRect, state->image);
++        }
++
+ 	if (!_cairo_operator_bounded_by_source (op)) {
+ 	    CGContextBeginPath (state->context);
+ 	    CGContextAddRect (state->context, state->imageRect);
+ 	    CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
+ 	    CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
+ 	    CGContextEOFillPath (state->context);
+ 	}
+-    } else
+-	CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
++    }
+ }
+ 
+ 
+ /*
+  * get source/dest image implementation
+  */
+ 
+ /* Read the image from the surface's front buffer */
+@@ -1971,95 +2002,153 @@ _cairo_quartz_surface_finish (void *abst
+ 	surface->imageSurfaceEquiv = NULL;
+     }
+ 
+     if (surface->imageData) {
+ 	free (surface->imageData);
+ 	surface->imageData = NULL;
+     }
+ 
++    if (surface->cgLayer) {
++        CGLayerRelease (surface->cgLayer);
++    }
++
+     return CAIRO_STATUS_SUCCESS;
+ }
+ 
+ static cairo_status_t
+-_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
+-					     cairo_image_surface_t **image_out,
+-					     void **image_extra)
++_cairo_quartz_surface_acquire_image (void *abstract_surface,
++                                     cairo_image_surface_t **image_out,
++                                     void **image_extra)
+ {
+     cairo_int_status_t status;
+     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ 
+-    //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
++    *image_extra = NULL;
++
++    /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+ 
+     status = _cairo_quartz_get_image (surface, image_out);
++
++    if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
++        /* copy the layer into a Quartz bitmap context so we can get the data */
++        cairo_surface_t *tmp =
++            cairo_quartz_surface_create (CAIRO_CONTENT_COLOR_ALPHA,
++                                         surface->extents.width,
++                                         surface->extents.height);
++        cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
++
++        /* if surface creation failed, we won't have a Quartz surface here */
++        if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
++            tmp_surface->imageSurfaceEquiv) {
++            CGContextSaveGState (tmp_surface->cgContext);
++            CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
++            CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
++            /* Note that according to Apple docs it's completely legal
++             * to draw a CGLayer to any CGContext, even one it wasn't
++             * created for.
++             */
++            CGContextDrawLayerAtPoint (tmp_surface->cgContext,
++                                       CGPointMake (0.0, 0.0),
++                                       surface->cgLayer);
++            CGContextRestoreGState (tmp_surface->cgContext);
++
++            *image_out = (cairo_image_surface_t*)
++                cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
++            *image_extra = tmp;
++        } else {
++            cairo_surface_destroy (tmp);
++        }
++    }
++
+     if (status)
+ 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ 
+-    *image_extra = NULL;
+-
+     return CAIRO_STATUS_SUCCESS;
+ }
+ 
+ static void
+ _cairo_quartz_surface_release_source_image (void *abstract_surface,
+ 					     cairo_image_surface_t *image,
+ 					     void *image_extra)
+ {
+     cairo_surface_destroy ((cairo_surface_t *) image);
++
++    if (image_extra) {
++        cairo_surface_destroy ((cairo_surface_t *) image_extra);
++    }
+ }
+ 
+ 
+ static cairo_status_t
+ _cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
+ 					  cairo_rectangle_int_t *interest_rect,
+ 					  cairo_image_surface_t **image_out,
+ 					  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;
++    _cairo_quartz_surface_will_change (surface);
++
++    return _cairo_quartz_surface_acquire_image (abstract_surface,
++        image_out, image_extra);
+ }
+ 
+ static void
+ _cairo_quartz_surface_release_dest_image (void *abstract_surface,
+ 					  cairo_rectangle_int_t *interest_rect,
+ 					  cairo_image_surface_t *image,
+ 					  cairo_rectangle_int_t *image_rect,
+ 					  void *image_extra)
+ {
+-    //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+-
+-    //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
++    /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
+ 
+     cairo_surface_destroy ((cairo_surface_t *) image);
++
++    if (image_extra) {
++        /* we need to write the data from the temp surface back to the layer */
++        cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++        cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
++        CGImageRef img;
++        cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
++        if (status) {
++            cairo_surface_destroy (&tmp_surface->base);
++            return;
++        }
++
++        CGContextSaveGState (surface->cgContext);
++        CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
++        CGContextScaleCTM (surface->cgContext, 1, -1);
++        CGContextDrawImage (surface->cgContext,
++                            CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
++                            img);
++        CGContextRestoreGState (surface->cgContext);
++
++        cairo_surface_destroy (&tmp_surface->base);
++    }
+ }
+ 
+ static cairo_surface_t *
+ _cairo_quartz_surface_create_similar (void *abstract_surface,
+ 				       cairo_content_t content,
+ 				       int width,
+ 				       int height)
+ {
+-    /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
+-
++    cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+     cairo_format_t format;
+ 
++    if (surface->cgLayer)
++        return cairo_quartz_surface_create_cg_layer (abstract_surface, width, height);
++
+     if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ 	format = CAIRO_FORMAT_ARGB32;
+     else if (content == CAIRO_CONTENT_COLOR)
+ 	format = CAIRO_FORMAT_RGB24;
+     else if (content == CAIRO_CONTENT_ALPHA)
+ 	format = CAIRO_FORMAT_A8;
+     else
+ 	return NULL;
+@@ -2113,17 +2202,17 @@ _cairo_quartz_surface_clone_similar (voi
+ 		_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+ 						       qsurf->extents.width, qsurf->extents.height);
+ 	    *clone_offset_x = 0;
+ 	    *clone_offset_y = 0;
+ 	    return CAIRO_STATUS_SUCCESS;
+ 	}
+     }
+ 
+-    status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
++    status = _cairo_surface_to_cgimage (src, &quartz_image);
+     if (status)
+ 	return CAIRO_INT_STATUS_UNSUPPORTED;
+ 
+     new_format = CAIRO_FORMAT_ARGB32;  /* assumed */
+     if (_cairo_surface_is_image (src)) {
+ 	new_format = ((cairo_image_surface_t *) src)->format;
+     }
+ 
+@@ -2194,17 +2283,18 @@ _cairo_quartz_surface_paint (void *abstr
+     if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ 	CGContextFillRect (state.context, CGRectMake(surface->extents.x,
+ 						     surface->extents.y,
+ 						     surface->extents.width,
+ 						     surface->extents.height));
+     } else if (state.action == DO_SHADING) {
+ 	CGContextConcatCTM (state.context, state.transform);
+ 	CGContextDrawShading (state.context, state.shading);
+-    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++               state.action == DO_LAYER) {
+ 	_cairo_quartz_draw_image (&state, op);
+     } else if (state.action != DO_NOTHING) {
+ 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
+     }
+ 
+     _cairo_quartz_teardown_state (&state);
+ 
+     ND((stderr, "-- paint\n"));
+@@ -2291,17 +2381,18 @@ _cairo_quartz_surface_fill (void *abstra
+ 	// with the shading
+ 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ 	    CGContextClip (state.context);
+ 	else
+             CGContextEOClip (state.context);
+ 
+ 	CGContextConcatCTM (state.context, state.transform);
+ 	CGContextDrawShading (state.context, state.shading);
+-    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++               state.action == DO_LAYER) {
+ 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
+ 	    CGContextClip (state.context);
+ 	else
+ 	    CGContextEOClip (state.context);
+ 
+ 	_cairo_quartz_draw_image (&state, op);
+     } else if (state.action != DO_NOTHING) {
+ 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
+@@ -2416,17 +2507,18 @@ _cairo_quartz_surface_stroke (void *abst
+     if (rv)
+ 	goto BAIL;
+ 
+     if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+ 	path_for_unbounded = CGContextCopyPathPtr (state.context);
+ 
+     if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ 	CGContextStrokePath (state.context);
+-    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++               state.action == DO_LAYER) {
+ 	CGContextReplacePathWithStrokedPath (state.context);
+ 	CGContextClip (state.context);
+ 
+ 	CGContextSetCTM (state.context, origCTM);
+ 	_cairo_quartz_draw_image (&state, op);
+     } else if (state.action == DO_SHADING) {
+ 	CGContextReplacePathWithStrokedPath (state.context);
+ 	CGContextClip (state.context);
+@@ -2511,17 +2603,18 @@ _cairo_quartz_surface_show_glyphs (void 
+                                                  &glyph_extents, NULL);
+         state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+     } else {
+         state = _cairo_quartz_setup_state (surface, source, op, NULL);
+     }
+ 
+     if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ 	CGContextSetTextDrawingMode (state.context, kCGTextFill);
+-    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) {
++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++               state.action == DO_SHADING || state.action == DO_LAYER) {
+ 	CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ 	isClipping = TRUE;
+     } else {
+ 	if (state.action != DO_NOTHING)
+ 	    rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ 	goto BAIL;
+     }
+ 
+@@ -2622,17 +2715,18 @@ _cairo_quartz_surface_show_glyphs (void 
+ 
+     CGContextShowGlyphsWithAdvances (state.context,
+ 				     cg_glyphs,
+ 				     cg_advances,
+ 				     num_glyphs);
+ 
+     CGContextSetCTM (state.context, ctm);
+ 
+-    if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++    if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++        state.action == DO_LAYER) {
+ 	_cairo_quartz_draw_image (&state, op);
+     } else if (state.action == DO_SHADING) {
+ 	CGContextConcatCTM (state.context, state.transform);
+ 	CGContextDrawShading (state.context, state.shading);
+     }
+ 
+ BAIL:
+     if (didForceFontSmoothing)
+@@ -2679,17 +2773,17 @@ _cairo_quartz_surface_mask_with_surface 
+ 					 cairo_clip_t *clip)
+ {
+     CGRect rect;
+     CGImageRef img;
+     cairo_surface_t *pat_surf = mask->surface;
+     cairo_status_t status = CAIRO_STATUS_SUCCESS;
+     CGAffineTransform ctm, mask_matrix;
+ 
+-    status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
++    status = _cairo_surface_to_cgimage (pat_surf, &img);
+     if (status)
+ 	return status;
+     if (img == NULL) {
+ 	if (!_cairo_operator_bounded_by_mask (op))
+ 	    CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+ 	return CAIRO_STATUS_SUCCESS;
+     }
+ 
+@@ -2869,17 +2963,17 @@ _cairo_quartz_surface_clipper_intersect_
+ }
+ 
+ // XXXtodo implement show_page; need to figure out how to handle begin/end
+ 
+ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
+     CAIRO_SURFACE_TYPE_QUARTZ,
+     _cairo_quartz_surface_create_similar,
+     _cairo_quartz_surface_finish,
+-    _cairo_quartz_surface_acquire_source_image,
++    _cairo_quartz_surface_acquire_image,
+     _cairo_quartz_surface_release_source_image,
+     _cairo_quartz_surface_acquire_dest_image,
+     _cairo_quartz_surface_release_dest_image,
+     _cairo_quartz_surface_clone_similar,
+     NULL, /* composite */
+     NULL, /* fill_rectangles */
+     NULL, /* composite_trapezoids */
+     NULL, /* create_span_renderer */
+@@ -2950,16 +3044,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;
++    surface->cgLayer = 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
+@@ -3002,16 +3097,88 @@ cairo_quartz_surface_create_for_cg_conte
+ 	// create_internal will have set an error
+ 	return (cairo_surface_t*) surf;
+     }
+ 
+     return (cairo_surface_t *) surf;
+ }
+ 
+ /**
++ * cairo_quartz_cglayer_surface_create_similar
++ * @surface: The returned surface can be efficiently drawn into this
++ * destination surface (if tiling is not used)."
++ * @width: width of the surface, in pixels
++ * @height: height of the surface, in pixels
++ *
++ * Creates a Quartz surface backed by a CGLayer, if the given surface
++ * is a Quartz surface; the CGLayer is created to match the surface's
++ * Quartz context. Otherwise just calls cairo_surface_create_similar
++ * with CAIRO_CONTENT_COLOR_ALPHA.
++ * The returned surface can be efficiently blitted to the given surface,
++ * but tiling and 'extend' modes other than NONE are not so efficient.
++ *
++ * Return value: the newly created surface.
++ *
++ * Since: 1.10
++ **/
++cairo_surface_t *
++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
++                                      unsigned int width,
++                                      unsigned int height)
++{
++    cairo_quartz_surface_t *surf;
++    CGLayerRef layer;
++    CGContextRef ctx;
++    CGContextRef cgContext;
++
++    cgContext = cairo_quartz_surface_get_cg_context (surface);
++    if (!cgContext)
++        return cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
++                                             width, height);
++
++    if (!_cairo_quartz_verify_surface_size(width, height))
++        return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
++
++    /* If we pass zero width or height into CGLayerCreateWithContext below,
++     * it will fail.
++     */
++    if (width == 0 || height == 0) {
++        return (cairo_surface_t*)
++            _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
++                                                   width, height);
++    }
++
++    layer = CGLayerCreateWithContext (cgContext,
++                                      CGSizeMake (width, height),
++                                      NULL);
++    if (!layer)
++      return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
++
++    ctx = CGLayerGetContext (layer);
++    /* Flip it when we draw into it, so that when we finally composite it
++     * to a flipped target, the directions match and Quartz will optimize
++     * the composition properly
++     */
++    CGContextTranslateCTM (ctx, 0, height);
++    CGContextScaleCTM (ctx, 1, -1);
++
++    CGContextRetain (ctx);
++    surf = _cairo_quartz_surface_create_internal (ctx, CAIRO_CONTENT_COLOR_ALPHA,
++              width, height);
++    if (surf->base.status) {
++        CGLayerRelease (layer);
++        // create_internal will have set an error
++        return (cairo_surface_t*) surf;
++    }
++    surf->cgLayer = layer;
++
++    return (cairo_surface_t *) surf;
++}
++
++/**
+  * cairo_quartz_surface_create
+  * @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 Quartz surface backed by a CGBitmap.  The surface is
+  * created using the Device RGB (or Device Gray, for A8) color space.
+  * All Cairo operations, including those that require software
+diff --git a/gfx/cairo/cairo/src/cairo-quartz.h b/gfx/cairo/cairo/src/cairo-quartz.h
+--- a/gfx/cairo/cairo/src/cairo-quartz.h
++++ b/gfx/cairo/cairo/src/cairo-quartz.h
+@@ -45,16 +45,21 @@
+ CAIRO_BEGIN_DECLS
+ 
+ cairo_public cairo_surface_t *
+ cairo_quartz_surface_create (cairo_format_t format,
+                              unsigned int width,
+                              unsigned int height);
+ 
+ cairo_public cairo_surface_t *
++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
++                                      unsigned int width,
++                                      unsigned int height);
++
++cairo_public cairo_surface_t *
+ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
+                                             unsigned int width,
+                                             unsigned int height);
+ 
+ cairo_public CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+ 
+ cairo_public CGContextRef
+
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/quartz-remove-snapshot.patch
@@ -0,0 +1,62 @@
+changeset:   42958:dd0f1f0a96b3
+user:        Robert O'Callahan <robert@ocallahan.org>
+date:        Tue Jun 01 11:33:05 2010 +1200
+summary:     Bug 568189. Part 3: Remove snapshot backend function since it doesn't work and we may as well just fall back. r=jrmuizel
+
+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
+@@ -1993,33 +1993,16 @@ _cairo_quartz_surface_acquire_source_ima
+     if (status)
+ 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ 
+     *image_extra = NULL;
+ 
+     return CAIRO_STATUS_SUCCESS;
+ }
+ 
+-static cairo_surface_t *
+-_cairo_quartz_surface_snapshot (void *abstract_surface)
+-{
+-    cairo_int_status_t status;
+-    cairo_quartz_surface_t *surface = abstract_surface;
+-    cairo_image_surface_t *image;
+-
+-    if (surface->imageSurfaceEquiv)
+-	return NULL;
+-
+-    status = _cairo_quartz_get_image (surface, &image);
+-    if (unlikely (status))
+-        return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+-
+-    return &image->base;
+-}
+-
+ static void
+ _cairo_quartz_surface_release_source_image (void *abstract_surface,
+ 					     cairo_image_surface_t *image,
+ 					     void *image_extra)
+ {
+     cairo_surface_destroy ((cairo_surface_t *) image);
+ }
+ 
+@@ -2916,17 +2899,17 @@ static const struct _cairo_surface_backe
+     _cairo_quartz_surface_stroke,
+     _cairo_quartz_surface_fill,
+ #if CAIRO_HAS_QUARTZ_FONT
+     _cairo_quartz_surface_show_glyphs,
+ #else
+     NULL, /* show_glyphs */
+ #endif
+ 
+-    _cairo_quartz_surface_snapshot,
++    NULL, /* snapshot */
+     NULL, /* is_similar */
+     NULL  /* fill_stroke */
+ };
+ 
+ cairo_quartz_surface_t *
+ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
+ 					cairo_content_t content,
+ 					unsigned int width,
+