summaryrefslogtreecommitdiffstats
path: root/gfx/cairo/quartz-cglayers.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/cairo/quartz-cglayers.patch')
-rw-r--r--gfx/cairo/quartz-cglayers.patch715
1 files changed, 715 insertions, 0 deletions
diff --git a/gfx/cairo/quartz-cglayers.patch b/gfx/cairo/quartz-cglayers.patch
new file mode 100644
index 000000000..bb3d44d9e
--- /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
+