summaryrefslogtreecommitdiffstats
path: root/gfx/cairo/quartz-state.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/cairo/quartz-state.patch')
-rw-r--r--gfx/cairo/quartz-state.patch1190
1 files changed, 1190 insertions, 0 deletions
diff --git a/gfx/cairo/quartz-state.patch b/gfx/cairo/quartz-state.patch
new file mode 100644
index 000000000..d4c04f706
--- /dev/null
+++ b/gfx/cairo/quartz-state.patch
@@ -0,0 +1,1190 @@
+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
+@@ -50,30 +50,16 @@ typedef struct cairo_quartz_surface {
+ CGContextRef cgContext;
+ CGAffineTransform cgContextBaseCTM;
+
+ void *imageData;
+ 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()
+- */
+- CGAffineTransform sourceTransform;
+-
+- CGImageRef sourceImage;
+- cairo_surface_t *sourceImageSurface;
+- CGRect sourceImageRect;
+-
+- CGShadingRef sourceShading;
+- CGPatternRef sourcePattern;
+-
+- CGInterpolationQuality oldInterpolationQuality;
+ } cairo_quartz_surface_t;
+
+ typedef struct cairo_quartz_image_surface {
+ cairo_surface_t base;
+
+ cairo_rectangle_int_t extents;
+
+ CGImageRef image;
+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
+@@ -1333,36 +1333,59 @@ _cairo_quartz_cairo_repeating_surface_pa
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ typedef enum {
+ DO_SOLID,
+ DO_SHADING,
+ DO_PATTERN,
+ DO_IMAGE,
++ DO_TILED_IMAGE,
+ 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 and DO_TILED_IMAGE
++ CGAffineTransform transform;
++
++ // Used with DO_IMAGE and DO_TILED_IMAGE
++ CGImageRef image;
++ cairo_surface_t *imageSurface;
++ CGRect imageRect;
++
++ // 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;
+ CGImageRef img;
+
+ 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);
+ w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
+ h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
+
+ /* Create a temporary the size of the clip surface, and position
+ * it so that the device origin coincides with the original surface */
+@@ -1396,73 +1419,79 @@ _cairo_quartz_setup_fallback_source (cai
+ &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);
+- 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);
+-
+- return DO_IMAGE;
++ if (status) {
++ state->action = DO_UNSUPPORTED;
++ return;
++ }
++ if (img == NULL) {
++ state->action = DO_NOTHING;
++ return;
++ }
++
++ state->imageRect = CGRectMake (0.0, 0.0, w, h);
++ state->image = img;
++ state->imageSurface = fallback;
++ state->transform = CGAffineTransformMakeTranslation (x0, y0);
++ state->action = DO_IMAGE;
+ }
+
+ /*
+ Quartz does not support repeating radients. We handle repeating gradients
+ by manually extending the gradient and repeating color stops. We need to
+ minimize the number of repetitions since Quartz seems to sample our color
+ function across the entire range, even if part of that range is not needed
+ for the visible area of the gradient, and it samples with some fixed resolution,
+ so if the gradient range is too large it samples with very low resolution and
+ the gradient is very coarse. CreateRepeatingLinearGradientFunction and
+ CreateRepeatingRadialGradientFunction compute the number of repetitions needed
+ based on the extents of the object (the clip region cannot be used here since
+ 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;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ 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 &&
+ lpat->p1.y == lpat->p2.y) {
+ /* Quartz handles cases where the vector has no length very
+ * differently from pixman.
+ * 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();
+
+ start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
+ _cairo_fixed_to_double (lpat->p1.y));
+ end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
+ _cairo_fixed_to_double (lpat->p2.y));
+
+@@ -1472,31 +1501,32 @@ _cairo_quartz_setup_linear_source (cairo
+ gradFunc = CreateGradientFunction (&lpat->base);
+ } else {
+ gradFunc = CreateRepeatingLinearGradientFunction (surface,
+ &lpat->base,
+ &start, &end,
+ 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;
+ CGPoint start, end;
+ CGFunctionRef gradFunc;
+ CGColorSpaceRef rgb;
+ bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+ double c1x = _cairo_fixed_to_double (rpat->c1.x);
+@@ -1505,35 +1535,37 @@ _cairo_quartz_setup_radial_source (cairo
+ double c2y = _cairo_fixed_to_double (rpat->c2.y);
+ double r1 = _cairo_fixed_to_double (rpat->r1);
+ double r2 = _cairo_fixed_to_double (rpat->r2);
+ double dx = c1x - c2x;
+ double dy = c1y - c2y;
+ 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 */
+ r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
+ /* Quartz handles cases where neither circle contains the other very
+ * differently from pixman.
+ * Whatever the correct behaviour is, let's at least have only pixman's
+ * 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();
+
+ start = CGPointMake (c1x, c1y);
+ end = CGPointMake (c2x, c2y);
+
+ if (abspat->extend == CAIRO_EXTEND_NONE ||
+ abspat->extend == CAIRO_EXTEND_PAD)
+@@ -1542,111 +1574,146 @@ _cairo_quartz_setup_radial_source (cairo
+ } else {
+ gradFunc = CreateRepeatingRadialGradientFunction (surface,
+ &rpat->base,
+ &start, &r1,
+ &end, &r2,
+ 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)
++/**
++ * 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)
+ {
+- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+-
+- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
+- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
++ CGContextRef context = surface->cgContext;
++ cairo_quartz_drawing_state_t state;
++ cairo_status_t status;
++
++ state.context = context;
++ state.image = NULL;
++ state.imageSurface = NULL;
++ state.shading = NULL;
++ state.pattern = NULL;
++
++ // 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 (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+- CGContextSetRGBStrokeColor (surface->cgContext,
++ CGContextSetRGBStrokeColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+- CGContextSetRGBFillColor (surface->cgContext,
++ CGContextSetRGBFillColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+- return DO_SOLID;
++ 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;
+- return _cairo_quartz_setup_linear_source (surface, lpat, extents);
++ _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;
+- return _cairo_quartz_setup_radial_source (surface, rpat, extents);
++ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
++ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ (source->extend == CAIRO_EXTEND_NONE || (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;
+
+ status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
+- if (status)
+- return DO_UNSUPPORTED;
+- if (img == NULL)
+- return DO_NOTHING;
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
++ }
++ if (img == NULL) {
++ state.action = DO_NOTHING;
++ return state;
++ }
+
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+
+- surface->sourceImage = img;
++ state.image = img;
+
+ cairo_matrix_invert(&m);
+- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
++ _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) {
+- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
+- return DO_IMAGE;
++ state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
++ state.action = DO_IMAGE;
++ return state;
+ }
+
+ /* Quartz seems to tile images at pixel-aligned regions only -- this
+ * leads to seams if the image doesn't end up scaling to fill the
+ * space exactly. The CGPattern tiling approach doesn't have this
+ * problem. Check if we're going to fill up the space (within some
+ * 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);
+
+ fw = _cairo_fixed_from_double (srcRect.size.width);
+ fh = _cairo_fixed_from_double (srcRect.size.height);
+
+ if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
+@@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz
+
+ srcRect.size.width = round(srcRect.size.width);
+ srcRect.size.height = round(srcRect.size.height);
+
+ xform = CGAffineTransformInvert (xform);
+
+ srcRect = CGRectApplyAffineTransform (srcRect, xform);
+
+- surface->sourceImageRect = srcRect;
+-
+- return DO_TILED_IMAGE;
++ state.imageRect = srcRect;
++ state.action = DO_TILED_IMAGE;
++ return state;
+ }
+
+ /* Fall through to generic SURFACE case */
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ CGFloat 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);
++ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
++ state.action = DO_NOTHING;
++ return state;
++ }
++ if (status) {
++ state.action = DO_UNSUPPORTED;
++ return state;
++ }
++
++ 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 (surface->cgContext, CGSizeMake(0,0));
+-
+- surface->sourcePattern = pattern;
+-
+- return DO_PATTERN;
++ CGContextSetPatternPhase (context, CGSizeMake(0,0));
++
++ state.pattern = pattern;
++ state.action = DO_PATTERN;
++ return state;
+ }
+
+- return DO_UNSUPPORTED;
++ 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_teardown_source (cairo_quartz_surface_t *surface,
+- const cairo_pattern_t *source)
++_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
+ {
+- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
+-
+- if (surface->sourceImage) {
+- CGImageRelease(surface->sourceImage);
+- surface->sourceImage = NULL;
+-
+- cairo_surface_destroy(surface->sourceImageSurface);
+- surface->sourceImageSurface = NULL;
++ if (state->image) {
++ CGImageRelease(state->image);
+ }
+
+- if (surface->sourceShading) {
+- CGShadingRelease(surface->sourceShading);
+- surface->sourceShading = NULL;
++ if (state->imageSurface) {
++ cairo_surface_destroy(state->imageSurface);
+ }
+
+- if (surface->sourcePattern) {
+- CGPatternRelease(surface->sourcePattern);
+- // To tear down the pattern and colorspace
+- CGContextRestoreGState(surface->cgContext);
+-
+- surface->sourcePattern = NULL;
++ if (state->shading) {
++ CGShadingRelease(state->shading);
+ }
++
++ if (state->pattern) {
++ CGPatternRelease(state->pattern);
++ }
++
++ 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_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+ {
+- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_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);
++ assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
++
++ 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 (!_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);
++ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
+ }
+
+
+ /*
+ * get source/dest image implementation
+ */
+
+ /* Read the image from the surface's front buffer */
+@@ -2098,52 +2163,44 @@ _cairo_quartz_surface_get_extents (void
+ static cairo_int_status_t
+ _cairo_quartz_surface_paint (void *abstract_surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
+ {
+ 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));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ 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;
+-
+- 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) {
++ state = _cairo_quartz_setup_state (surface, source, op, NULL);
++
++ 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) {
++ _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;
+ }
+
+ static cairo_bool_t
+ _cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
+ {
+@@ -2170,91 +2227,83 @@ _cairo_quartz_surface_fill (void *abstra
+ cairo_path_fixed_t *path,
+ cairo_fill_rule_t fill_rule,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ 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;
+ quartz_stroke_t stroke;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ 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
+ compute the number of gradient reptitions needed to cover the
+ 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);
+ }
+
+- CGContextBeginPath (surface->cgContext);
+-
+- stroke.cgContext = surface->cgContext;
++ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
++
++ CGContextBeginPath (state.context);
++
++ stroke.cgContext = state.context;
+ stroke.ctm_inverse = NULL;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+
+ if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- if (action == DO_SOLID || action == DO_PATTERN) {
++ path_for_unbounded = CGContextCopyPathPtr (state.context);
++
++ 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);
+-
+- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
+- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
++ CGContextEOClip (state.context);
++
++ CGContextConcatCTM (state.context, state.transform);
++ CGContextDrawShading (state.context, state.shading);
++ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
+ if (fill_rule == CAIRO_FILL_RULE_WINDING)
+- CGContextClip (surface->cgContext);
++ CGContextClip (state.context);
+ else
+- CGContextEOClip (surface->cgContext);
+-
+- _cairo_quartz_draw_image (surface, op, action);
+- } else if (action != DO_NOTHING) {
++ CGContextEOClip (state.context);
++
++ _cairo_quartz_draw_image (&state, op);
++ } else if (state.action != DO_NOTHING) {
+ rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
++ _cairo_quartz_teardown_state (&state);
+
+ if (path_for_unbounded) {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_STROKE_FILL;
+ ub.u.stroke_fill.cgPath = path_for_unbounded;
+ ub.u.stroke_fill.fill_rule = fill_rule;
+
+ _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
+@@ -2274,44 +2323,49 @@ _cairo_quartz_surface_stroke (void *abst
+ cairo_matrix_t *ctm,
+ cairo_matrix_t *ctm_inverse,
+ double tolerance,
+ cairo_antialias_t antialias,
+ cairo_clip_t *clip)
+ {
+ 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;
+ quartz_stroke_t stroke;
+ CGAffineTransform origCTM, strokeTransform;
+ CGPathRef path_for_unbounded = NULL;
+
+ ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
+
+ if (IS_EMPTY(surface))
+ return CAIRO_STATUS_SUCCESS;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ 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;
++ 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);
+-
+- origCTM = CGContextGetCTM (surface->cgContext);
++ 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 (state.context);
+
+ if (style->dash && style->num_dashes) {
+ #define STATIC_DASH 32
+ CGFloat sdash[STATIC_DASH];
+ CGFloat *fdash = sdash;
+ double offset = style->dash_offset;
+ unsigned int max_dashes = style->num_dashes;
+ unsigned int k;
+@@ -2330,90 +2384,75 @@ _cairo_quartz_surface_stroke (void *abst
+ if (max_dashes > STATIC_DASH)
+ fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat));
+ if (fdash == NULL)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ for (k = 0; k < max_dashes; k++)
+ fdash[k] = (CGFloat) style->dash[k % style->num_dashes];
+ }
+- CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes);
++ CGContextSetLineDash (state.context, offset, fdash, max_dashes);
+ if (fdash != sdash)
+ free (fdash);
+ } else
+- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
+-
+- CGContextSaveGState (surface->cgContext);
+-
+-
+- 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);
+- }
++ CGContextSetLineDash (state.context, 0, NULL, 0);
+
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+-
+- CGContextBeginPath (surface->cgContext);
+-
+- stroke.cgContext = surface->cgContext;
++ CGContextConcatCTM (state.context, strokeTransform);
++
++ CGContextBeginPath (state.context);
++
++ stroke.cgContext = state.context;
+ stroke.ctm_inverse = ctm_inverse;
+ rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
+ if (rv)
+ goto BAIL;
+
+ if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
+- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+-
+- 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) {
++ 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) {
++ 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;
+ }
+
++ if (path_for_unbounded) {
++ CGContextSetCTM (state.context, origCTM);
++ CGContextConcatCTM (state.context, strokeTransform);
++
++ CGContextBeginPath (state.context);
++ CGContextAddPath (state.context, path_for_unbounded);
++ CGPathRelease (path_for_unbounded);
++
++ CGContextReplacePathWithStrokedPath (state.context);
++
++ CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context));
++
++ CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.);
++ CGContextEOFillPath (state.context);
++ }
++
+ BAIL:
+- _cairo_quartz_teardown_source (surface, source);
+-
+- CGContextRestoreGState (surface->cgContext);
+-
+- if (path_for_unbounded) {
+- CGContextSaveGState (surface->cgContext);
+- CGContextConcatCTM (surface->cgContext, strokeTransform);
+-
+- CGContextBeginPath (surface->cgContext);
+- CGContextAddPath (surface->cgContext, path_for_unbounded);
+- CGPathRelease (path_for_unbounded);
+-
+- CGContextReplacePathWithStrokedPath (surface->cgContext);
+-
+- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
+-
+- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
+- CGContextEOFillPath (surface->cgContext);
+-
+- CGContextRestoreGState (surface->cgContext);
+- }
++ _cairo_quartz_teardown_state (&state);
+
+ ND((stderr, "-- stroke\n"));
+ return rv;
+ }
+
+ #if CAIRO_HAS_QUARTZ_FONT
+ static cairo_int_status_t
+ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
+@@ -2429,17 +2468,17 @@ _cairo_quartz_surface_show_glyphs (void
+ #define STATIC_BUF_SIZE 64
+ CGGlyph glyphs_static[STATIC_BUF_SIZE];
+ CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGGlyph *cg_glyphs = &glyphs_static[0];
+ CGSize *cg_advances = &cg_advances_static[0];
+
+ 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;
+ float xprev, yprev;
+ int i;
+ CGFontRef cgfref = NULL;
+
+ cairo_bool_t isClipping = FALSE;
+ cairo_bool_t didForceFontSmoothing = FALSE;
+
+ if (IS_EMPTY(surface))
+@@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void
+
+ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ 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_rectangle_int_t glyph_extents;
+ _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) {
++ 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);
+
+ 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 */
+ break;
+ }
+
+ if (num_glyphs > STATIC_BUF_SIZE) {
+ cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
+@@ -2532,17 +2565,17 @@ _cairo_quartz_surface_show_glyphs (void
+ textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
+ textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
+ -scaled_font->ctm.yx,
+ -scaled_font->ctm.xy,
+ scaled_font->ctm.yy,
+ 0., 0.),
+ textTransform);
+
+- CGContextSetTextMatrix (surface->cgContext, textTransform);
++ CGContextSetTextMatrix (state.context, textTransform);
+
+ /* 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;
+
+@@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void
+
+ #if 0
+ for (i = 0; i < num_glyphs; i++) {
+ ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
+ }
+ #endif
+
+ /* Translate to the first glyph's position before drawing */
+- ctm = CGContextGetCTM (surface->cgContext);
+- CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
+-
+- CGContextShowGlyphsWithAdvances (surface->cgContext,
++ ctm = CGContextGetCTM (state.context);
++ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
++
++ CGContextShowGlyphsWithAdvances (state.context,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+
+- CGContextSetCTM (surface->cgContext, ctm);
+-
+- 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);
++ CGContextSetCTM (state.context, ctm);
++
++ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
++ _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);
+-
+- CGContextRestoreGState (surface->cgContext);
++ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
++
++ _cairo_quartz_teardown_state (&state);
+
+ if (rv == CAIRO_STATUS_SUCCESS &&
+ cgfref &&
+ !_cairo_operator_bounded_by_mask (op))
+ {
+ unbounded_op_data_t ub;
+ ub.op = UNBOUNDED_SHOW_GLYPHS;
+