gfx/cairo/quartz-state.patch

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/cairo/quartz-state.patch	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1190 @@
     1.4 +diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h
     1.5 +--- a/gfx/cairo/cairo/src/cairo-quartz-private.h
     1.6 ++++ b/gfx/cairo/cairo/src/cairo-quartz-private.h
     1.7 +@@ -50,30 +50,16 @@ typedef struct cairo_quartz_surface {
     1.8 +     CGContextRef cgContext;
     1.9 +     CGAffineTransform cgContextBaseCTM;
    1.10 + 
    1.11 +     void *imageData;
    1.12 +     cairo_surface_t *imageSurfaceEquiv;
    1.13 + 
    1.14 +     cairo_surface_clipper_t clipper;
    1.15 +     cairo_rectangle_int_t extents;
    1.16 +-
    1.17 +-    /* These are stored while drawing operations are in place, set up
    1.18 +-     * by quartz_setup_source() and quartz_finish_source()
    1.19 +-     */
    1.20 +-    CGAffineTransform sourceTransform;
    1.21 +-
    1.22 +-    CGImageRef sourceImage;
    1.23 +-    cairo_surface_t *sourceImageSurface;
    1.24 +-    CGRect sourceImageRect;
    1.25 +-
    1.26 +-    CGShadingRef sourceShading;
    1.27 +-    CGPatternRef sourcePattern;
    1.28 +-
    1.29 +-    CGInterpolationQuality oldInterpolationQuality;
    1.30 + } cairo_quartz_surface_t;
    1.31 + 
    1.32 + typedef struct cairo_quartz_image_surface {
    1.33 +     cairo_surface_t base;
    1.34 + 
    1.35 +     cairo_rectangle_int_t extents;
    1.36 + 
    1.37 +     CGImageRef image;
    1.38 +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.39 +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.40 ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    1.41 +@@ -1333,36 +1333,59 @@ _cairo_quartz_cairo_repeating_surface_pa
    1.42 +     return CAIRO_STATUS_SUCCESS;
    1.43 + }
    1.44 + 
    1.45 + typedef enum {
    1.46 +     DO_SOLID,
    1.47 +     DO_SHADING,
    1.48 +     DO_PATTERN,
    1.49 +     DO_IMAGE,
    1.50 ++    DO_TILED_IMAGE,
    1.51 +     DO_UNSUPPORTED,
    1.52 +-    DO_NOTHING,
    1.53 +-    DO_TILED_IMAGE
    1.54 ++    DO_NOTHING
    1.55 + } cairo_quartz_action_t;
    1.56 + 
    1.57 +-static cairo_quartz_action_t
    1.58 ++/* State used during a drawing operation. */
    1.59 ++typedef struct {
    1.60 ++    CGContextRef context;
    1.61 ++    cairo_quartz_action_t action;
    1.62 ++
    1.63 ++    // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE
    1.64 ++    CGAffineTransform transform;
    1.65 ++
    1.66 ++    // Used with DO_IMAGE and DO_TILED_IMAGE
    1.67 ++    CGImageRef image;
    1.68 ++    cairo_surface_t *imageSurface;
    1.69 ++    CGRect imageRect;
    1.70 ++
    1.71 ++    // Used with DO_SHADING
    1.72 ++    CGShadingRef shading;
    1.73 ++
    1.74 ++    // Used with DO_PATTERN
    1.75 ++    CGPatternRef pattern;
    1.76 ++} cairo_quartz_drawing_state_t;
    1.77 ++
    1.78 ++static void
    1.79 + _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
    1.80 +-				     const cairo_pattern_t *source)
    1.81 ++				     const cairo_pattern_t *source,
    1.82 ++				     cairo_quartz_drawing_state_t *state)
    1.83 + {
    1.84 +-    CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
    1.85 ++    CGRect clipBox = CGContextGetClipBoundingBox (state->context);
    1.86 +     double x0, y0, w, h;
    1.87 + 
    1.88 +     cairo_surface_t *fallback;
    1.89 +     CGImageRef img;
    1.90 + 
    1.91 +     cairo_status_t status;
    1.92 + 
    1.93 +     if (clipBox.size.width == 0.0f ||
    1.94 +-	clipBox.size.height == 0.0f)
    1.95 +-	return DO_NOTHING;
    1.96 ++	clipBox.size.height == 0.0f) {
    1.97 ++	state->action = DO_NOTHING;
    1.98 ++	return;
    1.99 ++    }
   1.100 + 
   1.101 +     x0 = floor(clipBox.origin.x);
   1.102 +     y0 = floor(clipBox.origin.y);
   1.103 +     w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
   1.104 +     h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
   1.105 + 
   1.106 +     /* Create a temporary the size of the clip surface, and position
   1.107 +      * it so that the device origin coincides with the original surface */
   1.108 +@@ -1396,73 +1419,79 @@ _cairo_quartz_setup_fallback_source (cai
   1.109 + 				  &fallback->device_transform_inverse);
   1.110 + 	status = _cairo_surface_paint (fallback,
   1.111 + 				       CAIRO_OPERATOR_SOURCE,
   1.112 + 				       &pattern.base, NULL);
   1.113 +     }
   1.114 + #endif
   1.115 + 
   1.116 +     status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
   1.117 +-    if (status)
   1.118 +-	return DO_UNSUPPORTED;
   1.119 +-    if (img == NULL)
   1.120 +-	return DO_NOTHING;
   1.121 +-
   1.122 +-    surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
   1.123 +-    surface->sourceImage = img;
   1.124 +-    surface->sourceImageSurface = fallback;
   1.125 +-    surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
   1.126 +-
   1.127 +-    return DO_IMAGE;
   1.128 ++    if (status) {
   1.129 ++        state->action = DO_UNSUPPORTED;
   1.130 ++	return;
   1.131 ++    }
   1.132 ++    if (img == NULL) {
   1.133 ++        state->action = DO_NOTHING;
   1.134 ++	return;
   1.135 ++    }
   1.136 ++
   1.137 ++    state->imageRect = CGRectMake (0.0, 0.0, w, h);
   1.138 ++    state->image = img;
   1.139 ++    state->imageSurface = fallback;
   1.140 ++    state->transform = CGAffineTransformMakeTranslation (x0, y0);
   1.141 ++    state->action = DO_IMAGE;
   1.142 + }
   1.143 + 
   1.144 + /*
   1.145 + Quartz does not support repeating radients. We handle repeating gradients
   1.146 + by manually extending the gradient and repeating color stops. We need to
   1.147 + minimize the number of repetitions since Quartz seems to sample our color
   1.148 + function across the entire range, even if part of that range is not needed
   1.149 + for the visible area of the gradient, and it samples with some fixed resolution,
   1.150 + so if the gradient range is too large it samples with very low resolution and
   1.151 + the gradient is very coarse. CreateRepeatingLinearGradientFunction and
   1.152 + CreateRepeatingRadialGradientFunction compute the number of repetitions needed
   1.153 + based on the extents of the object (the clip region cannot be used here since
   1.154 + we don't want the rasterization of the entire gradient to depend on the
   1.155 + clip region).
   1.156 + */
   1.157 +-static cairo_quartz_action_t
   1.158 ++static void
   1.159 + _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
   1.160 + 				   const cairo_linear_pattern_t *lpat,
   1.161 +-				   cairo_rectangle_int_t *extents)
   1.162 ++				   cairo_rectangle_int_t *extents,
   1.163 ++				   cairo_quartz_drawing_state_t *state)
   1.164 + {
   1.165 +     const cairo_pattern_t *abspat = &lpat->base.base;
   1.166 +     cairo_matrix_t mat;
   1.167 +     CGPoint start, end;
   1.168 +     CGFunctionRef gradFunc;
   1.169 +     CGColorSpaceRef rgb;
   1.170 +     bool extend = abspat->extend == CAIRO_EXTEND_PAD;
   1.171 + 
   1.172 +     if (lpat->base.n_stops == 0) {
   1.173 +-	CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
   1.174 +-	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
   1.175 +-	return DO_SOLID;
   1.176 ++	CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
   1.177 ++	CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
   1.178 ++	state->action = DO_SOLID;
   1.179 ++	return;
   1.180 +     }
   1.181 + 
   1.182 +     if (lpat->p1.x == lpat->p2.x &&
   1.183 +         lpat->p1.y == lpat->p2.y) {
   1.184 + 	/* Quartz handles cases where the vector has no length very
   1.185 + 	 * differently from pixman.
   1.186 + 	 * Whatever the correct behaviour is, let's at least have only pixman's
   1.187 + 	 * implementation to worry about.
   1.188 + 	 */
   1.189 +-	return _cairo_quartz_setup_fallback_source (surface, abspat);
   1.190 ++	_cairo_quartz_setup_fallback_source (surface, abspat, state);
   1.191 ++	return;
   1.192 +     }
   1.193 + 
   1.194 +     mat = abspat->matrix;
   1.195 +     cairo_matrix_invert (&mat);
   1.196 +-    _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
   1.197 ++    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
   1.198 + 
   1.199 +     rgb = CGColorSpaceCreateDeviceRGB();
   1.200 + 
   1.201 +     start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
   1.202 + 			 _cairo_fixed_to_double (lpat->p1.y));
   1.203 +     end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
   1.204 + 		       _cairo_fixed_to_double (lpat->p2.y));
   1.205 + 
   1.206 +@@ -1472,31 +1501,32 @@ _cairo_quartz_setup_linear_source (cairo
   1.207 + 	gradFunc = CreateGradientFunction (&lpat->base);
   1.208 +     } else {
   1.209 + 	gradFunc = CreateRepeatingLinearGradientFunction (surface,
   1.210 + 						          &lpat->base,
   1.211 + 						          &start, &end,
   1.212 + 						          extents);
   1.213 +     }
   1.214 + 
   1.215 +-    surface->sourceShading = CGShadingCreateAxial (rgb,
   1.216 +-						   start, end,
   1.217 +-						   gradFunc,
   1.218 +-						   extend, extend);
   1.219 ++    state->shading = CGShadingCreateAxial (rgb,
   1.220 ++					   start, end,
   1.221 ++					   gradFunc,
   1.222 ++					   extend, extend);
   1.223 + 
   1.224 +     CGColorSpaceRelease(rgb);
   1.225 +     CGFunctionRelease(gradFunc);
   1.226 + 
   1.227 +-    return DO_SHADING;
   1.228 ++    state->action = DO_SHADING;
   1.229 + }
   1.230 + 
   1.231 +-static cairo_quartz_action_t
   1.232 ++static void
   1.233 + _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
   1.234 + 				   const cairo_radial_pattern_t *rpat,
   1.235 +-				   cairo_rectangle_int_t *extents)
   1.236 ++				   cairo_rectangle_int_t *extents,
   1.237 ++				   cairo_quartz_drawing_state_t *state)
   1.238 + {
   1.239 +     const cairo_pattern_t *abspat = &rpat->base.base;
   1.240 +     cairo_matrix_t mat;
   1.241 +     CGPoint start, end;
   1.242 +     CGFunctionRef gradFunc;
   1.243 +     CGColorSpaceRef rgb;
   1.244 +     bool extend = abspat->extend == CAIRO_EXTEND_PAD;
   1.245 +     double c1x = _cairo_fixed_to_double (rpat->c1.x);
   1.246 +@@ -1505,35 +1535,37 @@ _cairo_quartz_setup_radial_source (cairo
   1.247 +     double c2y = _cairo_fixed_to_double (rpat->c2.y);
   1.248 +     double r1 = _cairo_fixed_to_double (rpat->r1);
   1.249 +     double r2 = _cairo_fixed_to_double (rpat->r2);
   1.250 +     double dx = c1x - c2x;
   1.251 +     double dy = c1y - c2y;
   1.252 +     double centerDistance = sqrt (dx*dx + dy*dy);
   1.253 + 
   1.254 +     if (rpat->base.n_stops == 0) {
   1.255 +-	CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
   1.256 +-	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
   1.257 +-	return DO_SOLID;
   1.258 ++	CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
   1.259 ++	CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
   1.260 ++	state->action = DO_SOLID;
   1.261 ++	return;
   1.262 +     }
   1.263 + 
   1.264 +     if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
   1.265 +         r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
   1.266 + 	/* Quartz handles cases where neither circle contains the other very
   1.267 + 	 * differently from pixman.
   1.268 + 	 * Whatever the correct behaviour is, let's at least have only pixman's
   1.269 + 	 * implementation to worry about.
   1.270 + 	 * Note that this also catches the cases where r1 == r2.
   1.271 + 	 */
   1.272 +-	return _cairo_quartz_setup_fallback_source (surface, abspat);
   1.273 ++	_cairo_quartz_setup_fallback_source (surface, abspat, state);
   1.274 ++	return;
   1.275 +     }
   1.276 + 
   1.277 +     mat = abspat->matrix;
   1.278 +     cairo_matrix_invert (&mat);
   1.279 +-    _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
   1.280 ++    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
   1.281 + 
   1.282 +     rgb = CGColorSpaceCreateDeviceRGB();
   1.283 + 
   1.284 +     start = CGPointMake (c1x, c1y);
   1.285 +     end = CGPointMake (c2x, c2y);
   1.286 + 
   1.287 +     if (abspat->extend == CAIRO_EXTEND_NONE ||
   1.288 +         abspat->extend == CAIRO_EXTEND_PAD)
   1.289 +@@ -1542,111 +1574,146 @@ _cairo_quartz_setup_radial_source (cairo
   1.290 +     } else {
   1.291 + 	gradFunc = CreateRepeatingRadialGradientFunction (surface,
   1.292 + 						          &rpat->base,
   1.293 + 						          &start, &r1,
   1.294 + 						          &end, &r2,
   1.295 + 						          extents);
   1.296 +     }
   1.297 + 
   1.298 +-    surface->sourceShading = CGShadingCreateRadial (rgb,
   1.299 +-						    start,
   1.300 +-						    r1,
   1.301 +-						    end,
   1.302 +-						    r2,
   1.303 +-						    gradFunc,
   1.304 +-						    extend, extend);
   1.305 ++    state->shading = CGShadingCreateRadial (rgb,
   1.306 ++					    start,
   1.307 ++					    r1,
   1.308 ++					    end,
   1.309 ++					    r2,
   1.310 ++					    gradFunc,
   1.311 ++					    extend, extend);
   1.312 + 
   1.313 +     CGColorSpaceRelease(rgb);
   1.314 +     CGFunctionRelease(gradFunc);
   1.315 + 
   1.316 +-    return DO_SHADING;
   1.317 ++    state->action = DO_SHADING;
   1.318 + }
   1.319 + 
   1.320 +-static cairo_quartz_action_t
   1.321 +-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
   1.322 +-			    const cairo_pattern_t *source,
   1.323 +-			    cairo_rectangle_int_t *extents)
   1.324 ++/**
   1.325 ++ * Sets up internal state to be used to draw the source mask, stored in
   1.326 ++ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
   1.327 ++ * surface->cgContext.
   1.328 ++ */
   1.329 ++static cairo_quartz_drawing_state_t
   1.330 ++_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
   1.331 ++			   const cairo_pattern_t *source,
   1.332 ++			   cairo_operator_t op,
   1.333 ++			   cairo_rectangle_int_t *extents)
   1.334 + {
   1.335 +-    assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
   1.336 +-
   1.337 +-    surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
   1.338 +-    CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
   1.339 ++    CGContextRef context = surface->cgContext;
   1.340 ++    cairo_quartz_drawing_state_t state;
   1.341 ++    cairo_status_t status;
   1.342 ++
   1.343 ++    state.context = context;
   1.344 ++    state.image = NULL;
   1.345 ++    state.imageSurface = NULL;
   1.346 ++    state.shading = NULL;
   1.347 ++    state.pattern = NULL;
   1.348 ++
   1.349 ++    // Save before we change the pattern, colorspace, etc. so that
   1.350 ++    // we can restore and make sure that quartz releases our
   1.351 ++    // pattern (which may be stack allocated)
   1.352 ++    CGContextSaveGState(context);
   1.353 ++
   1.354 ++    CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
   1.355 ++
   1.356 ++    status = _cairo_quartz_surface_set_cairo_operator (surface, op);
   1.357 ++    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
   1.358 ++        state.action = DO_NOTHING;
   1.359 ++        return state;
   1.360 ++    }
   1.361 ++    if (status) {
   1.362 ++        state.action = DO_UNSUPPORTED;
   1.363 ++        return state;
   1.364 ++    }
   1.365 + 
   1.366 +     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
   1.367 + 	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
   1.368 + 
   1.369 +-	CGContextSetRGBStrokeColor (surface->cgContext,
   1.370 ++	CGContextSetRGBStrokeColor (context,
   1.371 + 				    solid->color.red,
   1.372 + 				    solid->color.green,
   1.373 + 				    solid->color.blue,
   1.374 + 				    solid->color.alpha);
   1.375 +-	CGContextSetRGBFillColor (surface->cgContext,
   1.376 ++	CGContextSetRGBFillColor (context,
   1.377 + 				  solid->color.red,
   1.378 + 				  solid->color.green,
   1.379 + 				  solid->color.blue,
   1.380 + 				  solid->color.alpha);
   1.381 + 
   1.382 +-	return DO_SOLID;
   1.383 ++        state.action = DO_SOLID;
   1.384 ++        return state;
   1.385 +     }
   1.386 + 
   1.387 +     if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
   1.388 + 	const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
   1.389 +-	return _cairo_quartz_setup_linear_source (surface, lpat, extents);
   1.390 ++	_cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
   1.391 ++	return state;
   1.392 +     }
   1.393 + 
   1.394 +     if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
   1.395 + 	const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
   1.396 +-	return _cairo_quartz_setup_radial_source (surface, rpat, extents);
   1.397 ++	_cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
   1.398 ++	return state;
   1.399 +     }
   1.400 + 
   1.401 +     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
   1.402 + 	(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
   1.403 +     {
   1.404 + 	const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
   1.405 + 	cairo_surface_t *pat_surf = spat->surface;
   1.406 + 	CGImageRef img;
   1.407 + 	cairo_matrix_t m = spat->base.matrix;
   1.408 + 	cairo_rectangle_int_t extents;
   1.409 +-	cairo_status_t status;
   1.410 + 	CGAffineTransform xform;
   1.411 + 	CGRect srcRect;
   1.412 + 	cairo_fixed_t fw, fh;
   1.413 + 	cairo_bool_t is_bounded;
   1.414 + 
   1.415 + 	status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
   1.416 +-	if (status)
   1.417 +-	    return DO_UNSUPPORTED;
   1.418 +-	if (img == NULL)
   1.419 +-	    return DO_NOTHING;
   1.420 ++        if (status) {
   1.421 ++            state.action = DO_UNSUPPORTED;
   1.422 ++	    return state;
   1.423 ++        }
   1.424 ++        if (img == NULL) {
   1.425 ++            state.action = DO_NOTHING;
   1.426 ++	    return state;
   1.427 ++        }
   1.428 + 
   1.429 + 	CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
   1.430 + 
   1.431 +-	surface->sourceImage = img;
   1.432 ++	state.image = img;
   1.433 + 
   1.434 + 	cairo_matrix_invert(&m);
   1.435 +-	_cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
   1.436 ++	_cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
   1.437 + 
   1.438 + 	is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
   1.439 + 	assert (is_bounded);
   1.440 + 
   1.441 + 	if (source->extend == CAIRO_EXTEND_NONE) {
   1.442 +-	    surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
   1.443 +-	    return DO_IMAGE;
   1.444 ++	    state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
   1.445 ++	    state.action = DO_IMAGE;
   1.446 ++	    return state;
   1.447 + 	}
   1.448 + 
   1.449 + 	/* Quartz seems to tile images at pixel-aligned regions only -- this
   1.450 + 	 * leads to seams if the image doesn't end up scaling to fill the
   1.451 + 	 * space exactly.  The CGPattern tiling approach doesn't have this
   1.452 + 	 * problem.  Check if we're going to fill up the space (within some
   1.453 + 	 * epsilon), and if not, fall back to the CGPattern type.
   1.454 + 	 */
   1.455 + 
   1.456 +-	xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
   1.457 +-					 surface->sourceTransform);
   1.458 ++	xform = CGAffineTransformConcat (CGContextGetCTM (context),
   1.459 ++					 state.transform);
   1.460 + 
   1.461 + 	srcRect = CGRectMake (0, 0, extents.width, extents.height);
   1.462 + 	srcRect = CGRectApplyAffineTransform (srcRect, xform);
   1.463 + 
   1.464 + 	fw = _cairo_fixed_from_double (srcRect.size.width);
   1.465 + 	fh = _cairo_fixed_from_double (srcRect.size.height);
   1.466 + 
   1.467 + 	if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
   1.468 +@@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz
   1.469 + 
   1.470 + 	    srcRect.size.width = round(srcRect.size.width);
   1.471 + 	    srcRect.size.height = round(srcRect.size.height);
   1.472 + 
   1.473 + 	    xform = CGAffineTransformInvert (xform);
   1.474 + 
   1.475 + 	    srcRect = CGRectApplyAffineTransform (srcRect, xform);
   1.476 + 
   1.477 +-	    surface->sourceImageRect = srcRect;
   1.478 +-
   1.479 +-	    return DO_TILED_IMAGE;
   1.480 ++	    state.imageRect = srcRect;
   1.481 ++            state.action = DO_TILED_IMAGE;
   1.482 ++            return state;
   1.483 + 	}
   1.484 + 
   1.485 + 	/* Fall through to generic SURFACE case */
   1.486 +     }
   1.487 + 
   1.488 +     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
   1.489 + 	CGFloat patternAlpha = 1.0f;
   1.490 + 	CGColorSpaceRef patternSpace;
   1.491 + 	CGPatternRef pattern;
   1.492 + 	cairo_int_status_t status;
   1.493 + 
   1.494 + 	status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
   1.495 +-	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
   1.496 +-	    return DO_NOTHING;
   1.497 +-	if (status)
   1.498 +-	    return DO_UNSUPPORTED;
   1.499 +-
   1.500 +-	// Save before we change the pattern, colorspace, etc. so that
   1.501 +-	// we can restore and make sure that quartz releases our
   1.502 +-	// pattern (which may be stack allocated)
   1.503 +-	CGContextSaveGState(surface->cgContext);
   1.504 +-
   1.505 +-	patternSpace = CGColorSpaceCreatePattern(NULL);
   1.506 +-	CGContextSetFillColorSpace (surface->cgContext, patternSpace);
   1.507 +-	CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
   1.508 +-	CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
   1.509 +-	CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
   1.510 ++	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
   1.511 ++	    state.action = DO_NOTHING;
   1.512 ++	    return state;
   1.513 ++	}
   1.514 ++	if (status) {
   1.515 ++	    state.action = DO_UNSUPPORTED;
   1.516 ++	    return state;
   1.517 ++	}
   1.518 ++
   1.519 ++	patternSpace = CGColorSpaceCreatePattern (NULL);
   1.520 ++	CGContextSetFillColorSpace (context, patternSpace);
   1.521 ++	CGContextSetFillPattern (context, pattern, &patternAlpha);
   1.522 ++	CGContextSetStrokeColorSpace (context, patternSpace); 
   1.523 ++	CGContextSetStrokePattern (context, pattern, &patternAlpha);
   1.524 + 	CGColorSpaceRelease (patternSpace);
   1.525 + 
   1.526 + 	/* Quartz likes to munge the pattern phase (as yet unexplained
   1.527 + 	 * why); force it to 0,0 as we've already baked in the correct
   1.528 + 	 * pattern translation into the pattern matrix
   1.529 + 	 */
   1.530 +-	CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
   1.531 +-
   1.532 +-	surface->sourcePattern = pattern;
   1.533 +-
   1.534 +-	return DO_PATTERN;
   1.535 ++	CGContextSetPatternPhase (context, CGSizeMake(0,0));
   1.536 ++
   1.537 ++	state.pattern = pattern;
   1.538 ++        state.action = DO_PATTERN;
   1.539 ++        return state;
   1.540 +     }
   1.541 + 
   1.542 +-    return DO_UNSUPPORTED;
   1.543 ++    state.action = DO_UNSUPPORTED;
   1.544 ++    return state;
   1.545 + }
   1.546 + 
   1.547 ++/**
   1.548 ++ * 1) Tears down internal state used to draw the source
   1.549 ++ * 2) Does CGContextRestoreGState(state->context)
   1.550 ++ */
   1.551 + static void
   1.552 +-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
   1.553 +-			       const cairo_pattern_t *source)
   1.554 ++_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
   1.555 + {
   1.556 +-    CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
   1.557 +-
   1.558 +-    if (surface->sourceImage) {
   1.559 +-	CGImageRelease(surface->sourceImage);
   1.560 +-	surface->sourceImage = NULL;
   1.561 +-
   1.562 +-	cairo_surface_destroy(surface->sourceImageSurface);
   1.563 +-	surface->sourceImageSurface = NULL;
   1.564 ++    if (state->image) {
   1.565 ++	CGImageRelease(state->image);
   1.566 +     }
   1.567 + 
   1.568 +-    if (surface->sourceShading) {
   1.569 +-	CGShadingRelease(surface->sourceShading);
   1.570 +-	surface->sourceShading = NULL;
   1.571 ++    if (state->imageSurface) {
   1.572 ++	cairo_surface_destroy(state->imageSurface);
   1.573 +     }
   1.574 + 
   1.575 +-    if (surface->sourcePattern) {
   1.576 +-	CGPatternRelease(surface->sourcePattern);
   1.577 +-	// To tear down the pattern and colorspace
   1.578 +-	CGContextRestoreGState(surface->cgContext);
   1.579 +-
   1.580 +-	surface->sourcePattern = NULL;
   1.581 ++    if (state->shading) {
   1.582 ++	CGShadingRelease(state->shading);
   1.583 +     }
   1.584 ++
   1.585 ++    if (state->pattern) {
   1.586 ++	CGPatternRelease(state->pattern);
   1.587 ++    }
   1.588 ++
   1.589 ++    CGContextRestoreGState(state->context);
   1.590 + }
   1.591 + 
   1.592 + 
   1.593 + static void
   1.594 +-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op,  cairo_quartz_action_t action)
   1.595 ++_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
   1.596 + {
   1.597 +-    assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
   1.598 +-
   1.599 +-    CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   1.600 +-    CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
   1.601 +-    CGContextScaleCTM (surface->cgContext, 1, -1);
   1.602 +-
   1.603 +-    if (action == DO_IMAGE) {
   1.604 +-	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
   1.605 +-	if (!_cairo_operator_bounded_by_source(op)) {
   1.606 +-	    CGContextBeginPath (surface->cgContext);
   1.607 +-	    CGContextAddRect (surface->cgContext, surface->sourceImageRect);
   1.608 +-	    CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
   1.609 +-	    CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
   1.610 +-	    CGContextEOFillPath (surface->cgContext);
   1.611 ++    assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
   1.612 ++
   1.613 ++    CGContextConcatCTM (state->context, state->transform);
   1.614 ++    CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
   1.615 ++    CGContextScaleCTM (state->context, 1, -1);
   1.616 ++
   1.617 ++    if (state->action == DO_IMAGE) {
   1.618 ++	CGContextDrawImage (state->context, state->imageRect, state->image);
   1.619 ++	if (!_cairo_operator_bounded_by_source (op)) {
   1.620 ++	    CGContextBeginPath (state->context);
   1.621 ++	    CGContextAddRect (state->context, state->imageRect);
   1.622 ++	    CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
   1.623 ++	    CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
   1.624 ++	    CGContextEOFillPath (state->context);
   1.625 + 	}
   1.626 +     } else
   1.627 +-	CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
   1.628 ++	CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
   1.629 + }
   1.630 + 
   1.631 + 
   1.632 + /*
   1.633 +  * get source/dest image implementation
   1.634 +  */
   1.635 + 
   1.636 + /* Read the image from the surface's front buffer */
   1.637 +@@ -2098,52 +2163,44 @@ _cairo_quartz_surface_get_extents (void 
   1.638 + static cairo_int_status_t
   1.639 + _cairo_quartz_surface_paint (void *abstract_surface,
   1.640 + 			     cairo_operator_t op,
   1.641 + 			     const cairo_pattern_t *source,
   1.642 + 			     cairo_clip_t *clip)
   1.643 + {
   1.644 +     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   1.645 +     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   1.646 +-    cairo_quartz_action_t action;
   1.647 ++    cairo_quartz_drawing_state_t state;
   1.648 + 
   1.649 +     ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
   1.650 + 
   1.651 +     if (IS_EMPTY(surface))
   1.652 + 	return CAIRO_STATUS_SUCCESS;
   1.653 + 
   1.654 +     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   1.655 +     if (unlikely (rv))
   1.656 + 	return rv;
   1.657 + 
   1.658 +-    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   1.659 +-    if (unlikely (rv))
   1.660 +-	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   1.661 +-
   1.662 +-    action = _cairo_quartz_setup_source (surface, source, NULL);
   1.663 +-
   1.664 +-    if (action == DO_SOLID || action == DO_PATTERN) {
   1.665 +-	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
   1.666 +-							  surface->extents.y,
   1.667 +-							  surface->extents.width,
   1.668 +-							  surface->extents.height));
   1.669 +-    } else if (action == DO_SHADING) {
   1.670 +-	CGContextSaveGState (surface->cgContext);
   1.671 +-	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   1.672 +-	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   1.673 +-	CGContextRestoreGState (surface->cgContext);
   1.674 +-    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   1.675 +-	CGContextSaveGState (surface->cgContext);
   1.676 +-	_cairo_quartz_draw_image (surface, op, action);
   1.677 +-	CGContextRestoreGState (surface->cgContext);
   1.678 +-    } else if (action != DO_NOTHING) {
   1.679 ++    state = _cairo_quartz_setup_state (surface, source, op, NULL);
   1.680 ++
   1.681 ++    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   1.682 ++	CGContextFillRect (state.context, CGRectMake(surface->extents.x,
   1.683 ++						     surface->extents.y,
   1.684 ++						     surface->extents.width,
   1.685 ++						     surface->extents.height));
   1.686 ++    } else if (state.action == DO_SHADING) {
   1.687 ++	CGContextConcatCTM (state.context, state.transform);
   1.688 ++	CGContextDrawShading (state.context, state.shading);
   1.689 ++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   1.690 ++	_cairo_quartz_draw_image (&state, op);
   1.691 ++    } else if (state.action != DO_NOTHING) {
   1.692 + 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   1.693 +     }
   1.694 + 
   1.695 +-    _cairo_quartz_teardown_source (surface, source);
   1.696 ++    _cairo_quartz_teardown_state (&state);
   1.697 + 
   1.698 +     ND((stderr, "-- paint\n"));
   1.699 +     return rv;
   1.700 + }
   1.701 + 
   1.702 + static cairo_bool_t
   1.703 + _cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
   1.704 + {
   1.705 +@@ -2170,91 +2227,83 @@ _cairo_quartz_surface_fill (void *abstra
   1.706 + 			     cairo_path_fixed_t *path,
   1.707 + 			     cairo_fill_rule_t fill_rule,
   1.708 + 			     double tolerance,
   1.709 + 			     cairo_antialias_t antialias,
   1.710 + 			     cairo_clip_t *clip)
   1.711 + {
   1.712 +     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   1.713 +     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   1.714 +-    cairo_quartz_action_t action;
   1.715 ++    cairo_quartz_drawing_state_t state;
   1.716 +     quartz_stroke_t stroke;
   1.717 +     CGPathRef path_for_unbounded = NULL;
   1.718 + 
   1.719 +     ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
   1.720 + 
   1.721 +     if (IS_EMPTY(surface))
   1.722 + 	return CAIRO_STATUS_SUCCESS;
   1.723 + 
   1.724 +     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   1.725 +     if (unlikely (rv))
   1.726 + 	return rv;
   1.727 + 
   1.728 +-    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   1.729 +-    if (unlikely (rv))
   1.730 +-	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   1.731 +-
   1.732 +-    CGContextSaveGState (surface->cgContext);
   1.733 +-
   1.734 +-    CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
   1.735 +-
   1.736 +     if (_cairo_quartz_source_needs_extents (source))
   1.737 +     {
   1.738 +         /* We don't need precise extents since these are only used to
   1.739 +            compute the number of gradient reptitions needed to cover the
   1.740 +            object. */
   1.741 +         cairo_rectangle_int_t path_extents;
   1.742 +         _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
   1.743 +-        action = _cairo_quartz_setup_source (surface, source, &path_extents);
   1.744 ++        state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
   1.745 +     } else {
   1.746 +-        action = _cairo_quartz_setup_source (surface, source, NULL);
   1.747 ++        state = _cairo_quartz_setup_state (surface, source, op, NULL);
   1.748 +     }
   1.749 + 
   1.750 +-    CGContextBeginPath (surface->cgContext);
   1.751 +-
   1.752 +-    stroke.cgContext = surface->cgContext;
   1.753 ++    CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
   1.754 ++
   1.755 ++    CGContextBeginPath (state.context);
   1.756 ++
   1.757 ++    stroke.cgContext = state.context;
   1.758 +     stroke.ctm_inverse = NULL;
   1.759 +     rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
   1.760 +     if (rv)
   1.761 +         goto BAIL;
   1.762 + 
   1.763 +     if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
   1.764 +-	path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
   1.765 +-
   1.766 +-    if (action == DO_SOLID || action == DO_PATTERN) {
   1.767 ++	path_for_unbounded = CGContextCopyPathPtr (state.context);
   1.768 ++
   1.769 ++    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   1.770 + 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   1.771 +-	    CGContextFillPath (surface->cgContext);
   1.772 ++	    CGContextFillPath (state.context);
   1.773 + 	else
   1.774 +-	    CGContextEOFillPath (surface->cgContext);
   1.775 +-    } else if (action == DO_SHADING) {
   1.776 ++	    CGContextEOFillPath (state.context);
   1.777 ++    } else if (state.action == DO_SHADING) {
   1.778 + 
   1.779 + 	// we have to clip and then paint the shading; we can't fill
   1.780 + 	// with the shading
   1.781 + 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   1.782 +-	    CGContextClip (surface->cgContext);
   1.783 ++	    CGContextClip (state.context);
   1.784 + 	else
   1.785 +-	    CGContextEOClip (surface->cgContext);
   1.786 +-
   1.787 +-	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   1.788 +-	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   1.789 +-    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   1.790 ++            CGContextEOClip (state.context);
   1.791 ++
   1.792 ++	CGContextConcatCTM (state.context, state.transform);
   1.793 ++	CGContextDrawShading (state.context, state.shading);
   1.794 ++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   1.795 + 	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   1.796 +-	    CGContextClip (surface->cgContext);
   1.797 ++	    CGContextClip (state.context);
   1.798 + 	else
   1.799 +-	    CGContextEOClip (surface->cgContext);
   1.800 +-
   1.801 +-	_cairo_quartz_draw_image (surface, op, action);
   1.802 +-    } else if (action != DO_NOTHING) {
   1.803 ++	    CGContextEOClip (state.context);
   1.804 ++
   1.805 ++	_cairo_quartz_draw_image (&state, op);
   1.806 ++    } else if (state.action != DO_NOTHING) {
   1.807 + 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   1.808 +     }
   1.809 + 
   1.810 +   BAIL:
   1.811 +-    _cairo_quartz_teardown_source (surface, source);
   1.812 +-
   1.813 +-    CGContextRestoreGState (surface->cgContext);
   1.814 ++    _cairo_quartz_teardown_state (&state);
   1.815 + 
   1.816 +     if (path_for_unbounded) {
   1.817 + 	unbounded_op_data_t ub;
   1.818 + 	ub.op = UNBOUNDED_STROKE_FILL;
   1.819 + 	ub.u.stroke_fill.cgPath = path_for_unbounded;
   1.820 + 	ub.u.stroke_fill.fill_rule = fill_rule;
   1.821 + 
   1.822 + 	_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
   1.823 +@@ -2274,44 +2323,49 @@ _cairo_quartz_surface_stroke (void *abst
   1.824 + 			      cairo_matrix_t *ctm,
   1.825 + 			      cairo_matrix_t *ctm_inverse,
   1.826 + 			      double tolerance,
   1.827 + 			      cairo_antialias_t antialias,
   1.828 + 			      cairo_clip_t *clip)
   1.829 + {
   1.830 +     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   1.831 +     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   1.832 +-    cairo_quartz_action_t action;
   1.833 ++    cairo_quartz_drawing_state_t state;
   1.834 +     quartz_stroke_t stroke;
   1.835 +     CGAffineTransform origCTM, strokeTransform;
   1.836 +     CGPathRef path_for_unbounded = NULL;
   1.837 + 
   1.838 +     ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
   1.839 + 
   1.840 +     if (IS_EMPTY(surface))
   1.841 + 	return CAIRO_STATUS_SUCCESS;
   1.842 + 
   1.843 +     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   1.844 +     if (unlikely (rv))
   1.845 + 	return rv;
   1.846 + 
   1.847 +-    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   1.848 +-    if (unlikely (rv))
   1.849 +-	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   1.850 ++    if (_cairo_quartz_source_needs_extents (source))
   1.851 ++    {
   1.852 ++        cairo_rectangle_int_t path_extents;
   1.853 ++        _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
   1.854 ++        state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
   1.855 ++    } else {
   1.856 ++        state = _cairo_quartz_setup_state (surface, source, op, NULL);
   1.857 ++    }
   1.858 + 
   1.859 +     // Turning antialiasing off used to cause misrendering with
   1.860 +     // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
   1.861 +     // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
   1.862 +-    CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
   1.863 +-    CGContextSetLineWidth (surface->cgContext, style->line_width);
   1.864 +-    CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
   1.865 +-    CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
   1.866 +-    CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
   1.867 +-
   1.868 +-    origCTM = CGContextGetCTM (surface->cgContext);
   1.869 ++    CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
   1.870 ++    CGContextSetLineWidth (state.context, style->line_width);
   1.871 ++    CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
   1.872 ++    CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
   1.873 ++    CGContextSetMiterLimit (state.context, style->miter_limit);
   1.874 ++
   1.875 ++    origCTM = CGContextGetCTM (state.context);
   1.876 + 
   1.877 +     if (style->dash && style->num_dashes) {
   1.878 + #define STATIC_DASH 32
   1.879 + 	CGFloat sdash[STATIC_DASH];
   1.880 + 	CGFloat *fdash = sdash;
   1.881 + 	double offset = style->dash_offset;
   1.882 + 	unsigned int max_dashes = style->num_dashes;
   1.883 + 	unsigned int k;
   1.884 +@@ -2330,90 +2384,75 @@ _cairo_quartz_surface_stroke (void *abst
   1.885 + 	    if (max_dashes > STATIC_DASH)
   1.886 + 		fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat));
   1.887 + 	    if (fdash == NULL)
   1.888 + 		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
   1.889 + 
   1.890 + 	    for (k = 0; k < max_dashes; k++)
   1.891 + 		fdash[k] = (CGFloat) style->dash[k % style->num_dashes];
   1.892 + 	}
   1.893 +-	CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes);
   1.894 ++	CGContextSetLineDash (state.context, offset, fdash, max_dashes);
   1.895 + 	if (fdash != sdash)
   1.896 + 	    free (fdash);
   1.897 +     } else
   1.898 +-	CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
   1.899 +-
   1.900 +-    CGContextSaveGState (surface->cgContext);
   1.901 +-
   1.902 +-
   1.903 +-    if (_cairo_quartz_source_needs_extents (source))
   1.904 +-    {
   1.905 +-        cairo_rectangle_int_t path_extents;
   1.906 +-        _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
   1.907 +-        action = _cairo_quartz_setup_source (surface, source, &path_extents);
   1.908 +-    } else {
   1.909 +-        action = _cairo_quartz_setup_source (surface, source, NULL);
   1.910 +-    }
   1.911 ++	CGContextSetLineDash (state.context, 0, NULL, 0);
   1.912 + 
   1.913 +     _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
   1.914 +-    CGContextConcatCTM (surface->cgContext, strokeTransform);
   1.915 +-
   1.916 +-    CGContextBeginPath (surface->cgContext);
   1.917 +-
   1.918 +-    stroke.cgContext = surface->cgContext;
   1.919 ++    CGContextConcatCTM (state.context, strokeTransform);
   1.920 ++
   1.921 ++    CGContextBeginPath (state.context);
   1.922 ++
   1.923 ++    stroke.cgContext = state.context;
   1.924 +     stroke.ctm_inverse = ctm_inverse;
   1.925 +     rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
   1.926 +     if (rv)
   1.927 + 	goto BAIL;
   1.928 + 
   1.929 +     if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
   1.930 +-	path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
   1.931 +-
   1.932 +-    if (action == DO_SOLID || action == DO_PATTERN) {
   1.933 +-	CGContextStrokePath (surface->cgContext);
   1.934 +-    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   1.935 +-	CGContextReplacePathWithStrokedPath (surface->cgContext);
   1.936 +-	CGContextClip (surface->cgContext);
   1.937 +-
   1.938 +-	CGContextSetCTM (surface->cgContext, origCTM);
   1.939 +-	_cairo_quartz_draw_image (surface, op, action);
   1.940 +-    } else if (action == DO_SHADING) {
   1.941 +-	CGContextReplacePathWithStrokedPath (surface->cgContext);
   1.942 +-	CGContextClip (surface->cgContext);
   1.943 +-
   1.944 +-	CGContextSetCTM (surface->cgContext, origCTM);
   1.945 +-
   1.946 +-	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   1.947 +-	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   1.948 +-    } else if (action != DO_NOTHING) {
   1.949 ++	path_for_unbounded = CGContextCopyPathPtr (state.context);
   1.950 ++
   1.951 ++    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   1.952 ++	CGContextStrokePath (state.context);
   1.953 ++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   1.954 ++	CGContextReplacePathWithStrokedPath (state.context);
   1.955 ++	CGContextClip (state.context);
   1.956 ++
   1.957 ++	CGContextSetCTM (state.context, origCTM);
   1.958 ++	_cairo_quartz_draw_image (&state, op);
   1.959 ++    } else if (state.action == DO_SHADING) {
   1.960 ++	CGContextReplacePathWithStrokedPath (state.context);
   1.961 ++	CGContextClip (state.context);
   1.962 ++
   1.963 ++	CGContextSetCTM (state.context, origCTM);
   1.964 ++
   1.965 ++	CGContextConcatCTM (state.context, state.transform);
   1.966 ++	CGContextDrawShading (state.context, state.shading);
   1.967 ++    } else if (state.action != DO_NOTHING) {
   1.968 + 	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   1.969 ++	goto BAIL;
   1.970 +     }
   1.971 + 
   1.972 ++    if (path_for_unbounded) {
   1.973 ++	CGContextSetCTM (state.context, origCTM);
   1.974 ++	CGContextConcatCTM (state.context, strokeTransform);
   1.975 ++
   1.976 ++	CGContextBeginPath (state.context);
   1.977 ++	CGContextAddPath (state.context, path_for_unbounded);
   1.978 ++	CGPathRelease (path_for_unbounded);
   1.979 ++
   1.980 ++	CGContextReplacePathWithStrokedPath (state.context);
   1.981 ++
   1.982 ++	CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context));
   1.983 ++
   1.984 ++	CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.);
   1.985 ++	CGContextEOFillPath (state.context);
   1.986 ++    }
   1.987 ++
   1.988 +   BAIL:
   1.989 +-    _cairo_quartz_teardown_source (surface, source);
   1.990 +-
   1.991 +-    CGContextRestoreGState (surface->cgContext);
   1.992 +-
   1.993 +-    if (path_for_unbounded) {
   1.994 +-	CGContextSaveGState (surface->cgContext);
   1.995 +-	CGContextConcatCTM (surface->cgContext, strokeTransform);
   1.996 +-
   1.997 +-	CGContextBeginPath (surface->cgContext);
   1.998 +-	CGContextAddPath (surface->cgContext, path_for_unbounded);
   1.999 +-	CGPathRelease (path_for_unbounded);
  1.1000 +-
  1.1001 +-	CGContextReplacePathWithStrokedPath (surface->cgContext);
  1.1002 +-
  1.1003 +-	CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
  1.1004 +-
  1.1005 +-	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
  1.1006 +-	CGContextEOFillPath (surface->cgContext);
  1.1007 +-
  1.1008 +-	CGContextRestoreGState (surface->cgContext);
  1.1009 +-    }
  1.1010 ++    _cairo_quartz_teardown_state (&state);
  1.1011 + 
  1.1012 +     ND((stderr, "-- stroke\n"));
  1.1013 +     return rv;
  1.1014 + }
  1.1015 + 
  1.1016 + #if CAIRO_HAS_QUARTZ_FONT
  1.1017 + static cairo_int_status_t
  1.1018 + _cairo_quartz_surface_show_glyphs (void *abstract_surface,
  1.1019 +@@ -2429,17 +2468,17 @@ _cairo_quartz_surface_show_glyphs (void 
  1.1020 + #define STATIC_BUF_SIZE 64
  1.1021 +     CGGlyph glyphs_static[STATIC_BUF_SIZE];
  1.1022 +     CGSize cg_advances_static[STATIC_BUF_SIZE];
  1.1023 +     CGGlyph *cg_glyphs = &glyphs_static[0];
  1.1024 +     CGSize *cg_advances = &cg_advances_static[0];
  1.1025 + 
  1.1026 +     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
  1.1027 +     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
  1.1028 +-    cairo_quartz_action_t action;
  1.1029 ++    cairo_quartz_drawing_state_t state;
  1.1030 +     float xprev, yprev;
  1.1031 +     int i;
  1.1032 +     CGFontRef cgfref = NULL;
  1.1033 + 
  1.1034 +     cairo_bool_t isClipping = FALSE;
  1.1035 +     cairo_bool_t didForceFontSmoothing = FALSE;
  1.1036 + 
  1.1037 +     if (IS_EMPTY(surface))
  1.1038 +@@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void 
  1.1039 + 
  1.1040 +     if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
  1.1041 + 	return CAIRO_INT_STATUS_UNSUPPORTED;
  1.1042 + 
  1.1043 +     rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
  1.1044 +     if (unlikely (rv))
  1.1045 + 	return rv;
  1.1046 + 
  1.1047 +-    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
  1.1048 +-    if (unlikely (rv))
  1.1049 +-	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
  1.1050 +-
  1.1051 +-    CGContextSaveGState (surface->cgContext);
  1.1052 +-
  1.1053 +     if (_cairo_quartz_source_needs_extents (source))
  1.1054 +     {
  1.1055 +         cairo_rectangle_int_t glyph_extents;
  1.1056 +         _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
  1.1057 +                                                  &glyph_extents, NULL);
  1.1058 +-        action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
  1.1059 ++        state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
  1.1060 +     } else {
  1.1061 +-        action = _cairo_quartz_setup_source (surface, source, NULL);
  1.1062 ++        state = _cairo_quartz_setup_state (surface, source, op, NULL);
  1.1063 +     }
  1.1064 + 
  1.1065 +-    if (action == DO_SOLID || action == DO_PATTERN) {
  1.1066 +-	CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
  1.1067 +-    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
  1.1068 +-	CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
  1.1069 ++    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
  1.1070 ++	CGContextSetTextDrawingMode (state.context, kCGTextFill);
  1.1071 ++    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) {
  1.1072 ++	CGContextSetTextDrawingMode (state.context, kCGTextClip);
  1.1073 + 	isClipping = TRUE;
  1.1074 +     } else {
  1.1075 +-	if (action != DO_NOTHING)
  1.1076 ++	if (state.action != DO_NOTHING)
  1.1077 + 	    rv = CAIRO_INT_STATUS_UNSUPPORTED;
  1.1078 + 	goto BAIL;
  1.1079 +     }
  1.1080 + 
  1.1081 +     /* this doesn't addref */
  1.1082 +     cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
  1.1083 +-    CGContextSetFont (surface->cgContext, cgfref);
  1.1084 +-    CGContextSetFontSize (surface->cgContext, 1.0);
  1.1085 ++    CGContextSetFont (state.context, cgfref);
  1.1086 ++    CGContextSetFontSize (state.context, 1.0);
  1.1087 + 
  1.1088 +     switch (scaled_font->options.antialias) {
  1.1089 + 	case CAIRO_ANTIALIAS_SUBPIXEL:
  1.1090 +-	    CGContextSetShouldAntialias (surface->cgContext, TRUE);
  1.1091 +-	    CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
  1.1092 ++	    CGContextSetShouldAntialias (state.context, TRUE);
  1.1093 ++	    CGContextSetShouldSmoothFonts (state.context, TRUE);
  1.1094 + 	    if (CGContextSetAllowsFontSmoothingPtr &&
  1.1095 +-		!CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
  1.1096 ++		!CGContextGetAllowsFontSmoothingPtr (state.context))
  1.1097 + 	    {
  1.1098 + 		didForceFontSmoothing = TRUE;
  1.1099 +-		CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
  1.1100 ++		CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
  1.1101 + 	    }
  1.1102 + 	    break;
  1.1103 + 	case CAIRO_ANTIALIAS_NONE:
  1.1104 +-	    CGContextSetShouldAntialias (surface->cgContext, FALSE);
  1.1105 ++	    CGContextSetShouldAntialias (state.context, FALSE);
  1.1106 + 	    break;
  1.1107 + 	case CAIRO_ANTIALIAS_GRAY:
  1.1108 +-	    CGContextSetShouldAntialias (surface->cgContext, TRUE);
  1.1109 +-	    CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
  1.1110 ++	    CGContextSetShouldAntialias (state.context, TRUE);
  1.1111 ++	    CGContextSetShouldSmoothFonts (state.context, FALSE);
  1.1112 + 	    break;
  1.1113 + 	case CAIRO_ANTIALIAS_DEFAULT:
  1.1114 + 	    /* Don't do anything */
  1.1115 + 	    break;
  1.1116 +     }
  1.1117 + 
  1.1118 +     if (num_glyphs > STATIC_BUF_SIZE) {
  1.1119 + 	cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
  1.1120 +@@ -2532,17 +2565,17 @@ _cairo_quartz_surface_show_glyphs (void 
  1.1121 +     textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
  1.1122 +     textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
  1.1123 + 								   -scaled_font->ctm.yx,
  1.1124 + 								   -scaled_font->ctm.xy,
  1.1125 + 								   scaled_font->ctm.yy,
  1.1126 + 								   0., 0.),
  1.1127 + 					     textTransform);
  1.1128 + 
  1.1129 +-    CGContextSetTextMatrix (surface->cgContext, textTransform);
  1.1130 ++    CGContextSetTextMatrix (state.context, textTransform);
  1.1131 + 
  1.1132 +     /* Convert our glyph positions to glyph advances.  We need n-1 advances,
  1.1133 +      * since the advance at index 0 is applied after glyph 0. */
  1.1134 +     xprev = glyphs[0].x;
  1.1135 +     yprev = glyphs[0].y;
  1.1136 + 
  1.1137 +     cg_glyphs[0] = glyphs[0].index;
  1.1138 + 
  1.1139 +@@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void 
  1.1140 + 
  1.1141 + #if 0
  1.1142 +     for (i = 0; i < num_glyphs; i++) {
  1.1143 + 	ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
  1.1144 +     }
  1.1145 + #endif
  1.1146 + 
  1.1147 +     /* Translate to the first glyph's position before drawing */
  1.1148 +-    ctm = CGContextGetCTM (surface->cgContext);
  1.1149 +-    CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
  1.1150 +-
  1.1151 +-    CGContextShowGlyphsWithAdvances (surface->cgContext,
  1.1152 ++    ctm = CGContextGetCTM (state.context);
  1.1153 ++    CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
  1.1154 ++
  1.1155 ++    CGContextShowGlyphsWithAdvances (state.context,
  1.1156 + 				     cg_glyphs,
  1.1157 + 				     cg_advances,
  1.1158 + 				     num_glyphs);
  1.1159 + 
  1.1160 +-    CGContextSetCTM (surface->cgContext, ctm);
  1.1161 +-
  1.1162 +-    if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
  1.1163 +-	_cairo_quartz_draw_image (surface, op, action);
  1.1164 +-    } else if (action == DO_SHADING) {
  1.1165 +-	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
  1.1166 +-	CGContextDrawShading (surface->cgContext, surface->sourceShading);
  1.1167 ++    CGContextSetCTM (state.context, ctm);
  1.1168 ++
  1.1169 ++    if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
  1.1170 ++	_cairo_quartz_draw_image (&state, op);
  1.1171 ++    } else if (state.action == DO_SHADING) {
  1.1172 ++	CGContextConcatCTM (state.context, state.transform);
  1.1173 ++	CGContextDrawShading (state.context, state.shading);
  1.1174 +     }
  1.1175 + 
  1.1176 + BAIL:
  1.1177 +-    _cairo_quartz_teardown_source (surface, source);
  1.1178 +-
  1.1179 +     if (didForceFontSmoothing)
  1.1180 +-	CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
  1.1181 +-
  1.1182 +-    CGContextRestoreGState (surface->cgContext);
  1.1183 ++        CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
  1.1184 ++
  1.1185 ++    _cairo_quartz_teardown_state (&state);
  1.1186 + 
  1.1187 +     if (rv == CAIRO_STATUS_SUCCESS &&
  1.1188 + 	cgfref &&
  1.1189 + 	!_cairo_operator_bounded_by_mask (op))
  1.1190 +     {
  1.1191 + 	unbounded_op_data_t ub;
  1.1192 + 	ub.op = UNBOUNDED_SHOW_GLYPHS;
  1.1193 + 

mercurial