+--- a/src/cairo-quartz-font.c 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz-font.c 2012-11-13 18:06:56.000000000 -0800
+@@ -90,8 +90,9 @@ static int (*CGFontGetAscentPtr) (CGFont
+ static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
+ static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
+
+-/* Not public anymore in 64-bits nor in 10.7 */
+-static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
++/* CTFontCreateWithGraphicsFont is not public until 10.5. */
++typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
++static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
+
+ static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
+ static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
+@@ -130,7 +131,7 @@ quartz_font_ensure_symbols(void)
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+
+- FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
++ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
+
+ if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
+ CGFontGetGlyphBBoxesPtr &&
+@@ -155,6 +156,7 @@ struct _cairo_quartz_font_face {
+ cairo_font_face_t base;
+
+ CGFontRef cgFont;
++ CTFontRef ctFont;
+ };
+
+ /*
+@@ -239,6 +241,10 @@ _cairo_quartz_font_face_destroy (void *a
+ {
+ cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
+
++ if (font_face->ctFont) {
++ CFRelease (font_face->ctFont);
++ }
++
+ CGFontRelease (font_face->cgFont);
+ }
+
+@@ -363,6 +369,12 @@ cairo_quartz_font_face_create_for_cgfont
+
+ font_face->cgFont = CGFontRetain (font);
+
++ if (CTFontCreateWithGraphicsFontPtr) {
++ font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
++ } else {
++ font_face->ctFont = NULL;
++ }
++
+ _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
+
+ return &font_face->base;
+@@ -782,49 +794,10 @@ _cairo_quartz_scaled_font_get_cg_font_re
+ return ffont->cgFont;
+ }
+
+-/*
+- * compat with old ATSUI backend
+- */
+-
+-/**
+- * cairo_quartz_font_face_create_for_atsu_font_id
+- * @font_id: an ATSUFontID for the font.
+- *
+- * Creates a new font for the Quartz font backend based on an
+- * #ATSUFontID. This font can then be used with
+- * cairo_set_font_face() or cairo_scaled_font_create().
+- *
+- * Return value: a newly created #cairo_font_face_t. Free with
+- * cairo_font_face_destroy() when you are done using it.
+- *
+- * Since: 1.6
+- **/
+-cairo_font_face_t *
+-cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
+ {
+- quartz_font_ensure_symbols();
+-
+- if (FMGetATSFontRefFromFontPtr != NULL) {
+- ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
+- CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
+- cairo_font_face_t *ff;
+-
+- ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
+-
+- CGFontRelease (cgFont);
+-
+- return ff;
+- } else {
+- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+- return (cairo_font_face_t *)&_cairo_font_face_nil;
+- }
+-}
+-
+-/* This is the old name for the above function, exported for compat purposes */
+-cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
++ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
+
+-cairo_font_face_t *
+-cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+-{
+- return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
++ return ffont->ctFont;
+ }
+--- a/src/cairo-quartz-image-surface.c 2010-06-18 04:47:13.000000000 -0700
++++ b/src/cairo-quartz-image-surface.c 2012-11-13 18:06:56.000000000 -0800
+@@ -148,6 +148,8 @@ _cairo_quartz_image_surface_flush (void
+ surface->image = newImage;
+ CGImageRelease (oldImage);
+
++ surface->base.is_clear = surface->imageSurface->base.is_clear;
++
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+@@ -270,6 +272,8 @@ cairo_quartz_image_surface_create (cairo
+ qisurf->image = image;
+ qisurf->imageSurface = image_surface;
+
++ qisurf->base.is_clear = image_surface->base.is_clear;
++
+ return &qisurf->base;
+ }
+
+--- a/src/cairo-quartz-private.h 2010-12-25 06:21:34.000000000 -0800
++++ b/src/cairo-quartz-private.h 2012-11-13 18:06:56.000000000 -0800
+@@ -50,6 +50,9 @@ typedef CGFloat cairo_quartz_float_t;
+ typedef float cairo_quartz_float_t;
+ #endif
+
++/* define CTFontRef for pre-10.5 SDKs */
++typedef const struct __CTFont *CTFontRef;
++
+ typedef struct cairo_quartz_surface {
+ cairo_surface_t base;
+
+@@ -60,21 +63,22 @@ typedef struct cairo_quartz_surface {
+ cairo_surface_t *imageSurfaceEquiv;
+
+ cairo_surface_clipper_t clipper;
+- cairo_rectangle_int_t extents;
+
+- /* These are stored while drawing operations are in place, set up
+- * by quartz_setup_source() and quartz_finish_source()
++ /**
++ * 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.
+ */
+- CGAffineTransform sourceTransform;
++ CGImageRef bitmapContextImage;
+
+- CGImageRef sourceImage;
+- cairo_surface_t *sourceImageSurface;
+- CGRect sourceImageRect;
++ /**
++ * If non-null, this is the CGLayer for the surface.
++ */
++ CGLayerRef cgLayer;
+
+- CGShadingRef sourceShading;
+- CGPatternRef sourcePattern;
++ cairo_rectangle_int_t extents;
+
+- CGInterpolationQuality oldInterpolationQuality;
++ cairo_bool_t ownsData;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+@@ -103,6 +107,9 @@ _cairo_quartz_create_cgimage (cairo_form
+ CGFontRef
+ _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+
++CTFontRef
++_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
++
+ #else
+
+ # error Cairo was not compiled with support for the quartz backend
+--- a/src/cairo-quartz-surface.c 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz-surface.c 2012-11-13 18:06:56.000000000 -0800
+@@ -41,6 +41,8 @@
+
+ #include "cairo-error-private.h"
+ #include "cairo-surface-clipper-private.h"
++#include "cairo-gstate-private.h"
++#include "cairo-private.h"
+
+ #include <dlfcn.h>
+
+@@ -77,6 +79,11 @@
+ * This macro can be used to conditionally compile backend-specific code.
+ */
+
++/* Here are some of the differences between cairo and CoreGraphics
++ - cairo has only a single source active at once vs. CoreGraphics having
++ separate sources for stroke and fill
++*/
++
+ /* This method is private, but it exists. Its params are are exposed
+ * as args to the NS* method, but not as CG.
+ */
+@@ -126,6 +133,12 @@ static void (*CGContextSetShouldAntialia
+ static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
+ static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
++static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
++
++/* CTFontDrawGlyphs is not available until 10.7 */
++static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
++
++static SInt32 _cairo_quartz_osx_version = 0x0;
+
+ static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
+
+@@ -160,6 +173,14 @@ static void quartz_ensure_symbols(void)
+ CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
++ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
++
++ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
++
++ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
++ // assume 10.5
++ _cairo_quartz_osx_version = 0x1050;
++ }
+
+ _cairo_quartz_symbol_lookup_done = TRUE;
+ }
+@@ -430,6 +446,7 @@ _cairo_quartz_cairo_operator_to_quartz_c
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ default:
+ assert (0);
++ return kPrivateCGCompositeClear;
+ }
+ }
+
+@@ -598,10 +615,13 @@ _cairo_quartz_cairo_matrix_to_quartz (co
+ typedef struct {
+ bool isClipping;
+ CGGlyph *cg_glyphs;
+- CGSize *cg_advances;
++ union {
++ CGSize *cg_advances;
++ CGPoint *cg_positions;
++ } u;
+ size_t nglyphs;
+ CGAffineTransform textTransform;
+- CGFontRef font;
++ cairo_scaled_font_t *scaled_font;
+ CGPoint origin;
+ } unbounded_show_glyphs_t;
+
+@@ -679,12 +699,6 @@ _cairo_quartz_fixup_unbounded_operation
+ else
+ CGContextEOFillPath (cgc);
+ } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
+- CGContextSetFont (cgc, op->u.show_glyphs.font);
+- CGContextSetFontSize (cgc, 1.0);
+- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+- CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
+- CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
+-
+ if (op->u.show_glyphs.isClipping) {
+ /* Note that the comment in show_glyphs about kCGTextClip
+ * and the text transform still applies here; however, the
+@@ -693,12 +707,25 @@ _cairo_quartz_fixup_unbounded_operation
+ CGContextSetTextDrawingMode (cgc, kCGTextClip);
+ CGContextSaveGState (cgc);
+ }
++ CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
++ CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
++ if (CTFontDrawGlyphsPtr) {
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_positions,
++ op->u.show_glyphs.nglyphs,
++ cgc);
++ } else {
++ CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
++ CGContextSetFontSize (cgc, 1.0);
++ CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
++
++ CGContextShowGlyphsWithAdvances (cgc,
++ op->u.show_glyphs.cg_glyphs,
++ op->u.show_glyphs.u.cg_advances,
++ op->u.show_glyphs.nglyphs);
+
+- CGContextShowGlyphsWithAdvances (cgc,
+- op->u.show_glyphs.cg_glyphs,
+- op->u.show_glyphs.cg_advances,
+- op->u.show_glyphs.nglyphs);
+-
++ }
+ if (op->u.show_glyphs.isClipping) {
+ CGContextClearRect (cgc, clipBoxRound);
+ CGContextRestoreGState (cgc);
+@@ -1102,12 +1129,12 @@ DataProviderReleaseCallback (void *info,
+ {
+ quartz_source_image_t *source_img = info;
+ _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
++ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+ }
+
+ 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;
+@@ -1127,9 +1154,14 @@ _cairo_surface_to_cgimage (cairo_surface
+ }
+
+ 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;
++ }
+ }
+ }
+
+@@ -1137,10 +1169,11 @@ _cairo_surface_to_cgimage (cairo_surface
+ if (source_img == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+- source_img->surface = source;
++ source_img->surface = cairo_surface_reference(source);
+
+ status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
+ if (status) {
++ cairo_surface_destroy (source_img->surface);
+ free (source_img);
+ return status;
+ }
+@@ -1251,7 +1284,7 @@ _cairo_quartz_cairo_repeating_surface_pa
+ 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)
+@@ -1322,16 +1355,43 @@ typedef enum {
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
++ DO_TILED_IMAGE,
++ DO_LAYER,
+ DO_UNSUPPORTED,
+- DO_NOTHING,
+- DO_TILED_IMAGE
++ DO_NOTHING
+ } cairo_quartz_action_t;
+
+-static 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, 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
+ _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++ const cairo_pattern_t *source,
++ cairo_quartz_drawing_state_t *state)
+ {
+- CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
++ CGRect clipBox = CGContextGetClipBoundingBox (state->context);
+ double x0, y0, w, h;
+
+ cairo_surface_t *fallback;
+@@ -1340,8 +1400,10 @@ _cairo_quartz_setup_fallback_source (cai
+ cairo_status_t status;
+
+ if (clipBox.size.width == 0.0f ||
+- clipBox.size.height == 0.0f)
+- return DO_NOTHING;
++ clipBox.size.height == 0.0f) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+ x0 = floor(clipBox.origin.x);
+ y0 = floor(clipBox.origin.y);
+@@ -1384,18 +1446,21 @@ _cairo_quartz_setup_fallback_source (cai
+ }
+ #endif
+
+- status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
+-
+- surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
+- surface->sourceImage = img;
+- surface->sourceImageSurface = fallback;
+- surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
++ status = _cairo_surface_to_cgimage (fallback, &img);
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+- return DO_IMAGE;
++ state->imageRect = CGRectMake (0.0, 0.0, w, h);
++ state->image = img;
++ state->imageSurface = fallback;
++ state->transform = CGAffineTransformMakeTranslation (x0, y0);
++ state->action = DO_IMAGE;
+ }
+
+ /*
+@@ -1411,10 +1476,11 @@ based on the extents of the object (the
+ we don't want the rasterization of the entire gradient to depend on the
+ clip region).
+ */
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
+ const cairo_linear_pattern_t *lpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &lpat->base.base;
+ cairo_matrix_t mat;
+@@ -1424,9 +1490,10 @@ _cairo_quartz_setup_linear_source (cairo
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+
+ if (lpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (lpat->p1.x == lpat->p2.x &&
+@@ -1436,12 +1503,13 @@ _cairo_quartz_setup_linear_source (cairo
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * implementation to worry about.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+@@ -1461,21 +1529,22 @@ _cairo_quartz_setup_linear_source (cairo
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateAxial (rgb,
+- start, end,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateAxial (rgb,
++ start, end,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
++static void
+ _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
+ const cairo_radial_pattern_t *rpat,
+- cairo_rectangle_int_t *extents)
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+ const cairo_pattern_t *abspat = &rpat->base.base;
+ cairo_matrix_t mat;
+@@ -1494,9 +1563,10 @@ _cairo_quartz_setup_radial_source (cairo
+ double centerDistance = sqrt (dx*dx + dy*dy);
+
+ if (rpat->base.n_stops == 0) {
+- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- return DO_SOLID;
++ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
++ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
++ state->action = DO_SOLID;
++ return;
+ }
+
+ if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
+@@ -1507,12 +1577,13 @@ _cairo_quartz_setup_radial_source (cairo
+ * implementation to worry about.
+ * Note that this also catches the cases where r1 == r2.
+ */
+- return _cairo_quartz_setup_fallback_source (surface, abspat);
++ _cairo_quartz_setup_fallback_source (surface, abspat, state);
++ return;
+ }
+
+ mat = abspat->matrix;
+ cairo_matrix_invert (&mat);
+- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
++ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
+
+ rgb = CGColorSpaceCreateDeviceRGB();
+
+@@ -1531,90 +1602,79 @@ _cairo_quartz_setup_radial_source (cairo
+ extents);
+ }
+
+- surface->sourceShading = CGShadingCreateRadial (rgb,
+- start,
+- r1,
+- end,
+- r2,
+- gradFunc,
+- extend, extend);
++ state->shading = CGShadingCreateRadial (rgb,
++ start,
++ r1,
++ end,
++ r2,
++ gradFunc,
++ extend, extend);
+
+ CGColorSpaceRelease(rgb);
+ CGFunctionRelease(gradFunc);
+
+- return DO_SHADING;
++ state->action = DO_SHADING;
+ }
+
+-static cairo_quartz_action_t
+-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source,
+- cairo_rectangle_int_t *extents)
++static void
++_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
++ const cairo_surface_pattern_t *spat,
++ cairo_rectangle_int_t *extents,
++ cairo_quartz_drawing_state_t *state)
+ {
+- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+-
+- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
++ const cairo_pattern_t *source = &spat->base;
++ CGContextRef context = state->context;
+
+- if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+- cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+-
+- CGContextSetRGBStrokeColor (surface->cgContext,
+- solid->color.red,
+- solid->color.green,
+- solid->color.blue,
+- solid->color.alpha);
+- CGContextSetRGBFillColor (surface->cgContext,
+- solid->color.red,
+- solid->color.green,
+- solid->color.blue,
+- solid->color.alpha);
+-
+- return DO_SOLID;
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+- const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+- return _cairo_quartz_setup_linear_source (surface, lpat, extents);
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+- const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+- return _cairo_quartz_setup_radial_source (surface, rpat, extents);
+- }
+-
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+- (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
++ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
++ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
+ {
+- const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ cairo_surface_t *pat_surf = spat->surface;
+ CGImageRef img;
+ cairo_matrix_t m = spat->base.matrix;
+ cairo_rectangle_int_t extents;
+- cairo_status_t status;
+ CGAffineTransform xform;
+ CGRect srcRect;
+ cairo_fixed_t fw, fh;
+ cairo_bool_t is_bounded;
++ cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
++ cairo_status_t status;
+
+- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
++ cairo_matrix_invert(&m);
++ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
+
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
++ /* Draw nonrepeating CGLayer surface using DO_LAYER */
++ if (!repeat && 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) {
++ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
++ state->layer = quartz_surf->cgLayer;
++ state->action = DO_LAYER;
++ return;
++ }
++ }
++
++ status = _cairo_surface_to_cgimage (pat_surf, &img);
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
+
+- surface->sourceImage = img;
++ /* XXXroc what is this for? */
++ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+- cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
++ state->image = img;
+
+ is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
+ assert (is_bounded);
+
+- if (source->extend == CAIRO_EXTEND_NONE) {
+- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
+- return DO_IMAGE;
++ if (!repeat) {
++ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
++ state->action = DO_IMAGE;
++ return;
+ }
+
+ /* Quartz seems to tile images at pixel-aligned regions only -- this
+@@ -1624,8 +1684,8 @@ _cairo_quartz_setup_source (cairo_quartz
+ * epsilon), and if not, fall back to the CGPattern type.
+ */
+
+- xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
+- surface->sourceTransform);
++ xform = CGAffineTransformConcat (CGContextGetCTM (context),
++ state->transform);
+
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+@@ -1646,101 +1706,218 @@ _cairo_quartz_setup_source (cairo_quartz
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+- surface->sourceImageRect = srcRect;
+-
+- return DO_TILED_IMAGE;
++ state->imageRect = srcRect;
++ state->action = DO_TILED_IMAGE;
++ return;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+- if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+- cairo_quartz_float_t patternAlpha = 1.0f;
+- CGColorSpaceRef patternSpace;
+- CGPatternRef pattern;
+- cairo_int_status_t status;
+-
+- status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+- return DO_NOTHING;
+- if (status)
+- return DO_UNSUPPORTED;
+-
+- // 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(surface->cgContext);
+-
+- patternSpace = CGColorSpaceCreatePattern(NULL);
+- CGContextSetFillColorSpace (surface->cgContext, patternSpace);
+- CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
+- CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
+- CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
+- CGColorSpaceRelease (patternSpace);
+-
+- /* Quartz likes to munge the pattern phase (as yet unexplained
+- * why); force it to 0,0 as we've already baked in the correct
+- * pattern translation into the pattern matrix
+- */
+- CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
+-
+- surface->sourcePattern = pattern;
++ CGFloat patternAlpha = 1.0f;
++ CGColorSpaceRef patternSpace;
++ CGPatternRef pattern;
++ cairo_int_status_t status;
+
+- return DO_PATTERN;
++ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state->action = DO_NOTHING;
++ return;
++ }
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
+ }
+
+- return DO_UNSUPPORTED;
++ patternSpace = CGColorSpaceCreatePattern (NULL);
++ CGContextSetFillColorSpace (context, patternSpace);
++ CGContextSetFillPattern (context, pattern, &patternAlpha);
++ CGContextSetStrokeColorSpace (context, patternSpace);
++ CGContextSetStrokePattern (context, pattern, &patternAlpha);
++ CGColorSpaceRelease (patternSpace);
++
++ /* Quartz likes to munge the pattern phase (as yet unexplained
++ * why); force it to 0,0 as we've already baked in the correct
++ * pattern translation into the pattern matrix
++ */
++ CGContextSetPatternPhase (context, CGSizeMake(0,0));
++
++ state->pattern = pattern;
++ state->action = DO_PATTERN;
++ return;
+ }
+
++/**
++ * Call this before any operation that can modify the contents of a
++ * cairo_quartz_surface_t.
++ */
+ static void
+-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
+ {
+- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
++ if (surface->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++}
+
+- if (surface->sourceImage) {
+- CGImageRelease(surface->sourceImage);
+- surface->sourceImage = 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,
++ cairo_rectangle_int_t *extents)
++{
++ CGContextRef context = surface->cgContext;
++ cairo_quartz_drawing_state_t state;
++ cairo_status_t status;
+
+- cairo_surface_destroy(surface->sourceImageSurface);
+- surface->sourceImageSurface = NULL;
++ 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)
++ CGContextSaveGState(context);
++
++ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
++
++ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state.action = DO_NOTHING;
++ return state;
++ }
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
+ }
+
+- if (surface->sourceShading) {
+- CGShadingRelease(surface->sourceShading);
+- surface->sourceShading = NULL;
++ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
++ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
++
++ CGContextSetRGBStrokeColor (context,
++ solid->color.red,
++ solid->color.green,
++ solid->color.blue,
++ solid->color.alpha);
++ CGContextSetRGBFillColor (context,
++ solid->color.red,
++ solid->color.green,
++ solid->color.blue,
++ solid->color.alpha);
++
++ state.action = DO_SOLID;
++ return state;
++ }
++
++ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
++ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
++ _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
++ return state;
++ }
++
++ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
++ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
++ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
++ return state;
+ }
+
+- if (surface->sourcePattern) {
+- CGPatternRelease(surface->sourcePattern);
+- // To tear down the pattern and colorspace
+- CGContextRestoreGState(surface->cgContext);
++ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
++ if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) &&
++ CGContextGetAlphaPtr &&
++ CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
++ // Quartz won't touch pixels outside the bounds of the
++ // source surface, so we can just go ahead and use Copy here
++ // to accelerate things.
++ // Quartz won't necessarily be able to do this optimization internally;
++ // for CGLayer surfaces, we can know all the pixels are opaque
++ // (because it's CONTENT_COLOR), but Quartz won't know.
++ CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
++ }
+
+- surface->sourcePattern = NULL;
++ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
++ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
++ return state;
+ }
+-}
+
++ state.action = DO_UNSUPPORTED;
++ return state;
++}
+
++/**
++ * 1) Tears down internal state used to draw the source
++ * 2) Does CGContextRestoreGState(state->context)
++ */
+ static void
+-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
++_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
+ {
+- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
++ if (state->image) {
++ CGImageRelease(state->image);
++ }
+
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
+- CGContextScaleCTM (surface->cgContext, 1, -1);
+-
+- if (action == DO_IMAGE) {
+- CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+- if (!_cairo_operator_bounded_by_source(op)) {
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddRect (surface->cgContext, surface->sourceImageRect);
+- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
+- CGContextEOFillPath (surface->cgContext);
++ if (state->imageSurface) {
++ cairo_surface_destroy(state->imageSurface);
++ }
++
++ if (state->shading) {
++ CGShadingRelease(state->shading);
++ }
++
++ if (state->pattern) {
++ CGPatternRelease(state->pattern);
++ }
++
++ 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)) ||
++ (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_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.
++ */
++ CGContextSetInterpolationQuality (state->context, kCGInterpolationNone);
++ CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
++ state->layer);
++ } else {
++ CGContextDrawImage (state->context, state->imageRect, state->image);
++ }
++
++ /* disable this EXTEND_NONE correctness code because we use this path
++ * for both EXTEND_NONE and EXTEND_PAD */
++ if (0 && !_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 (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
++ }
+ }
+
+
+@@ -1762,6 +1939,7 @@ _cairo_quartz_get_image (cairo_quartz_su
+ }
+
+ if (surface->imageSurfaceEquiv) {
++ CGContextFlush(surface->cgContext);
+ *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
+ return CAIRO_STATUS_SUCCESS;
+ }
+@@ -1773,6 +1951,7 @@ _cairo_quartz_get_image (cairo_quartz_su
+ CGColorSpaceRef colorspace;
+ unsigned int color_comps;
+
++ CGContextFlush(surface->cgContext);
+ imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
+
+ #ifdef USE_10_3_WORKAROUNDS
+@@ -1860,53 +2039,79 @@ _cairo_quartz_surface_finish (void *abst
+
+ surface->cgContext = NULL;
+
++ if (surface->bitmapContextImage) {
++ CGImageRelease (surface->bitmapContextImage);
++ surface->bitmapContextImage = NULL;
++ }
++
+ if (surface->imageSurfaceEquiv) {
++ if (surface->ownsData)
++ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
+ cairo_surface_destroy (surface->imageSurfaceEquiv);
+ surface->imageSurfaceEquiv = NULL;
++ } else if (surface->imageData && surface->ownsData) {
++ free (surface->imageData);
+ }
+
+- if (surface->imageData) {
+- free (surface->imageData);
+- surface->imageData = NULL;
++ 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));
+-
+- status = _cairo_quartz_get_image (surface, image_out);
+- if (status)
+- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+-
+ *image_extra = NULL;
+
+- return CAIRO_STATUS_SUCCESS;
+-}
++ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
+
+-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;
++ status = _cairo_quartz_get_image (surface, image_out);
+
+- if (surface->imageSurfaceEquiv)
+- return NULL;
++ 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_FORMAT_ARGB32,
++ 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;
++ status = CAIRO_STATUS_SUCCESS;
++ } else {
++ cairo_surface_destroy (tmp);
++ }
++ }
+
+- status = _cairo_quartz_get_image (surface, &image);
+- if (unlikely (status))
+- return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
++ if (status)
++ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+- return &image->base;
++ return CAIRO_STATUS_SUCCESS;
+ }
+
+ static void
+@@ -1915,6 +2120,10 @@ _cairo_quartz_surface_release_source_ima
+ void *image_extra)
+ {
+ cairo_surface_destroy ((cairo_surface_t *) image);
++
++ if (image_extra) {
++ cairo_surface_destroy ((cairo_surface_t *) image_extra);
++ }
+ }
+
+
+@@ -1926,18 +2135,16 @@ _cairo_quartz_surface_acquire_dest_image
+ 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));
+
+- 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
+@@ -1947,11 +2154,31 @@ _cairo_quartz_surface_release_dest_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 *
+@@ -1960,10 +2187,13 @@ _cairo_quartz_surface_create_similar (vo
+ 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, content,
++ width, height);
++
+ if (content == CAIRO_CONTENT_COLOR_ALPHA)
+ format = CAIRO_FORMAT_ARGB32;
+ else if (content == CAIRO_CONTENT_COLOR)
+@@ -2027,7 +2257,7 @@ _cairo_quartz_surface_clone_similar (voi
+ }
+ }
+
+- 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;
+
+@@ -2087,7 +2317,7 @@ _cairo_quartz_surface_paint_cg (void *ab
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+
+ ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+
+@@ -2098,31 +2328,24 @@ _cairo_quartz_surface_paint_cg (void *ab
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
+- surface->extents.y,
+- surface->extents.width,
+- surface->extents.height));
+- } else if (action == DO_SHADING) {
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextSaveGState (surface->cgContext);
+- _cairo_quartz_draw_image (surface, op, action);
+- CGContextRestoreGState (surface->cgContext);
+- } else if (action != DO_NOTHING) {
++ 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 ||
++ state.action == DO_LAYER) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
++ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- paint\n"));
+ return rv;
+@@ -2186,7 +2409,7 @@ _cairo_quartz_surface_fill_cg (void *abs
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+@@ -2198,14 +2421,6 @@ _cairo_quartz_surface_fill_cg (void *abs
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+-
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ /* We don't need precise extents since these are only used to
+@@ -2213,46 +2428,47 @@ _cairo_quartz_surface_fill_cg (void *abs
+ object. */
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++
++ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+ if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextFillPath (surface->cgContext);
++ CGContextFillPath (state.context);
+ else
+- CGContextEOFillPath (surface->cgContext);
+- } else if (action == DO_SHADING) {
++ CGContextEOFillPath (state.context);
++ } else if (state.action == DO_SHADING) {
+
+ // we have to clip and then paint the shading; we can't fill
+ // with the shading
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
++ CGContextEOClip (state.context);
+
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
++ state.action == DO_LAYER) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
++ CGContextEOClip (state.context);
+
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action != DO_NOTHING) {
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+@@ -2319,7 +2535,7 @@ _cairo_quartz_surface_stroke_cg (void *a
+ {
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ CGAffineTransform origCTM, strokeTransform;
+ CGPathRef path_for_unbounded = NULL;
+
+@@ -2336,16 +2552,25 @@ _cairo_quartz_surface_stroke_cg (void *a
+ if (unlikely (rv))
+ return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+
++ if (_cairo_quartz_source_needs_extents (source))
++ {
++ cairo_rectangle_int_t path_extents;
++ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
++ } else {
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
++ }
++
+ // Turning antialiasing off used to cause misrendering with
+ // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
+ // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
+- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
+- CGContextSetLineWidth (surface->cgContext, style->line_width);
+- CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+- CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+- CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++ CGContextSetLineWidth (state.context, style->line_width);
++ CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
++ CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
++ CGContextSetMiterLimit (state.context, style->miter_limit);
+
+- origCTM = CGContextGetCTM (surface->cgContext);
++ origCTM = CGContextGetCTM (state.context);
+
+ if (style->dash && style->num_dashes) {
+ #define STATIC_DASH 32
+@@ -2368,72 +2593,62 @@ _cairo_quartz_surface_stroke_cg (void *a
+ if (fdash != sdash)
+ free (fdash);
+ } else
+- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
++ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+- CGContextSaveGState (surface->cgContext);
+
++ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
+
+- if (_cairo_quartz_source_needs_extents (source))
+- {
+- cairo_rectangle_int_t path_extents;
+- _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
+- action = _cairo_quartz_setup_source (surface, source, &path_extents);
+- } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
+- }
+-
+- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
++ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
++ CGContextConcatCTM (state.context, strokeTransform);
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextStrokePath (surface->cgContext);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextClip (surface->cgContext);
+-
+- CGContextSetCTM (surface->cgContext, origCTM);
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action != DO_NOTHING) {
++ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
++ CGContextStrokePath (state.context);
++ } 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);
++
++ CGContextSetCTM (state.context, origCTM);
++
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
++ goto BAIL;
+ }
+
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
+-
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
+
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddPath (surface->cgContext, path_for_unbounded);
++ CGContextBeginPath (state.context);
++ CGContextAddPath (state.context, path_for_unbounded);
+ CGPathRelease (path_for_unbounded);
+
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+- CGContextRestoreGState (surface->cgContext);
++ CGContextSaveGState (state.context);
++ CGContextConcatCTM (state.context, strokeTransform);
++ CGContextReplacePathWithStrokedPath (state.context);
++ CGContextRestoreGState (state.context);
+
+- ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
++ ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+ CGPathRelease (ub.u.stroke_fill.cgPath);
+ }
+
++ BAIL:
++ _cairo_quartz_teardown_state (&state);
++
+ ND((stderr, "-- stroke\n"));
+ return rv;
+ }
+@@ -2490,18 +2705,22 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
++ /* We'll use the cg_advances array for either advances or positions,
++ depending which API we're using to actually draw. The types involved
++ have the same size, so this is safe. */
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ cairo_rectangle_int_t glyph_extents;
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
+- cairo_quartz_action_t action;
++ cairo_quartz_drawing_state_t state;
+ cairo_quartz_float_t xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
++ cairo_antialias_t effective_antialiasing;
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+@@ -2516,54 +2735,51 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ if (unlikely (rv))
+ return rv;
+
+- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+- if (unlikely (rv))
+- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+-
+- CGContextSaveGState (surface->cgContext);
+-
+ if (_cairo_quartz_source_needs_extents (source) &&
+ !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
+ &glyph_extents, NULL))
+ {
+- action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
++ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
+ } else {
+- action = _cairo_quartz_setup_source (surface, source, NULL);
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
+- if (action == DO_SOLID || action == DO_PATTERN) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
+- CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
++ 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 || state.action == DO_LAYER) {
++ CGContextSetTextDrawingMode (state.context, kCGTextClip);
+ isClipping = TRUE;
+ } else {
+- if (action != DO_NOTHING)
++ if (state.action != DO_NOTHING)
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
+ }
+
+ /* this doesn't addref */
+ cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
+- CGContextSetFont (surface->cgContext, cgfref);
+- CGContextSetFontSize (surface->cgContext, 1.0);
++ CGContextSetFont (state.context, cgfref);
++ CGContextSetFontSize (state.context, 1.0);
++
++ effective_antialiasing = scaled_font->options.antialias;
+
+ switch (scaled_font->options.antialias) {
+ case CAIRO_ANTIALIAS_SUBPIXEL:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, TRUE);
+ if (CGContextSetAllowsFontSmoothingPtr &&
+- !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
++ !CGContextGetAllowsFontSmoothingPtr (state.context))
+ {
+ didForceFontSmoothing = TRUE;
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
++ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
+ }
+ break;
+ case CAIRO_ANTIALIAS_NONE:
+- CGContextSetShouldAntialias (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_GRAY:
+- CGContextSetShouldAntialias (surface->cgContext, TRUE);
+- CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
++ CGContextSetShouldAntialias (state.context, TRUE);
++ CGContextSetShouldSmoothFonts (state.context, FALSE);
+ break;
+ case CAIRO_ANTIALIAS_DEFAULT:
+ /* Don't do anything */
+@@ -2584,57 +2800,84 @@ _cairo_quartz_surface_show_glyphs_cg (vo
+ }
+ }
+
++ /* scale(1,-1) * scaled_font->scale */
+ textTransform = CGAffineTransformMake (scaled_font->scale.xx,
+ scaled_font->scale.yx,
+ -scaled_font->scale.xy,
+ -scaled_font->scale.yy,
+ 0, 0);
+- _cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform);
+
+- CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
++ /* scaled_font->scale_inverse * scale(1,-1) */
++ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
++ -scaled_font->scale_inverse.yx,
++ scaled_font->scale_inverse.xy,
++ -scaled_font->scale_inverse.yy,
++ 0.0, 0.0);
+
+- /* Convert our glyph positions to glyph advances. We need n-1 advances,
+- * since the advance at index 0 is applied after glyph 0. */
+- xprev = glyphs[0].x;
+- yprev = glyphs[0].y;
+-
+- cg_glyphs[0] = glyphs[0].index;
+-
+- for (i = 1; i < num_glyphs; i++) {
+- cairo_quartz_float_t xf = glyphs[i].x;
+- cairo_quartz_float_t yf = glyphs[i].y;
+- cg_glyphs[i] = glyphs[i].index;
+- cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+- xprev = xf;
+- yprev = yf;
+- }
++ CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
+
+ /* Translate to the first glyph's position before drawing */
+- ctm = CGContextGetCTM (surface->cgContext);
+- CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
+- CGContextConcatCTM (surface->cgContext, textTransform);
+-
+- CGContextShowGlyphsWithAdvances (surface->cgContext,
+- cg_glyphs,
+- cg_advances,
+- num_glyphs);
+-
+- CGContextSetCTM (surface->cgContext, ctm);
++ ctm = CGContextGetCTM (state.context);
++ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
++ CGContextConcatCTM (state.context, textTransform);
++
++ if (CTFontDrawGlyphsPtr) {
++ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
++ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
++ * fonts like Apple Color Emoji will render properly.
++ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
++ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
++
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ cairo_quartz_float_t origin_x = glyphs[0].x;
++ cairo_quartz_float_t origin_y = glyphs[0].y;
++
++ for (i = 0; i < num_glyphs; i++) {
++ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
++ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
++ cg_glyphs[i] = glyphs[i].index;
++ }
+
+- if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action == DO_SHADING) {
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
++ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
++ cg_glyphs, cg_positions, num_glyphs, state.context);
++ } else {
++ /* Convert our glyph positions to glyph advances. We need n-1 advances,
++ * since the advance at index 0 is applied after glyph 0. */
++ xprev = glyphs[0].x;
++ yprev = glyphs[0].y;
++
++ cg_glyphs[0] = glyphs[0].index;
++
++ for (i = 1; i < num_glyphs; i++) {
++ cairo_quartz_float_t xf = glyphs[i].x;
++ cairo_quartz_float_t yf = glyphs[i].y;
++ cg_glyphs[i] = glyphs[i].index;
++ cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
++ xprev = xf;
++ yprev = yf;
++ }
++
++ CGContextShowGlyphsWithAdvances (state.context,
++ cg_glyphs,
++ cg_advances,
++ num_glyphs);
++ }
++
++ CGContextSetCTM (state.context, ctm);
++
++ 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:
+- _cairo_quartz_teardown_source (surface, source);
+-
+ if (didForceFontSmoothing)
+- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
++ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
+
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (rv == CAIRO_STATUS_SUCCESS &&
+ cgfref &&
+@@ -2645,10 +2888,17 @@ BAIL:
+
+ ub.u.show_glyphs.isClipping = isClipping;
+ ub.u.show_glyphs.cg_glyphs = cg_glyphs;
+- ub.u.show_glyphs.cg_advances = cg_advances;
++ if (CTFontDrawGlyphsPtr) {
++ /* we're using Core Text API: the cg_advances array was
++ reused (above) for glyph positions */
++ CGPoint *cg_positions = (CGPoint*) cg_advances;
++ ub.u.show_glyphs.u.cg_positions = cg_positions;
++ } else {
++ ub.u.show_glyphs.u.cg_advances = cg_advances;
++ }
+ ub.u.show_glyphs.nglyphs = num_glyphs;
+ ub.u.show_glyphs.textTransform = textTransform;
+- ub.u.show_glyphs.font = cgfref;
++ ub.u.show_glyphs.scaled_font = scaled_font;
+ ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
+@@ -2717,7 +2967,7 @@ _cairo_quartz_surface_mask_with_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) {
+@@ -2820,7 +3070,9 @@ _cairo_quartz_surface_mask_cg (void *abs
+ if (unlikely (rv))
+ return rv;
+
+- if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
++ /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
++ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
++ op == CAIRO_OPERATOR_OVER) {
+ /* This is easy; we just need to paint with the alpha. */
+ cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+
+@@ -2834,8 +3086,11 @@ _cairo_quartz_surface_mask_cg (void *abs
+ /* If we have CGContextClipToMask, we can do more complex masks */
+ if (CGContextClipToMaskPtr) {
+ /* For these, we can skip creating a temporary surface, since we already have one */
+- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
++ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
++ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
++ mask->extend == CAIRO_EXTEND_NONE) {
+ return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
++ }
+
+ return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
+ }
+@@ -2920,13 +3175,24 @@ _cairo_quartz_surface_clipper_intersect_
+ return CAIRO_STATUS_SUCCESS;
+ }
+
++static cairo_status_t
++_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
++ int x, int y,
++ int width, int height)
++{
++ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
++ _cairo_quartz_surface_will_change (surface);
++ return CAIRO_STATUS_SUCCESS;
++}
++
++
+ // 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,
+@@ -2942,7 +3208,7 @@ static const struct _cairo_surface_backe
+ NULL, /* old_show_glyphs */
+ NULL, /* get_font_options */
+ NULL, /* flush */
+- NULL, /* mark_dirty_rectangle */
++ _cairo_quartz_surface_mark_dirty_rectangle,
+ NULL, /* scaled_font_fini */
+ NULL, /* scaled_glyph_fini */
+
+@@ -2952,7 +3218,7 @@ static const struct _cairo_surface_backe
+ _cairo_quartz_surface_fill,
+ _cairo_quartz_surface_show_glyphs,
+
+- _cairo_quartz_surface_snapshot,
++ NULL, /* snapshot */
+ NULL, /* is_similar */
+ NULL /* fill_stroke */
+ };
+@@ -3004,6 +3270,9 @@ _cairo_quartz_surface_create_internal (C
+
+ surface->imageData = NULL;
+ surface->imageSurfaceEquiv = NULL;
++ surface->bitmapContextImage = NULL;
++ surface->cgLayer = NULL;
++ surface->ownsData = TRUE;
+
+ return surface;
+ }
+@@ -3056,6 +3325,81 @@ cairo_quartz_surface_create_for_cg_conte
+ }
+
+ /**
++ * cairo_quartz_cglayer_surface_create_similar
++ * @surface: The returned surface can be efficiently drawn into this
++ * destination surface (if tiling is not used)."
++ * @content: the content type of the surface
++ * @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.
++ * 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,
++ cairo_content_t content,
++ 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, content,
++ 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, content,
++ 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);
++ CGContextSetInterpolationQuality (ctx, kCGInterpolationNone);
++ /* 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, content,
++ 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
+@@ -3075,13 +3419,93 @@ cairo_quartz_surface_create (cairo_forma
+ unsigned int width,
+ unsigned int height)
+ {
++ int stride;
++ unsigned char *data;
++
++ if (!_cairo_quartz_verify_surface_size(width, height))
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
++
++ if (width == 0 || height == 0) {
++ return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
++ width, height);
++ }
++
++ if (format == CAIRO_FORMAT_ARGB32 ||
++ format == CAIRO_FORMAT_RGB24)
++ {
++ stride = width * 4;
++ } else if (format == CAIRO_FORMAT_A8) {
++ stride = width;
++ } else if (format == CAIRO_FORMAT_A1) {
++ /* I don't think we can usefully support this, as defined by
++ * cairo_format_t -- these are 1-bit pixels stored in 32-bit
++ * quantities.
++ */
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
++ } else {
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
++ }
++
++ /* The Apple docs say that for best performance, the stride and the data
++ * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
++ * so we don't have to anything special on allocation.
++ */
++ stride = (stride + 15) & ~15;
++
++ data = _cairo_malloc_ab (height, stride);
++ if (!data) {
++ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
++ }
++
++ /* zero the memory to match the image surface behaviour */
++ memset (data, 0, height * stride);
++
++ cairo_quartz_surface_t *surf;
++ surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
++ (data, format, width, height, stride);
++ if (surf->base.status) {
++ free (data);
++ return (cairo_surface_t *) surf;
++ }
++
++ // We created this data, so we can delete it.
++ surf->ownsData = TRUE;
++
++ return (cairo_surface_t *) surf;
++}
++
++/**
++ * cairo_quartz_surface_create_for_data
++ * @data: a pointer to a buffer supplied by the application in which
++ * to write contents. This pointer must be suitably aligned for any
++ * kind of variable, (for example, a pointer returned by malloc).
++ * @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
++ * rendering, will succeed on this surface.
++ *
++ * Return value: the newly created surface.
++ *
++ * Since: 1.12
++ **/
++cairo_surface_t *
++cairo_quartz_surface_create_for_data (unsigned char *data,
++ cairo_format_t format,
++ unsigned int width,
++ unsigned int height,
++ unsigned int stride)
++{
+ cairo_quartz_surface_t *surf;
+ CGContextRef cgc;
+ CGColorSpaceRef cgColorspace;
+ CGBitmapInfo bitinfo;
+- void *imageData;
+- int stride;
++ void *imageData = data;
+ int bitsPerComponent;
++ unsigned int i;
+
+ // verify width and height of surface
+ if (!_cairo_quartz_verify_surface_size(width, height))
+@@ -3102,10 +3526,8 @@ cairo_quartz_surface_create (cairo_forma
+ else
+ bitinfo |= kCGImageAlphaNoneSkipFirst;
+ bitsPerComponent = 8;
+- stride = width * 4;
+ } else if (format == CAIRO_FORMAT_A8) {
+ cgColorspace = NULL;
+- stride = width;
+ bitinfo = kCGImageAlphaOnly;
+ bitsPerComponent = 8;
+ } else if (format == CAIRO_FORMAT_A1) {
+@@ -3118,21 +3540,6 @@ cairo_quartz_surface_create (cairo_forma
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+- /* The Apple docs say that for best performance, the stride and the data
+- * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
+- * so we don't have to anything special on allocation.
+- */
+- stride = (stride + 15) & ~15;
+-
+- imageData = _cairo_malloc_ab (height, stride);
+- if (!imageData) {
+- CGColorSpaceRelease (cgColorspace);
+- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+- }
+-
+- /* zero the memory to match the image surface behaviour */
+- memset (imageData, 0, height * stride);
+-
+ cgc = CGBitmapContextCreate (imageData,
+ width,
+ height,
+@@ -3161,7 +3568,19 @@ cairo_quartz_surface_create (cairo_forma
+ }
+
+ surf->imageData = imageData;
+- surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
++
++ cairo_surface_t* tmpImageSurfaceEquiv =
++ cairo_image_surface_create_for_data (imageData, format,
++ width, height, stride);
++
++ if (cairo_surface_status (tmpImageSurfaceEquiv)) {
++ // Tried & failed to create an imageSurfaceEquiv!
++ cairo_surface_destroy (tmpImageSurfaceEquiv);
++ surf->imageSurfaceEquiv = NULL;
++ } else {
++ surf->imageSurfaceEquiv = tmpImageSurfaceEquiv;
++ surf->ownsData = FALSE;
++ }
+
+ return (cairo_surface_t *) surf;
+ }
+@@ -3193,6 +3612,74 @@ _cairo_surface_is_quartz (const cairo_su
+ return surface->backend == &cairo_quartz_surface_backend;
+ }
+
++CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
++{
++
++ cairo_surface_t *surface = cr->gstate->target;
++ cairo_clip_t *clip = &cr->gstate->clip;
++ cairo_status_t status;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return NULL;
++
++ if (!clip->path) {
++ if (clip->all_clipped) {
++ /* Save the state before we set an empty clip rect so that
++ * our previous clip will be restored */
++
++ /* _cairo_surface_clipper_set_clip doesn't deal with
++ * clip->all_clipped because drawing is normally discarded earlier */
++ CGRect empty = {{0,0}, {0,0}};
++ CGContextClipToRect (quartz->cgContext, empty);
++ CGContextSaveGState (quartz->cgContext);
++
++ return quartz->cgContext;
++ }
++
++ /* an empty clip is represented by NULL */
++ clip = NULL;
++ }
++
++ status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
++
++ /* Save the state after we set the clip so that it persists
++ * after we restore */
++ CGContextSaveGState (quartz->cgContext);
++
++ if (unlikely (status))
++ return NULL;
++
++ return quartz->cgContext;
++}
++
++void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
++{
++ cairo_surface_t *surface = cr->gstate->target;
++
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
++
++ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
++ return;
++
++ CGContextRestoreGState (quartz->cgContext);
++}
++
++cairo_surface_t *
++cairo_quartz_surface_get_image (cairo_surface_t *surface)
++{
++ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
++ cairo_image_surface_t *image;
++
++ if (_cairo_quartz_get_image(quartz, &image))
++ return NULL;
++
++ return (cairo_surface_t *)image;
++}
++
+ /* Debug stuff */
+
+ #ifdef QUARTZ_DEBUG
+--- a/src/cairo-quartz.h 2012-11-13 18:20:00.000000000 -0800
++++ b/src/cairo-quartz.h 2012-11-13 18:06:56.000000000 -0800
+@@ -50,6 +50,19 @@ cairo_quartz_surface_create (cairo_forma
+ unsigned int height);
+
+ cairo_public cairo_surface_t *
++cairo_quartz_surface_create_for_data (unsigned char *data,
++ cairo_format_t format,
++ unsigned int width,
++ unsigned int height,
++ unsigned int stride);
++
++cairo_public cairo_surface_t *
++cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
++ cairo_content_t content,
++ 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);
+@@ -57,6 +70,15 @@ cairo_quartz_surface_create_for_cg_conte
+ cairo_public CGContextRef
+ cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+
++cairo_public CGContextRef
++cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
++
++cairo_public void
++cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
++
++cairo_public cairo_surface_t *
++cairo_quartz_surface_get_image (cairo_surface_t *surface);
++
+ #if CAIRO_HAS_QUARTZ_FONT
+
+ /*
+@@ -66,8 +88,10 @@ cairo_quartz_surface_get_cg_context (cai
+ cairo_public cairo_font_face_t *
+ cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
+
++#ifndef __LP64__
+ cairo_public cairo_font_face_t *
+ cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
++#endif
+
+ #endif /* CAIRO_HAS_QUARTZ_FONT */
\ No newline at end of file