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.

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

mercurial