gfx/cairo/quartz-state.patch

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h
     2 --- a/gfx/cairo/cairo/src/cairo-quartz-private.h
     3 +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h
     4 @@ -50,30 +50,16 @@ typedef struct cairo_quartz_surface {
     5      CGContextRef cgContext;
     6      CGAffineTransform cgContextBaseCTM;
     8      void *imageData;
     9      cairo_surface_t *imageSurfaceEquiv;
    11      cairo_surface_clipper_t clipper;
    12      cairo_rectangle_int_t extents;
    13 -
    14 -    /* These are stored while drawing operations are in place, set up
    15 -     * by quartz_setup_source() and quartz_finish_source()
    16 -     */
    17 -    CGAffineTransform sourceTransform;
    18 -
    19 -    CGImageRef sourceImage;
    20 -    cairo_surface_t *sourceImageSurface;
    21 -    CGRect sourceImageRect;
    22 -
    23 -    CGShadingRef sourceShading;
    24 -    CGPatternRef sourcePattern;
    25 -
    26 -    CGInterpolationQuality oldInterpolationQuality;
    27  } cairo_quartz_surface_t;
    29  typedef struct cairo_quartz_image_surface {
    30      cairo_surface_t base;
    32      cairo_rectangle_int_t extents;
    34      CGImageRef image;
    35 diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    36 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
    37 +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
    38 @@ -1333,36 +1333,59 @@ _cairo_quartz_cairo_repeating_surface_pa
    39      return CAIRO_STATUS_SUCCESS;
    40  }
    42  typedef enum {
    43      DO_SOLID,
    44      DO_SHADING,
    45      DO_PATTERN,
    46      DO_IMAGE,
    47 +    DO_TILED_IMAGE,
    48      DO_UNSUPPORTED,
    49 -    DO_NOTHING,
    50 -    DO_TILED_IMAGE
    51 +    DO_NOTHING
    52  } cairo_quartz_action_t;
    54 -static cairo_quartz_action_t
    55 +/* State used during a drawing operation. */
    56 +typedef struct {
    57 +    CGContextRef context;
    58 +    cairo_quartz_action_t action;
    59 +
    60 +    // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE
    61 +    CGAffineTransform transform;
    62 +
    63 +    // Used with DO_IMAGE and DO_TILED_IMAGE
    64 +    CGImageRef image;
    65 +    cairo_surface_t *imageSurface;
    66 +    CGRect imageRect;
    67 +
    68 +    // Used with DO_SHADING
    69 +    CGShadingRef shading;
    70 +
    71 +    // Used with DO_PATTERN
    72 +    CGPatternRef pattern;
    73 +} cairo_quartz_drawing_state_t;
    74 +
    75 +static void
    76  _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
    77 -				     const cairo_pattern_t *source)
    78 +				     const cairo_pattern_t *source,
    79 +				     cairo_quartz_drawing_state_t *state)
    80  {
    81 -    CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
    82 +    CGRect clipBox = CGContextGetClipBoundingBox (state->context);
    83      double x0, y0, w, h;
    85      cairo_surface_t *fallback;
    86      CGImageRef img;
    88      cairo_status_t status;
    90      if (clipBox.size.width == 0.0f ||
    91 -	clipBox.size.height == 0.0f)
    92 -	return DO_NOTHING;
    93 +	clipBox.size.height == 0.0f) {
    94 +	state->action = DO_NOTHING;
    95 +	return;
    96 +    }
    98      x0 = floor(clipBox.origin.x);
    99      y0 = floor(clipBox.origin.y);
   100      w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
   101      h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
   103      /* Create a temporary the size of the clip surface, and position
   104       * it so that the device origin coincides with the original surface */
   105 @@ -1396,73 +1419,79 @@ _cairo_quartz_setup_fallback_source (cai
   106  				  &fallback->device_transform_inverse);
   107  	status = _cairo_surface_paint (fallback,
   108  				       CAIRO_OPERATOR_SOURCE,
   109  				       &pattern.base, NULL);
   110      }
   111  #endif
   113      status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
   114 -    if (status)
   115 -	return DO_UNSUPPORTED;
   116 -    if (img == NULL)
   117 -	return DO_NOTHING;
   118 -
   119 -    surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
   120 -    surface->sourceImage = img;
   121 -    surface->sourceImageSurface = fallback;
   122 -    surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
   123 -
   124 -    return DO_IMAGE;
   125 +    if (status) {
   126 +        state->action = DO_UNSUPPORTED;
   127 +	return;
   128 +    }
   129 +    if (img == NULL) {
   130 +        state->action = DO_NOTHING;
   131 +	return;
   132 +    }
   133 +
   134 +    state->imageRect = CGRectMake (0.0, 0.0, w, h);
   135 +    state->image = img;
   136 +    state->imageSurface = fallback;
   137 +    state->transform = CGAffineTransformMakeTranslation (x0, y0);
   138 +    state->action = DO_IMAGE;
   139  }
   141  /*
   142  Quartz does not support repeating radients. We handle repeating gradients
   143  by manually extending the gradient and repeating color stops. We need to
   144  minimize the number of repetitions since Quartz seems to sample our color
   145  function across the entire range, even if part of that range is not needed
   146  for the visible area of the gradient, and it samples with some fixed resolution,
   147  so if the gradient range is too large it samples with very low resolution and
   148  the gradient is very coarse. CreateRepeatingLinearGradientFunction and
   149  CreateRepeatingRadialGradientFunction compute the number of repetitions needed
   150  based on the extents of the object (the clip region cannot be used here since
   151  we don't want the rasterization of the entire gradient to depend on the
   152  clip region).
   153  */
   154 -static cairo_quartz_action_t
   155 +static void
   156  _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
   157  				   const cairo_linear_pattern_t *lpat,
   158 -				   cairo_rectangle_int_t *extents)
   159 +				   cairo_rectangle_int_t *extents,
   160 +				   cairo_quartz_drawing_state_t *state)
   161  {
   162      const cairo_pattern_t *abspat = &lpat->base.base;
   163      cairo_matrix_t mat;
   164      CGPoint start, end;
   165      CGFunctionRef gradFunc;
   166      CGColorSpaceRef rgb;
   167      bool extend = abspat->extend == CAIRO_EXTEND_PAD;
   169      if (lpat->base.n_stops == 0) {
   170 -	CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
   171 -	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
   172 -	return DO_SOLID;
   173 +	CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
   174 +	CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
   175 +	state->action = DO_SOLID;
   176 +	return;
   177      }
   179      if (lpat->p1.x == lpat->p2.x &&
   180          lpat->p1.y == lpat->p2.y) {
   181  	/* Quartz handles cases where the vector has no length very
   182  	 * differently from pixman.
   183  	 * Whatever the correct behaviour is, let's at least have only pixman's
   184  	 * implementation to worry about.
   185  	 */
   186 -	return _cairo_quartz_setup_fallback_source (surface, abspat);
   187 +	_cairo_quartz_setup_fallback_source (surface, abspat, state);
   188 +	return;
   189      }
   191      mat = abspat->matrix;
   192      cairo_matrix_invert (&mat);
   193 -    _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
   194 +    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
   196      rgb = CGColorSpaceCreateDeviceRGB();
   198      start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
   199  			 _cairo_fixed_to_double (lpat->p1.y));
   200      end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
   201  		       _cairo_fixed_to_double (lpat->p2.y));
   203 @@ -1472,31 +1501,32 @@ _cairo_quartz_setup_linear_source (cairo
   204  	gradFunc = CreateGradientFunction (&lpat->base);
   205      } else {
   206  	gradFunc = CreateRepeatingLinearGradientFunction (surface,
   207  						          &lpat->base,
   208  						          &start, &end,
   209  						          extents);
   210      }
   212 -    surface->sourceShading = CGShadingCreateAxial (rgb,
   213 -						   start, end,
   214 -						   gradFunc,
   215 -						   extend, extend);
   216 +    state->shading = CGShadingCreateAxial (rgb,
   217 +					   start, end,
   218 +					   gradFunc,
   219 +					   extend, extend);
   221      CGColorSpaceRelease(rgb);
   222      CGFunctionRelease(gradFunc);
   224 -    return DO_SHADING;
   225 +    state->action = DO_SHADING;
   226  }
   228 -static cairo_quartz_action_t
   229 +static void
   230  _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
   231  				   const cairo_radial_pattern_t *rpat,
   232 -				   cairo_rectangle_int_t *extents)
   233 +				   cairo_rectangle_int_t *extents,
   234 +				   cairo_quartz_drawing_state_t *state)
   235  {
   236      const cairo_pattern_t *abspat = &rpat->base.base;
   237      cairo_matrix_t mat;
   238      CGPoint start, end;
   239      CGFunctionRef gradFunc;
   240      CGColorSpaceRef rgb;
   241      bool extend = abspat->extend == CAIRO_EXTEND_PAD;
   242      double c1x = _cairo_fixed_to_double (rpat->c1.x);
   243 @@ -1505,35 +1535,37 @@ _cairo_quartz_setup_radial_source (cairo
   244      double c2y = _cairo_fixed_to_double (rpat->c2.y);
   245      double r1 = _cairo_fixed_to_double (rpat->r1);
   246      double r2 = _cairo_fixed_to_double (rpat->r2);
   247      double dx = c1x - c2x;
   248      double dy = c1y - c2y;
   249      double centerDistance = sqrt (dx*dx + dy*dy);
   251      if (rpat->base.n_stops == 0) {
   252 -	CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
   253 -	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
   254 -	return DO_SOLID;
   255 +	CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
   256 +	CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
   257 +	state->action = DO_SOLID;
   258 +	return;
   259      }
   261      if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
   262          r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
   263  	/* Quartz handles cases where neither circle contains the other very
   264  	 * differently from pixman.
   265  	 * Whatever the correct behaviour is, let's at least have only pixman's
   266  	 * implementation to worry about.
   267  	 * Note that this also catches the cases where r1 == r2.
   268  	 */
   269 -	return _cairo_quartz_setup_fallback_source (surface, abspat);
   270 +	_cairo_quartz_setup_fallback_source (surface, abspat, state);
   271 +	return;
   272      }
   274      mat = abspat->matrix;
   275      cairo_matrix_invert (&mat);
   276 -    _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
   277 +    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
   279      rgb = CGColorSpaceCreateDeviceRGB();
   281      start = CGPointMake (c1x, c1y);
   282      end = CGPointMake (c2x, c2y);
   284      if (abspat->extend == CAIRO_EXTEND_NONE ||
   285          abspat->extend == CAIRO_EXTEND_PAD)
   286 @@ -1542,111 +1574,146 @@ _cairo_quartz_setup_radial_source (cairo
   287      } else {
   288  	gradFunc = CreateRepeatingRadialGradientFunction (surface,
   289  						          &rpat->base,
   290  						          &start, &r1,
   291  						          &end, &r2,
   292  						          extents);
   293      }
   295 -    surface->sourceShading = CGShadingCreateRadial (rgb,
   296 -						    start,
   297 -						    r1,
   298 -						    end,
   299 -						    r2,
   300 -						    gradFunc,
   301 -						    extend, extend);
   302 +    state->shading = CGShadingCreateRadial (rgb,
   303 +					    start,
   304 +					    r1,
   305 +					    end,
   306 +					    r2,
   307 +					    gradFunc,
   308 +					    extend, extend);
   310      CGColorSpaceRelease(rgb);
   311      CGFunctionRelease(gradFunc);
   313 -    return DO_SHADING;
   314 +    state->action = DO_SHADING;
   315  }
   317 -static cairo_quartz_action_t
   318 -_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
   319 -			    const cairo_pattern_t *source,
   320 -			    cairo_rectangle_int_t *extents)
   321 +/**
   322 + * Sets up internal state to be used to draw the source mask, stored in
   323 + * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
   324 + * surface->cgContext.
   325 + */
   326 +static cairo_quartz_drawing_state_t
   327 +_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
   328 +			   const cairo_pattern_t *source,
   329 +			   cairo_operator_t op,
   330 +			   cairo_rectangle_int_t *extents)
   331  {
   332 -    assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
   333 -
   334 -    surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
   335 -    CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
   336 +    CGContextRef context = surface->cgContext;
   337 +    cairo_quartz_drawing_state_t state;
   338 +    cairo_status_t status;
   339 +
   340 +    state.context = context;
   341 +    state.image = NULL;
   342 +    state.imageSurface = NULL;
   343 +    state.shading = NULL;
   344 +    state.pattern = NULL;
   345 +
   346 +    // Save before we change the pattern, colorspace, etc. so that
   347 +    // we can restore and make sure that quartz releases our
   348 +    // pattern (which may be stack allocated)
   349 +    CGContextSaveGState(context);
   350 +
   351 +    CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
   352 +
   353 +    status = _cairo_quartz_surface_set_cairo_operator (surface, op);
   354 +    if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
   355 +        state.action = DO_NOTHING;
   356 +        return state;
   357 +    }
   358 +    if (status) {
   359 +        state.action = DO_UNSUPPORTED;
   360 +        return state;
   361 +    }
   363      if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
   364  	cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
   366 -	CGContextSetRGBStrokeColor (surface->cgContext,
   367 +	CGContextSetRGBStrokeColor (context,
   368  				    solid->color.red,
   369  				    solid->color.green,
   370  				    solid->color.blue,
   371  				    solid->color.alpha);
   372 -	CGContextSetRGBFillColor (surface->cgContext,
   373 +	CGContextSetRGBFillColor (context,
   374  				  solid->color.red,
   375  				  solid->color.green,
   376  				  solid->color.blue,
   377  				  solid->color.alpha);
   379 -	return DO_SOLID;
   380 +        state.action = DO_SOLID;
   381 +        return state;
   382      }
   384      if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
   385  	const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
   386 -	return _cairo_quartz_setup_linear_source (surface, lpat, extents);
   387 +	_cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
   388 +	return state;
   389      }
   391      if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
   392  	const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
   393 -	return _cairo_quartz_setup_radial_source (surface, rpat, extents);
   394 +	_cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
   395 +	return state;
   396      }
   398      if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
   399  	(source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
   400      {
   401  	const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
   402  	cairo_surface_t *pat_surf = spat->surface;
   403  	CGImageRef img;
   404  	cairo_matrix_t m = spat->base.matrix;
   405  	cairo_rectangle_int_t extents;
   406 -	cairo_status_t status;
   407  	CGAffineTransform xform;
   408  	CGRect srcRect;
   409  	cairo_fixed_t fw, fh;
   410  	cairo_bool_t is_bounded;
   412  	status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
   413 -	if (status)
   414 -	    return DO_UNSUPPORTED;
   415 -	if (img == NULL)
   416 -	    return DO_NOTHING;
   417 +        if (status) {
   418 +            state.action = DO_UNSUPPORTED;
   419 +	    return state;
   420 +        }
   421 +        if (img == NULL) {
   422 +            state.action = DO_NOTHING;
   423 +	    return state;
   424 +        }
   426  	CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
   428 -	surface->sourceImage = img;
   429 +	state.image = img;
   431  	cairo_matrix_invert(&m);
   432 -	_cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
   433 +	_cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform);
   435  	is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
   436  	assert (is_bounded);
   438  	if (source->extend == CAIRO_EXTEND_NONE) {
   439 -	    surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
   440 -	    return DO_IMAGE;
   441 +	    state.imageRect = CGRectMake (0, 0, extents.width, extents.height);
   442 +	    state.action = DO_IMAGE;
   443 +	    return state;
   444  	}
   446  	/* Quartz seems to tile images at pixel-aligned regions only -- this
   447  	 * leads to seams if the image doesn't end up scaling to fill the
   448  	 * space exactly.  The CGPattern tiling approach doesn't have this
   449  	 * problem.  Check if we're going to fill up the space (within some
   450  	 * epsilon), and if not, fall back to the CGPattern type.
   451  	 */
   453 -	xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
   454 -					 surface->sourceTransform);
   455 +	xform = CGAffineTransformConcat (CGContextGetCTM (context),
   456 +					 state.transform);
   458  	srcRect = CGRectMake (0, 0, extents.width, extents.height);
   459  	srcRect = CGRectApplyAffineTransform (srcRect, xform);
   461  	fw = _cairo_fixed_from_double (srcRect.size.width);
   462  	fh = _cairo_fixed_from_double (srcRect.size.height);
   464  	if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
   465 @@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz
   467  	    srcRect.size.width = round(srcRect.size.width);
   468  	    srcRect.size.height = round(srcRect.size.height);
   470  	    xform = CGAffineTransformInvert (xform);
   472  	    srcRect = CGRectApplyAffineTransform (srcRect, xform);
   474 -	    surface->sourceImageRect = srcRect;
   475 -
   476 -	    return DO_TILED_IMAGE;
   477 +	    state.imageRect = srcRect;
   478 +            state.action = DO_TILED_IMAGE;
   479 +            return state;
   480  	}
   482  	/* Fall through to generic SURFACE case */
   483      }
   485      if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
   486  	CGFloat patternAlpha = 1.0f;
   487  	CGColorSpaceRef patternSpace;
   488  	CGPatternRef pattern;
   489  	cairo_int_status_t status;
   491  	status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
   492 -	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
   493 -	    return DO_NOTHING;
   494 -	if (status)
   495 -	    return DO_UNSUPPORTED;
   496 -
   497 -	// Save before we change the pattern, colorspace, etc. so that
   498 -	// we can restore and make sure that quartz releases our
   499 -	// pattern (which may be stack allocated)
   500 -	CGContextSaveGState(surface->cgContext);
   501 -
   502 -	patternSpace = CGColorSpaceCreatePattern(NULL);
   503 -	CGContextSetFillColorSpace (surface->cgContext, patternSpace);
   504 -	CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
   505 -	CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
   506 -	CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
   507 +	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
   508 +	    state.action = DO_NOTHING;
   509 +	    return state;
   510 +	}
   511 +	if (status) {
   512 +	    state.action = DO_UNSUPPORTED;
   513 +	    return state;
   514 +	}
   515 +
   516 +	patternSpace = CGColorSpaceCreatePattern (NULL);
   517 +	CGContextSetFillColorSpace (context, patternSpace);
   518 +	CGContextSetFillPattern (context, pattern, &patternAlpha);
   519 +	CGContextSetStrokeColorSpace (context, patternSpace); 
   520 +	CGContextSetStrokePattern (context, pattern, &patternAlpha);
   521  	CGColorSpaceRelease (patternSpace);
   523  	/* Quartz likes to munge the pattern phase (as yet unexplained
   524  	 * why); force it to 0,0 as we've already baked in the correct
   525  	 * pattern translation into the pattern matrix
   526  	 */
   527 -	CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
   528 -
   529 -	surface->sourcePattern = pattern;
   530 -
   531 -	return DO_PATTERN;
   532 +	CGContextSetPatternPhase (context, CGSizeMake(0,0));
   533 +
   534 +	state.pattern = pattern;
   535 +        state.action = DO_PATTERN;
   536 +        return state;
   537      }
   539 -    return DO_UNSUPPORTED;
   540 +    state.action = DO_UNSUPPORTED;
   541 +    return state;
   542  }
   544 +/**
   545 + * 1) Tears down internal state used to draw the source
   546 + * 2) Does CGContextRestoreGState(state->context)
   547 + */
   548  static void
   549 -_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
   550 -			       const cairo_pattern_t *source)
   551 +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
   552  {
   553 -    CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
   554 -
   555 -    if (surface->sourceImage) {
   556 -	CGImageRelease(surface->sourceImage);
   557 -	surface->sourceImage = NULL;
   558 -
   559 -	cairo_surface_destroy(surface->sourceImageSurface);
   560 -	surface->sourceImageSurface = NULL;
   561 +    if (state->image) {
   562 +	CGImageRelease(state->image);
   563      }
   565 -    if (surface->sourceShading) {
   566 -	CGShadingRelease(surface->sourceShading);
   567 -	surface->sourceShading = NULL;
   568 +    if (state->imageSurface) {
   569 +	cairo_surface_destroy(state->imageSurface);
   570      }
   572 -    if (surface->sourcePattern) {
   573 -	CGPatternRelease(surface->sourcePattern);
   574 -	// To tear down the pattern and colorspace
   575 -	CGContextRestoreGState(surface->cgContext);
   576 -
   577 -	surface->sourcePattern = NULL;
   578 +    if (state->shading) {
   579 +	CGShadingRelease(state->shading);
   580      }
   581 +
   582 +    if (state->pattern) {
   583 +	CGPatternRelease(state->pattern);
   584 +    }
   585 +
   586 +    CGContextRestoreGState(state->context);
   587  }
   590  static void
   591 -_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op,  cairo_quartz_action_t action)
   592 +_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
   593  {
   594 -    assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
   595 -
   596 -    CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   597 -    CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
   598 -    CGContextScaleCTM (surface->cgContext, 1, -1);
   599 -
   600 -    if (action == DO_IMAGE) {
   601 -	CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
   602 -	if (!_cairo_operator_bounded_by_source(op)) {
   603 -	    CGContextBeginPath (surface->cgContext);
   604 -	    CGContextAddRect (surface->cgContext, surface->sourceImageRect);
   605 -	    CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
   606 -	    CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
   607 -	    CGContextEOFillPath (surface->cgContext);
   608 +    assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE));
   609 +
   610 +    CGContextConcatCTM (state->context, state->transform);
   611 +    CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
   612 +    CGContextScaleCTM (state->context, 1, -1);
   613 +
   614 +    if (state->action == DO_IMAGE) {
   615 +	CGContextDrawImage (state->context, state->imageRect, state->image);
   616 +	if (!_cairo_operator_bounded_by_source (op)) {
   617 +	    CGContextBeginPath (state->context);
   618 +	    CGContextAddRect (state->context, state->imageRect);
   619 +	    CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
   620 +	    CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
   621 +	    CGContextEOFillPath (state->context);
   622  	}
   623      } else
   624 -	CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
   625 +	CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
   626  }
   629  /*
   630   * get source/dest image implementation
   631   */
   633  /* Read the image from the surface's front buffer */
   634 @@ -2098,52 +2163,44 @@ _cairo_quartz_surface_get_extents (void 
   635  static cairo_int_status_t
   636  _cairo_quartz_surface_paint (void *abstract_surface,
   637  			     cairo_operator_t op,
   638  			     const cairo_pattern_t *source,
   639  			     cairo_clip_t *clip)
   640  {
   641      cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   642      cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   643 -    cairo_quartz_action_t action;
   644 +    cairo_quartz_drawing_state_t state;
   646      ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
   648      if (IS_EMPTY(surface))
   649  	return CAIRO_STATUS_SUCCESS;
   651      rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   652      if (unlikely (rv))
   653  	return rv;
   655 -    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   656 -    if (unlikely (rv))
   657 -	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   658 -
   659 -    action = _cairo_quartz_setup_source (surface, source, NULL);
   660 -
   661 -    if (action == DO_SOLID || action == DO_PATTERN) {
   662 -	CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
   663 -							  surface->extents.y,
   664 -							  surface->extents.width,
   665 -							  surface->extents.height));
   666 -    } else if (action == DO_SHADING) {
   667 -	CGContextSaveGState (surface->cgContext);
   668 -	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   669 -	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   670 -	CGContextRestoreGState (surface->cgContext);
   671 -    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   672 -	CGContextSaveGState (surface->cgContext);
   673 -	_cairo_quartz_draw_image (surface, op, action);
   674 -	CGContextRestoreGState (surface->cgContext);
   675 -    } else if (action != DO_NOTHING) {
   676 +    state = _cairo_quartz_setup_state (surface, source, op, NULL);
   677 +
   678 +    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   679 +	CGContextFillRect (state.context, CGRectMake(surface->extents.x,
   680 +						     surface->extents.y,
   681 +						     surface->extents.width,
   682 +						     surface->extents.height));
   683 +    } else if (state.action == DO_SHADING) {
   684 +	CGContextConcatCTM (state.context, state.transform);
   685 +	CGContextDrawShading (state.context, state.shading);
   686 +    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   687 +	_cairo_quartz_draw_image (&state, op);
   688 +    } else if (state.action != DO_NOTHING) {
   689  	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   690      }
   692 -    _cairo_quartz_teardown_source (surface, source);
   693 +    _cairo_quartz_teardown_state (&state);
   695      ND((stderr, "-- paint\n"));
   696      return rv;
   697  }
   699  static cairo_bool_t
   700  _cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
   701  {
   702 @@ -2170,91 +2227,83 @@ _cairo_quartz_surface_fill (void *abstra
   703  			     cairo_path_fixed_t *path,
   704  			     cairo_fill_rule_t fill_rule,
   705  			     double tolerance,
   706  			     cairo_antialias_t antialias,
   707  			     cairo_clip_t *clip)
   708  {
   709      cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   710      cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   711 -    cairo_quartz_action_t action;
   712 +    cairo_quartz_drawing_state_t state;
   713      quartz_stroke_t stroke;
   714      CGPathRef path_for_unbounded = NULL;
   716      ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
   718      if (IS_EMPTY(surface))
   719  	return CAIRO_STATUS_SUCCESS;
   721      rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   722      if (unlikely (rv))
   723  	return rv;
   725 -    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   726 -    if (unlikely (rv))
   727 -	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   728 -
   729 -    CGContextSaveGState (surface->cgContext);
   730 -
   731 -    CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
   732 -
   733      if (_cairo_quartz_source_needs_extents (source))
   734      {
   735          /* We don't need precise extents since these are only used to
   736             compute the number of gradient reptitions needed to cover the
   737             object. */
   738          cairo_rectangle_int_t path_extents;
   739          _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
   740 -        action = _cairo_quartz_setup_source (surface, source, &path_extents);
   741 +        state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
   742      } else {
   743 -        action = _cairo_quartz_setup_source (surface, source, NULL);
   744 +        state = _cairo_quartz_setup_state (surface, source, op, NULL);
   745      }
   747 -    CGContextBeginPath (surface->cgContext);
   748 -
   749 -    stroke.cgContext = surface->cgContext;
   750 +    CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
   751 +
   752 +    CGContextBeginPath (state.context);
   753 +
   754 +    stroke.cgContext = state.context;
   755      stroke.ctm_inverse = NULL;
   756      rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
   757      if (rv)
   758          goto BAIL;
   760      if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
   761 -	path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
   762 -
   763 -    if (action == DO_SOLID || action == DO_PATTERN) {
   764 +	path_for_unbounded = CGContextCopyPathPtr (state.context);
   765 +
   766 +    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   767  	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   768 -	    CGContextFillPath (surface->cgContext);
   769 +	    CGContextFillPath (state.context);
   770  	else
   771 -	    CGContextEOFillPath (surface->cgContext);
   772 -    } else if (action == DO_SHADING) {
   773 +	    CGContextEOFillPath (state.context);
   774 +    } else if (state.action == DO_SHADING) {
   776  	// we have to clip and then paint the shading; we can't fill
   777  	// with the shading
   778  	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   779 -	    CGContextClip (surface->cgContext);
   780 +	    CGContextClip (state.context);
   781  	else
   782 -	    CGContextEOClip (surface->cgContext);
   783 -
   784 -	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   785 -	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   786 -    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   787 +            CGContextEOClip (state.context);
   788 +
   789 +	CGContextConcatCTM (state.context, state.transform);
   790 +	CGContextDrawShading (state.context, state.shading);
   791 +    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   792  	if (fill_rule == CAIRO_FILL_RULE_WINDING)
   793 -	    CGContextClip (surface->cgContext);
   794 +	    CGContextClip (state.context);
   795  	else
   796 -	    CGContextEOClip (surface->cgContext);
   797 -
   798 -	_cairo_quartz_draw_image (surface, op, action);
   799 -    } else if (action != DO_NOTHING) {
   800 +	    CGContextEOClip (state.context);
   801 +
   802 +	_cairo_quartz_draw_image (&state, op);
   803 +    } else if (state.action != DO_NOTHING) {
   804  	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   805      }
   807    BAIL:
   808 -    _cairo_quartz_teardown_source (surface, source);
   809 -
   810 -    CGContextRestoreGState (surface->cgContext);
   811 +    _cairo_quartz_teardown_state (&state);
   813      if (path_for_unbounded) {
   814  	unbounded_op_data_t ub;
   815  	ub.op = UNBOUNDED_STROKE_FILL;
   816  	ub.u.stroke_fill.cgPath = path_for_unbounded;
   817  	ub.u.stroke_fill.fill_rule = fill_rule;
   819  	_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
   820 @@ -2274,44 +2323,49 @@ _cairo_quartz_surface_stroke (void *abst
   821  			      cairo_matrix_t *ctm,
   822  			      cairo_matrix_t *ctm_inverse,
   823  			      double tolerance,
   824  			      cairo_antialias_t antialias,
   825  			      cairo_clip_t *clip)
   826  {
   827      cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
   828      cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
   829 -    cairo_quartz_action_t action;
   830 +    cairo_quartz_drawing_state_t state;
   831      quartz_stroke_t stroke;
   832      CGAffineTransform origCTM, strokeTransform;
   833      CGPathRef path_for_unbounded = NULL;
   835      ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
   837      if (IS_EMPTY(surface))
   838  	return CAIRO_STATUS_SUCCESS;
   840      rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
   841      if (unlikely (rv))
   842  	return rv;
   844 -    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
   845 -    if (unlikely (rv))
   846 -	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
   847 +    if (_cairo_quartz_source_needs_extents (source))
   848 +    {
   849 +        cairo_rectangle_int_t path_extents;
   850 +        _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
   851 +        state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
   852 +    } else {
   853 +        state = _cairo_quartz_setup_state (surface, source, op, NULL);
   854 +    }
   856      // Turning antialiasing off used to cause misrendering with
   857      // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
   858      // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
   859 -    CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
   860 -    CGContextSetLineWidth (surface->cgContext, style->line_width);
   861 -    CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
   862 -    CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
   863 -    CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
   864 -
   865 -    origCTM = CGContextGetCTM (surface->cgContext);
   866 +    CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
   867 +    CGContextSetLineWidth (state.context, style->line_width);
   868 +    CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
   869 +    CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
   870 +    CGContextSetMiterLimit (state.context, style->miter_limit);
   871 +
   872 +    origCTM = CGContextGetCTM (state.context);
   874      if (style->dash && style->num_dashes) {
   875  #define STATIC_DASH 32
   876  	CGFloat sdash[STATIC_DASH];
   877  	CGFloat *fdash = sdash;
   878  	double offset = style->dash_offset;
   879  	unsigned int max_dashes = style->num_dashes;
   880  	unsigned int k;
   881 @@ -2330,90 +2384,75 @@ _cairo_quartz_surface_stroke (void *abst
   882  	    if (max_dashes > STATIC_DASH)
   883  		fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat));
   884  	    if (fdash == NULL)
   885  		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
   887  	    for (k = 0; k < max_dashes; k++)
   888  		fdash[k] = (CGFloat) style->dash[k % style->num_dashes];
   889  	}
   890 -	CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes);
   891 +	CGContextSetLineDash (state.context, offset, fdash, max_dashes);
   892  	if (fdash != sdash)
   893  	    free (fdash);
   894      } else
   895 -	CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
   896 -
   897 -    CGContextSaveGState (surface->cgContext);
   898 -
   899 -
   900 -    if (_cairo_quartz_source_needs_extents (source))
   901 -    {
   902 -        cairo_rectangle_int_t path_extents;
   903 -        _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
   904 -        action = _cairo_quartz_setup_source (surface, source, &path_extents);
   905 -    } else {
   906 -        action = _cairo_quartz_setup_source (surface, source, NULL);
   907 -    }
   908 +	CGContextSetLineDash (state.context, 0, NULL, 0);
   910      _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
   911 -    CGContextConcatCTM (surface->cgContext, strokeTransform);
   912 -
   913 -    CGContextBeginPath (surface->cgContext);
   914 -
   915 -    stroke.cgContext = surface->cgContext;
   916 +    CGContextConcatCTM (state.context, strokeTransform);
   917 +
   918 +    CGContextBeginPath (state.context);
   919 +
   920 +    stroke.cgContext = state.context;
   921      stroke.ctm_inverse = ctm_inverse;
   922      rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
   923      if (rv)
   924  	goto BAIL;
   926      if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
   927 -	path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
   928 -
   929 -    if (action == DO_SOLID || action == DO_PATTERN) {
   930 -	CGContextStrokePath (surface->cgContext);
   931 -    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
   932 -	CGContextReplacePathWithStrokedPath (surface->cgContext);
   933 -	CGContextClip (surface->cgContext);
   934 -
   935 -	CGContextSetCTM (surface->cgContext, origCTM);
   936 -	_cairo_quartz_draw_image (surface, op, action);
   937 -    } else if (action == DO_SHADING) {
   938 -	CGContextReplacePathWithStrokedPath (surface->cgContext);
   939 -	CGContextClip (surface->cgContext);
   940 -
   941 -	CGContextSetCTM (surface->cgContext, origCTM);
   942 -
   943 -	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
   944 -	CGContextDrawShading (surface->cgContext, surface->sourceShading);
   945 -    } else if (action != DO_NOTHING) {
   946 +	path_for_unbounded = CGContextCopyPathPtr (state.context);
   947 +
   948 +    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
   949 +	CGContextStrokePath (state.context);
   950 +    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
   951 +	CGContextReplacePathWithStrokedPath (state.context);
   952 +	CGContextClip (state.context);
   953 +
   954 +	CGContextSetCTM (state.context, origCTM);
   955 +	_cairo_quartz_draw_image (&state, op);
   956 +    } else if (state.action == DO_SHADING) {
   957 +	CGContextReplacePathWithStrokedPath (state.context);
   958 +	CGContextClip (state.context);
   959 +
   960 +	CGContextSetCTM (state.context, origCTM);
   961 +
   962 +	CGContextConcatCTM (state.context, state.transform);
   963 +	CGContextDrawShading (state.context, state.shading);
   964 +    } else if (state.action != DO_NOTHING) {
   965  	rv = CAIRO_INT_STATUS_UNSUPPORTED;
   966 +	goto BAIL;
   967      }
   969 +    if (path_for_unbounded) {
   970 +	CGContextSetCTM (state.context, origCTM);
   971 +	CGContextConcatCTM (state.context, strokeTransform);
   972 +
   973 +	CGContextBeginPath (state.context);
   974 +	CGContextAddPath (state.context, path_for_unbounded);
   975 +	CGPathRelease (path_for_unbounded);
   976 +
   977 +	CGContextReplacePathWithStrokedPath (state.context);
   978 +
   979 +	CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context));
   980 +
   981 +	CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.);
   982 +	CGContextEOFillPath (state.context);
   983 +    }
   984 +
   985    BAIL:
   986 -    _cairo_quartz_teardown_source (surface, source);
   987 -
   988 -    CGContextRestoreGState (surface->cgContext);
   989 -
   990 -    if (path_for_unbounded) {
   991 -	CGContextSaveGState (surface->cgContext);
   992 -	CGContextConcatCTM (surface->cgContext, strokeTransform);
   993 -
   994 -	CGContextBeginPath (surface->cgContext);
   995 -	CGContextAddPath (surface->cgContext, path_for_unbounded);
   996 -	CGPathRelease (path_for_unbounded);
   997 -
   998 -	CGContextReplacePathWithStrokedPath (surface->cgContext);
   999 -
  1000 -	CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
  1002 -	CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
  1003 -	CGContextEOFillPath (surface->cgContext);
  1005 -	CGContextRestoreGState (surface->cgContext);
  1006 -    }
  1007 +    _cairo_quartz_teardown_state (&state);
  1009      ND((stderr, "-- stroke\n"));
  1010      return rv;
  1013  #if CAIRO_HAS_QUARTZ_FONT
  1014  static cairo_int_status_t
  1015  _cairo_quartz_surface_show_glyphs (void *abstract_surface,
  1016 @@ -2429,17 +2468,17 @@ _cairo_quartz_surface_show_glyphs (void 
  1017  #define STATIC_BUF_SIZE 64
  1018      CGGlyph glyphs_static[STATIC_BUF_SIZE];
  1019      CGSize cg_advances_static[STATIC_BUF_SIZE];
  1020      CGGlyph *cg_glyphs = &glyphs_static[0];
  1021      CGSize *cg_advances = &cg_advances_static[0];
  1023      cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
  1024      cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
  1025 -    cairo_quartz_action_t action;
  1026 +    cairo_quartz_drawing_state_t state;
  1027      float xprev, yprev;
  1028      int i;
  1029      CGFontRef cgfref = NULL;
  1031      cairo_bool_t isClipping = FALSE;
  1032      cairo_bool_t didForceFontSmoothing = FALSE;
  1034      if (IS_EMPTY(surface))
  1035 @@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void 
  1037      if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
  1038  	return CAIRO_INT_STATUS_UNSUPPORTED;
  1040      rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
  1041      if (unlikely (rv))
  1042  	return rv;
  1044 -    rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
  1045 -    if (unlikely (rv))
  1046 -	return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
  1048 -    CGContextSaveGState (surface->cgContext);
  1050      if (_cairo_quartz_source_needs_extents (source))
  1052          cairo_rectangle_int_t glyph_extents;
  1053          _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
  1054                                                   &glyph_extents, NULL);
  1055 -        action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
  1056 +        state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
  1057      } else {
  1058 -        action = _cairo_quartz_setup_source (surface, source, NULL);
  1059 +        state = _cairo_quartz_setup_state (surface, source, op, NULL);
  1062 -    if (action == DO_SOLID || action == DO_PATTERN) {
  1063 -	CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
  1064 -    } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
  1065 -	CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
  1066 +    if (state.action == DO_SOLID || state.action == DO_PATTERN) {
  1067 +	CGContextSetTextDrawingMode (state.context, kCGTextFill);
  1068 +    } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) {
  1069 +	CGContextSetTextDrawingMode (state.context, kCGTextClip);
  1070  	isClipping = TRUE;
  1071      } else {
  1072 -	if (action != DO_NOTHING)
  1073 +	if (state.action != DO_NOTHING)
  1074  	    rv = CAIRO_INT_STATUS_UNSUPPORTED;
  1075  	goto BAIL;
  1078      /* this doesn't addref */
  1079      cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
  1080 -    CGContextSetFont (surface->cgContext, cgfref);
  1081 -    CGContextSetFontSize (surface->cgContext, 1.0);
  1082 +    CGContextSetFont (state.context, cgfref);
  1083 +    CGContextSetFontSize (state.context, 1.0);
  1085      switch (scaled_font->options.antialias) {
  1086  	case CAIRO_ANTIALIAS_SUBPIXEL:
  1087 -	    CGContextSetShouldAntialias (surface->cgContext, TRUE);
  1088 -	    CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
  1089 +	    CGContextSetShouldAntialias (state.context, TRUE);
  1090 +	    CGContextSetShouldSmoothFonts (state.context, TRUE);
  1091  	    if (CGContextSetAllowsFontSmoothingPtr &&
  1092 -		!CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
  1093 +		!CGContextGetAllowsFontSmoothingPtr (state.context))
  1095  		didForceFontSmoothing = TRUE;
  1096 -		CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
  1097 +		CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
  1099  	    break;
  1100  	case CAIRO_ANTIALIAS_NONE:
  1101 -	    CGContextSetShouldAntialias (surface->cgContext, FALSE);
  1102 +	    CGContextSetShouldAntialias (state.context, FALSE);
  1103  	    break;
  1104  	case CAIRO_ANTIALIAS_GRAY:
  1105 -	    CGContextSetShouldAntialias (surface->cgContext, TRUE);
  1106 -	    CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
  1107 +	    CGContextSetShouldAntialias (state.context, TRUE);
  1108 +	    CGContextSetShouldSmoothFonts (state.context, FALSE);
  1109  	    break;
  1110  	case CAIRO_ANTIALIAS_DEFAULT:
  1111  	    /* Don't do anything */
  1112  	    break;
  1115      if (num_glyphs > STATIC_BUF_SIZE) {
  1116  	cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
  1117 @@ -2532,17 +2565,17 @@ _cairo_quartz_surface_show_glyphs (void 
  1118      textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
  1119      textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
  1120  								   -scaled_font->ctm.yx,
  1121  								   -scaled_font->ctm.xy,
  1122  								   scaled_font->ctm.yy,
  1123  								   0., 0.),
  1124  					     textTransform);
  1126 -    CGContextSetTextMatrix (surface->cgContext, textTransform);
  1127 +    CGContextSetTextMatrix (state.context, textTransform);
  1129      /* Convert our glyph positions to glyph advances.  We need n-1 advances,
  1130       * since the advance at index 0 is applied after glyph 0. */
  1131      xprev = glyphs[0].x;
  1132      yprev = glyphs[0].y;
  1134      cg_glyphs[0] = glyphs[0].index;
  1136 @@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void 
  1138  #if 0
  1139      for (i = 0; i < num_glyphs; i++) {
  1140  	ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
  1142  #endif
  1144      /* Translate to the first glyph's position before drawing */
  1145 -    ctm = CGContextGetCTM (surface->cgContext);
  1146 -    CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
  1148 -    CGContextShowGlyphsWithAdvances (surface->cgContext,
  1149 +    ctm = CGContextGetCTM (state.context);
  1150 +    CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
  1152 +    CGContextShowGlyphsWithAdvances (state.context,
  1153  				     cg_glyphs,
  1154  				     cg_advances,
  1155  				     num_glyphs);
  1157 -    CGContextSetCTM (surface->cgContext, ctm);
  1159 -    if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
  1160 -	_cairo_quartz_draw_image (surface, op, action);
  1161 -    } else if (action == DO_SHADING) {
  1162 -	CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
  1163 -	CGContextDrawShading (surface->cgContext, surface->sourceShading);
  1164 +    CGContextSetCTM (state.context, ctm);
  1166 +    if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) {
  1167 +	_cairo_quartz_draw_image (&state, op);
  1168 +    } else if (state.action == DO_SHADING) {
  1169 +	CGContextConcatCTM (state.context, state.transform);
  1170 +	CGContextDrawShading (state.context, state.shading);
  1173  BAIL:
  1174 -    _cairo_quartz_teardown_source (surface, source);
  1176      if (didForceFontSmoothing)
  1177 -	CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
  1179 -    CGContextRestoreGState (surface->cgContext);
  1180 +        CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
  1182 +    _cairo_quartz_teardown_state (&state);
  1184      if (rv == CAIRO_STATUS_SUCCESS &&
  1185  	cgfref &&
  1186  	!_cairo_operator_bounded_by_mask (op))
  1188  	unbounded_op_data_t ub;
  1189  	ub.op = UNBOUNDED_SHOW_GLYPHS;

mercurial