1diff --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; 7 8 void *imageData; 9 cairo_surface_t *imageSurfaceEquiv; 10 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; 28 29 typedef struct cairo_quartz_image_surface { 30 cairo_surface_t base; 31 32 cairo_rectangle_int_t extents; 33 34 CGImageRef image; 35diff --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 } 41 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; 53 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; 84 85 cairo_surface_t *fallback; 86 CGImageRef img; 87 88 cairo_status_t status; 89 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+ } 97 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; 102 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 112 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 } 140 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; 168 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 } 178 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 } 190 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); 195 196 rgb = CGColorSpaceCreateDeviceRGB(); 197 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)); 202 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 } 211 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); 220 221 CGColorSpaceRelease(rgb); 222 CGFunctionRelease(gradFunc); 223 224- return DO_SHADING; 225+ state->action = DO_SHADING; 226 } 227 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); 250 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 } 260 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 } 273 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); 278 279 rgb = CGColorSpaceCreateDeviceRGB(); 280 281 start = CGPointMake (c1x, c1y); 282 end = CGPointMake (c2x, c2y); 283 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 } 294 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); 309 310 CGColorSpaceRelease(rgb); 311 CGFunctionRelease(gradFunc); 312 313- return DO_SHADING; 314+ state->action = DO_SHADING; 315 } 316 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+ } 362 363 if (source->type == CAIRO_PATTERN_TYPE_SOLID) { 364 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; 365 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); 378 379- return DO_SOLID; 380+ state.action = DO_SOLID; 381+ return state; 382 } 383 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 } 390 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 } 397 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; 411 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+ } 425 426 CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); 427 428- surface->sourceImage = img; 429+ state.image = img; 430 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); 434 435 is_bounded = _cairo_surface_get_extents (pat_surf, &extents); 436 assert (is_bounded); 437 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 } 445 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 */ 452 453- xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext), 454- surface->sourceTransform); 455+ xform = CGAffineTransformConcat (CGContextGetCTM (context), 456+ state.transform); 457 458 srcRect = CGRectMake (0, 0, extents.width, extents.height); 459 srcRect = CGRectApplyAffineTransform (srcRect, xform); 460 461 fw = _cairo_fixed_from_double (srcRect.size.width); 462 fh = _cairo_fixed_from_double (srcRect.size.height); 463 464 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON && 465@@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz 466 467 srcRect.size.width = round(srcRect.size.width); 468 srcRect.size.height = round(srcRect.size.height); 469 470 xform = CGAffineTransformInvert (xform); 471 472 srcRect = CGRectApplyAffineTransform (srcRect, xform); 473 474- surface->sourceImageRect = srcRect; 475- 476- return DO_TILED_IMAGE; 477+ state.imageRect = srcRect; 478+ state.action = DO_TILED_IMAGE; 479+ return state; 480 } 481 482 /* Fall through to generic SURFACE case */ 483 } 484 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; 490 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); 522 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 } 538 539- return DO_UNSUPPORTED; 540+ state.action = DO_UNSUPPORTED; 541+ return state; 542 } 543 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 } 564 565- if (surface->sourceShading) { 566- CGShadingRelease(surface->sourceShading); 567- surface->sourceShading = NULL; 568+ if (state->imageSurface) { 569+ cairo_surface_destroy(state->imageSurface); 570 } 571 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 } 588 589 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 } 627 628 629 /* 630 * get source/dest image implementation 631 */ 632 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; 645 646 ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type)); 647 648 if (IS_EMPTY(surface)) 649 return CAIRO_STATUS_SUCCESS; 650 651 rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); 652 if (unlikely (rv)) 653 return rv; 654 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 } 691 692- _cairo_quartz_teardown_source (surface, source); 693+ _cairo_quartz_teardown_state (&state); 694 695 ND((stderr, "-- paint\n")); 696 return rv; 697 } 698 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; 715 716 ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type)); 717 718 if (IS_EMPTY(surface)) 719 return CAIRO_STATUS_SUCCESS; 720 721 rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); 722 if (unlikely (rv)) 723 return rv; 724 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 } 746 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; 759 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) { 775 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 } 806 807 BAIL: 808- _cairo_quartz_teardown_source (surface, source); 809- 810- CGContextRestoreGState (surface->cgContext); 811+ _cairo_quartz_teardown_state (&state); 812 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; 818 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; 834 835 ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); 836 837 if (IS_EMPTY(surface)) 838 return CAIRO_STATUS_SUCCESS; 839 840 rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); 841 if (unlikely (rv)) 842 return rv; 843 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+ } 855 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); 873 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); 886 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); 909 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; 925 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 } 968 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)); 1001- 1002- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); 1003- CGContextEOFillPath (surface->cgContext); 1004- 1005- CGContextRestoreGState (surface->cgContext); 1006- } 1007+ _cairo_quartz_teardown_state (&state); 1008 1009 ND((stderr, "-- stroke\n")); 1010 return rv; 1011 } 1012 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]; 1022 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; 1030 1031 cairo_bool_t isClipping = FALSE; 1032 cairo_bool_t didForceFontSmoothing = FALSE; 1033 1034 if (IS_EMPTY(surface)) 1035@@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void 1036 1037 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) 1038 return CAIRO_INT_STATUS_UNSUPPORTED; 1039 1040 rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); 1041 if (unlikely (rv)) 1042 return rv; 1043 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; 1047- 1048- CGContextSaveGState (surface->cgContext); 1049- 1050 if (_cairo_quartz_source_needs_extents (source)) 1051 { 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); 1060 } 1061 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; 1076 } 1077 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); 1084 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)) 1094 { 1095 didForceFontSmoothing = TRUE; 1096- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE); 1097+ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE); 1098 } 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; 1113 } 1114 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); 1125 1126- CGContextSetTextMatrix (surface->cgContext, textTransform); 1127+ CGContextSetTextMatrix (state.context, textTransform); 1128 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; 1133 1134 cg_glyphs[0] = glyphs[0].index; 1135 1136@@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void 1137 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)); 1141 } 1142 #endif 1143 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); 1147- 1148- CGContextShowGlyphsWithAdvances (surface->cgContext, 1149+ ctm = CGContextGetCTM (state.context); 1150+ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y); 1151+ 1152+ CGContextShowGlyphsWithAdvances (state.context, 1153 cg_glyphs, 1154 cg_advances, 1155 num_glyphs); 1156 1157- CGContextSetCTM (surface->cgContext, ctm); 1158- 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); 1165+ 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); 1171 } 1172 1173 BAIL: 1174- _cairo_quartz_teardown_source (surface, source); 1175- 1176 if (didForceFontSmoothing) 1177- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE); 1178- 1179- CGContextRestoreGState (surface->cgContext); 1180+ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE); 1181+ 1182+ _cairo_quartz_teardown_state (&state); 1183 1184 if (rv == CAIRO_STATUS_SUCCESS && 1185 cgfref && 1186 !_cairo_operator_bounded_by_mask (op)) 1187 { 1188 unbounded_op_data_t ub; 1189 ub.op = UNBOUNDED_SHOW_GLYPHS; 1190 1191