gfx/cairo/quartz-minimize-gradient-repeat.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 # HG changeset patch
michael@0 2 # User Robert O'Callahan <robert@ocallahan.org>
michael@0 3 # Date 1249558989 -43200
michael@0 4 # Node ID 0bac4c903d2bb1d5c0d5426209001fc2a77cc105
michael@0 5 # Parent 963b9451ad305924738d05d997a640698cd3af91
michael@0 6 Bug 508730. Don't repeat a Quartz gradient more times than necessary, to avoid Quartz quality problems when there are lots of repeated color stops. r=jmuizelaar
michael@0 7
michael@0 8 diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 9 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 10 +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
michael@0 11 @@ -710,82 +710,100 @@ CreateGradientFunction (const cairo_grad
michael@0 12 return CGFunctionCreate (pat,
michael@0 13 1,
michael@0 14 input_value_range,
michael@0 15 4,
michael@0 16 gradient_output_value_ranges,
michael@0 17 &gradient_callbacks);
michael@0 18 }
michael@0 19
michael@0 20 +static void
michael@0 21 +UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start,
michael@0 22 + double dx, double dy,
michael@0 23 + double x, double y)
michael@0 24 +{
michael@0 25 + /* Compute a parameter t such that a line perpendicular to the (dx,dy)
michael@0 26 + vector, passing through (start->x + dx*t, start->y + dy*t), also
michael@0 27 + passes through (x,y).
michael@0 28 +
michael@0 29 + Let px = x - start->x, py = y - start->y.
michael@0 30 + t is given by
michael@0 31 + (px - dx*t)*dx + (py - dy*t)*dy = 0
michael@0 32 +
michael@0 33 + Solving for t we get
michael@0 34 + numerator = dx*px + dy*py
michael@0 35 + denominator = dx^2 + dy^2
michael@0 36 + t = numerator/denominator
michael@0 37 +
michael@0 38 + In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
michael@0 39 + is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
michael@0 40 + */
michael@0 41 + double px = x - start->x;
michael@0 42 + double py = y - start->y;
michael@0 43 + double numerator = dx*px + dy*py;
michael@0 44 + double denominator = dx*dx + dy*dy;
michael@0 45 + double t = numerator/denominator;
michael@0 46 +
michael@0 47 + if (*min_t > t) {
michael@0 48 + *min_t = t;
michael@0 49 + }
michael@0 50 + if (*max_t < t) {
michael@0 51 + *max_t = t;
michael@0 52 + }
michael@0 53 +}
michael@0 54 +
michael@0 55 static CGFunctionRef
michael@0 56 CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
michael@0 57 const cairo_gradient_pattern_t *gpat,
michael@0 58 CGPoint *start, CGPoint *end,
michael@0 59 - CGAffineTransform matrix)
michael@0 60 + cairo_rectangle_int_t *extents)
michael@0 61 {
michael@0 62 cairo_pattern_t *pat;
michael@0 63 float input_value_range[2];
michael@0 64 + double t_min = 0.;
michael@0 65 + double t_max = 0.;
michael@0 66 + double dx = end->x - start->x;
michael@0 67 + double dy = end->y - start->y;
michael@0 68 + double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
michael@0 69
michael@0 70 - CGPoint mstart, mend;
michael@0 71 + if (!extents) {
michael@0 72 + extents = &surface->extents;
michael@0 73 + }
michael@0 74 + bounds_x1 = extents->x;
michael@0 75 + bounds_y1 = extents->y;
michael@0 76 + bounds_x2 = extents->x + extents->width;
michael@0 77 + bounds_y2 = extents->y + extents->height;
michael@0 78 + _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
michael@0 79 + &bounds_x1, &bounds_y1,
michael@0 80 + &bounds_x2, &bounds_y2,
michael@0 81 + NULL);
michael@0 82
michael@0 83 - double dx, dy;
michael@0 84 - int x_rep_start = 0, x_rep_end = 0;
michael@0 85 - int y_rep_start = 0, y_rep_end = 0;
michael@0 86 + UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
michael@0 87 + bounds_x1, bounds_y1);
michael@0 88 + UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
michael@0 89 + bounds_x2, bounds_y1);
michael@0 90 + UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
michael@0 91 + bounds_x2, bounds_y2);
michael@0 92 + UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
michael@0 93 + bounds_x1, bounds_y2);
michael@0 94
michael@0 95 - int rep_start, rep_end;
michael@0 96 -
michael@0 97 - // figure out how many times we'd need to repeat the gradient pattern
michael@0 98 - // to cover the whole (transformed) surface area
michael@0 99 - mstart = CGPointApplyAffineTransform (*start, matrix);
michael@0 100 - mend = CGPointApplyAffineTransform (*end, matrix);
michael@0 101 -
michael@0 102 - dx = fabs (mend.x - mstart.x);
michael@0 103 - dy = fabs (mend.y - mstart.y);
michael@0 104 -
michael@0 105 - if (dx > 1e-6) {
michael@0 106 - x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx);
michael@0 107 - x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx);
michael@0 108 -
michael@0 109 - if (mend.x < mstart.x) {
michael@0 110 - int swap = x_rep_end;
michael@0 111 - x_rep_end = x_rep_start;
michael@0 112 - x_rep_start = swap;
michael@0 113 - }
michael@0 114 - }
michael@0 115 -
michael@0 116 - if (dy > 1e-6) {
michael@0 117 - y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy);
michael@0 118 - y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy);
michael@0 119 -
michael@0 120 - if (mend.y < mstart.y) {
michael@0 121 - int swap = y_rep_end;
michael@0 122 - y_rep_end = y_rep_start;
michael@0 123 - y_rep_start = swap;
michael@0 124 - }
michael@0 125 - }
michael@0 126 -
michael@0 127 - rep_start = MAX(x_rep_start, y_rep_start);
michael@0 128 - rep_end = MAX(x_rep_end, y_rep_end);
michael@0 129 -
michael@0 130 - // extend the line between start and end by rep_start times from the start
michael@0 131 - // and rep_end times from the end
michael@0 132 -
michael@0 133 - dx = end->x - start->x;
michael@0 134 - dy = end->y - start->y;
michael@0 135 -
michael@0 136 - start->x = start->x - dx * rep_start;
michael@0 137 - start->y = start->y - dy * rep_start;
michael@0 138 -
michael@0 139 - end->x = end->x + dx * rep_end;
michael@0 140 - end->y = end->y + dy * rep_end;
michael@0 141 + /* Move t_min and t_max to the nearest usable integer to try to avoid
michael@0 142 + subtle variations due to numerical instability, especially accidentally
michael@0 143 + cutting off a pixel. Extending the gradient repetitions is always safe. */
michael@0 144 + t_min = floor (t_min);
michael@0 145 + t_max = ceil (t_max);
michael@0 146 + end->x = start->x + dx*t_max;
michael@0 147 + end->y = start->y + dy*t_max;
michael@0 148 + start->x = start->x + dx*t_min;
michael@0 149 + start->y = start->y + dy*t_min;
michael@0 150
michael@0 151 // set the input range for the function -- the function knows how to
michael@0 152 // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
michael@0 153 - input_value_range[0] = 0.0 - 1.0 * rep_start;
michael@0 154 - input_value_range[1] = 1.0 + 1.0 * rep_end;
michael@0 155 + input_value_range[0] = t_min;
michael@0 156 + input_value_range[1] = t_max;
michael@0 157
michael@0 158 if (_cairo_pattern_create_copy (&pat, &gpat->base))
michael@0 159 /* quartz doesn't deal very well with malloc failing, so there's
michael@0 160 * not much point in us trying either */
michael@0 161 return NULL;
michael@0 162
michael@0 163 return CGFunctionCreate (pat,
michael@0 164 1,
michael@0 165 @@ -840,35 +858,43 @@ UpdateRadialParameterToIncludePoint(doub
michael@0 166 }
michael@0 167 }
michael@0 168
michael@0 169 /* This must only be called when one of the circles properly contains the other */
michael@0 170 static CGFunctionRef
michael@0 171 CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
michael@0 172 const cairo_gradient_pattern_t *gpat,
michael@0 173 CGPoint *start, double *start_radius,
michael@0 174 - CGPoint *end, double *end_radius)
michael@0 175 + CGPoint *end, double *end_radius,
michael@0 176 + cairo_rectangle_int_t *extents)
michael@0 177 {
michael@0 178 - CGRect clip = CGContextGetClipBoundingBox (surface->cgContext);
michael@0 179 - CGAffineTransform transform;
michael@0 180 cairo_pattern_t *pat;
michael@0 181 float input_value_range[2];
michael@0 182 CGPoint *inner;
michael@0 183 double *inner_radius;
michael@0 184 CGPoint *outer;
michael@0 185 double *outer_radius;
michael@0 186 /* minimum and maximum t-parameter values that will make our gradient
michael@0 187 cover the clipBox */
michael@0 188 double t_min, t_max, t_temp;
michael@0 189 /* outer minus inner */
michael@0 190 double dr, dx, dy;
michael@0 191 + double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
michael@0 192
michael@0 193 - _cairo_quartz_cairo_matrix_to_quartz (&gpat->base.matrix, &transform);
michael@0 194 - /* clip is in cairo device coordinates; get it into cairo user space */
michael@0 195 - clip = CGRectApplyAffineTransform (clip, transform);
michael@0 196 + if (!extents) {
michael@0 197 + extents = &surface->extents;
michael@0 198 + }
michael@0 199 + bounds_x1 = extents->x;
michael@0 200 + bounds_y1 = extents->y;
michael@0 201 + bounds_x2 = extents->x + extents->width;
michael@0 202 + bounds_y2 = extents->y + extents->height;
michael@0 203 + _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
michael@0 204 + &bounds_x1, &bounds_y1,
michael@0 205 + &bounds_x2, &bounds_y2,
michael@0 206 + NULL);
michael@0 207
michael@0 208 if (*start_radius < *end_radius) {
michael@0 209 /* end circle contains start circle */
michael@0 210 inner = start;
michael@0 211 outer = end;
michael@0 212 inner_radius = start_radius;
michael@0 213 outer_radius = end_radius;
michael@0 214 } else {
michael@0 215 @@ -878,36 +904,37 @@ CreateRepeatingRadialGradientFunction (c
michael@0 216 inner_radius = end_radius;
michael@0 217 outer_radius = start_radius;
michael@0 218 }
michael@0 219
michael@0 220 dr = *outer_radius - *inner_radius;
michael@0 221 dx = outer->x - inner->x;
michael@0 222 dy = outer->y - inner->y;
michael@0 223
michael@0 224 + /* We can't round or fudge t_min here, it has to be as accurate as possible. */
michael@0 225 t_min = -(*inner_radius/dr);
michael@0 226 inner->x += t_min*dx;
michael@0 227 inner->y += t_min*dy;
michael@0 228 *inner_radius = 0.;
michael@0 229
michael@0 230 t_temp = 0.;
michael@0 231 UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 232 - clip.origin.x, clip.origin.y);
michael@0 233 + bounds_x1, bounds_y1);
michael@0 234 UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 235 - clip.origin.x + clip.size.width, clip.origin.y);
michael@0 236 + bounds_x2, bounds_y1);
michael@0 237 UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 238 - clip.origin.x + clip.size.width, clip.origin.y + clip.size.height);
michael@0 239 + bounds_x2, bounds_y2);
michael@0 240 UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
michael@0 241 - clip.origin.x, clip.origin.y + clip.size.height);
michael@0 242 + bounds_x1, bounds_y2);
michael@0 243 /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
michael@0 244 But for the parameter values we use with Quartz, t_min means radius 0.
michael@0 245 - Also, add a small fudge factor to avoid rounding issues. Since the
michael@0 246 - circles are alway expanding and containing the earlier circles, this is
michael@0 247 - OK. */
michael@0 248 - t_temp += 1e-6;
michael@0 249 + Since the circles are alway expanding and contain the earlier circles,
michael@0 250 + it's safe to extend t_max/t_temp as much as we want, so round t_temp up
michael@0 251 + to the nearest integer. This may help us give stable results. */
michael@0 252 + t_temp = ceil (t_temp);
michael@0 253 t_max = t_min + t_temp;
michael@0 254 outer->x = inner->x + t_temp*dx;
michael@0 255 outer->y = inner->y + t_temp*dy;
michael@0 256 *outer_radius = t_temp*dr;
michael@0 257
michael@0 258 /* set the input range for the function -- the function knows how to
michael@0 259 map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
michael@0 260 if (*start_radius < *end_radius) {
michael@0 261 @@ -1218,33 +1245,57 @@ _cairo_quartz_setup_fallback_source (cai
michael@0 262 surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
michael@0 263 surface->sourceImage = img;
michael@0 264 surface->sourceImageSurface = fallback;
michael@0 265 surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
michael@0 266
michael@0 267 return DO_IMAGE;
michael@0 268 }
michael@0 269
michael@0 270 +/*
michael@0 271 +Quartz does not support repeating radients. We handle repeating gradients
michael@0 272 +by manually extending the gradient and repeating color stops. We need to
michael@0 273 +minimize the number of repetitions since Quartz seems to sample our color
michael@0 274 +function across the entire range, even if part of that range is not needed
michael@0 275 +for the visible area of the gradient, and it samples with some fixed resolution,
michael@0 276 +so if the gradient range is too large it samples with very low resolution and
michael@0 277 +the gradient is very coarse. CreateRepeatingLinearGradientFunction and
michael@0 278 +CreateRepeatingRadialGradientFunction compute the number of repetitions needed
michael@0 279 +based on the extents of the object (the clip region cannot be used here since
michael@0 280 +we don't want the rasterization of the entire gradient to depend on the
michael@0 281 +clip region).
michael@0 282 +*/
michael@0 283 static cairo_quartz_action_t
michael@0 284 _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
michael@0 285 - const cairo_linear_pattern_t *lpat)
michael@0 286 + const cairo_linear_pattern_t *lpat,
michael@0 287 + cairo_rectangle_int_t *extents)
michael@0 288 {
michael@0 289 const cairo_pattern_t *abspat = &lpat->base.base;
michael@0 290 cairo_matrix_t mat;
michael@0 291 CGPoint start, end;
michael@0 292 CGFunctionRef gradFunc;
michael@0 293 CGColorSpaceRef rgb;
michael@0 294 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
michael@0 295
michael@0 296 if (lpat->base.n_stops == 0) {
michael@0 297 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
michael@0 298 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
michael@0 299 return DO_SOLID;
michael@0 300 }
michael@0 301
michael@0 302 + if (lpat->p1.x == lpat->p2.x &&
michael@0 303 + lpat->p1.y == lpat->p2.y) {
michael@0 304 + /* Quartz handles cases where the vector has no length very
michael@0 305 + * differently from pixman.
michael@0 306 + * Whatever the correct behaviour is, let's at least have only pixman's
michael@0 307 + * implementation to worry about.
michael@0 308 + */
michael@0 309 + return _cairo_quartz_setup_fallback_source (surface, abspat);
michael@0 310 + }
michael@0 311 +
michael@0 312 mat = abspat->matrix;
michael@0 313 cairo_matrix_invert (&mat);
michael@0 314 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
michael@0 315
michael@0 316 rgb = CGColorSpaceCreateDeviceRGB();
michael@0 317
michael@0 318 start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
michael@0 319 _cairo_fixed_to_double (lpat->p1.y));
michael@0 320 @@ -1254,33 +1305,34 @@ _cairo_quartz_setup_linear_source (cairo
michael@0 321 if (abspat->extend == CAIRO_EXTEND_NONE ||
michael@0 322 abspat->extend == CAIRO_EXTEND_PAD)
michael@0 323 {
michael@0 324 gradFunc = CreateGradientFunction (&lpat->base);
michael@0 325 } else {
michael@0 326 gradFunc = CreateRepeatingLinearGradientFunction (surface,
michael@0 327 &lpat->base,
michael@0 328 &start, &end,
michael@0 329 - surface->sourceTransform);
michael@0 330 + extents);
michael@0 331 }
michael@0 332
michael@0 333 surface->sourceShading = CGShadingCreateAxial (rgb,
michael@0 334 start, end,
michael@0 335 gradFunc,
michael@0 336 extend, extend);
michael@0 337
michael@0 338 CGColorSpaceRelease(rgb);
michael@0 339 CGFunctionRelease(gradFunc);
michael@0 340
michael@0 341 return DO_SHADING;
michael@0 342 }
michael@0 343
michael@0 344 static cairo_quartz_action_t
michael@0 345 _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
michael@0 346 - const cairo_radial_pattern_t *rpat)
michael@0 347 + const cairo_radial_pattern_t *rpat,
michael@0 348 + cairo_rectangle_int_t *extents)
michael@0 349 {
michael@0 350 const cairo_pattern_t *abspat = &rpat->base.base;
michael@0 351 cairo_matrix_t mat;
michael@0 352 CGPoint start, end;
michael@0 353 CGFunctionRef gradFunc;
michael@0 354 CGColorSpaceRef rgb;
michael@0 355 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
michael@0 356 double c1x = _cairo_fixed_to_double (rpat->c1.x);
michael@0 357 @@ -1322,17 +1374,18 @@ _cairo_quartz_setup_radial_source (cairo
michael@0 358 if (abspat->extend == CAIRO_EXTEND_NONE ||
michael@0 359 abspat->extend == CAIRO_EXTEND_PAD)
michael@0 360 {
michael@0 361 gradFunc = CreateGradientFunction (&rpat->base);
michael@0 362 } else {
michael@0 363 gradFunc = CreateRepeatingRadialGradientFunction (surface,
michael@0 364 &rpat->base,
michael@0 365 &start, &r1,
michael@0 366 - &end, &r2);
michael@0 367 + &end, &r2,
michael@0 368 + extents);
michael@0 369 }
michael@0 370
michael@0 371 surface->sourceShading = CGShadingCreateRadial (rgb,
michael@0 372 start,
michael@0 373 r1,
michael@0 374 end,
michael@0 375 r2,
michael@0 376 gradFunc,
michael@0 377 @@ -1341,17 +1394,18 @@ _cairo_quartz_setup_radial_source (cairo
michael@0 378 CGColorSpaceRelease(rgb);
michael@0 379 CGFunctionRelease(gradFunc);
michael@0 380
michael@0 381 return DO_SHADING;
michael@0 382 }
michael@0 383
michael@0 384 static cairo_quartz_action_t
michael@0 385 _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
michael@0 386 - const cairo_pattern_t *source)
michael@0 387 + const cairo_pattern_t *source,
michael@0 388 + cairo_rectangle_int_t *extents)
michael@0 389 {
michael@0 390 assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
michael@0 391
michael@0 392 surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
michael@0 393 CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
michael@0 394
michael@0 395 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
michael@0 396 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
michael@0 397 @@ -1367,24 +1421,22 @@ _cairo_quartz_setup_source (cairo_quartz
michael@0 398 solid->color.blue,
michael@0 399 solid->color.alpha);
michael@0 400
michael@0 401 return DO_SOLID;
michael@0 402 }
michael@0 403
michael@0 404 if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
michael@0 405 const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
michael@0 406 - return _cairo_quartz_setup_linear_source (surface, lpat);
michael@0 407 -
michael@0 408 + return _cairo_quartz_setup_linear_source (surface, lpat, extents);
michael@0 409 }
michael@0 410
michael@0 411 if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
michael@0 412 const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
michael@0 413 - return _cairo_quartz_setup_radial_source (surface, rpat);
michael@0 414 -
michael@0 415 + return _cairo_quartz_setup_radial_source (surface, rpat, extents);
michael@0 416 }
michael@0 417
michael@0 418 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
michael@0 419 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
michael@0 420 {
michael@0 421 const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
michael@0 422 cairo_surface_t *pat_surf = spat->surface;
michael@0 423 CGImageRef img;
michael@0 424 @@ -1852,17 +1904,17 @@ _cairo_quartz_surface_paint (void *abstr
michael@0 425 if (IS_EMPTY(surface))
michael@0 426 return CAIRO_STATUS_SUCCESS;
michael@0 427
michael@0 428 if (op == CAIRO_OPERATOR_DEST)
michael@0 429 return CAIRO_STATUS_SUCCESS;
michael@0 430
michael@0 431 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
michael@0 432
michael@0 433 - action = _cairo_quartz_setup_source (surface, source);
michael@0 434 + action = _cairo_quartz_setup_source (surface, source, NULL);
michael@0 435
michael@0 436 if (action == DO_SOLID || action == DO_PATTERN) {
michael@0 437 CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
michael@0 438 surface->extents.y,
michael@0 439 surface->extents.width,
michael@0 440 surface->extents.height));
michael@0 441 } else if (action == DO_SHADING) {
michael@0 442 CGContextSaveGState (surface->cgContext);
michael@0 443 @@ -1886,16 +1938,35 @@ _cairo_quartz_surface_paint (void *abstr
michael@0 444 }
michael@0 445
michael@0 446 _cairo_quartz_teardown_source (surface, source);
michael@0 447
michael@0 448 ND((stderr, "-- paint\n"));
michael@0 449 return rv;
michael@0 450 }
michael@0 451
michael@0 452 +static cairo_bool_t
michael@0 453 +_cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
michael@0 454 +{
michael@0 455 + /* For repeating gradients we need to manually extend the gradient and
michael@0 456 + repeat stops, since Quartz doesn't support repeating gradients natively.
michael@0 457 + We need to minimze the number of repeated stops, and since rasterization
michael@0 458 + depends on the number of repetitions we use (even if some of the
michael@0 459 + repetitions go beyond the extents of the object or outside the clip
michael@0 460 + region), it's important to use the same number of repetitions when
michael@0 461 + rendering an object no matter what the clip region is. So the
michael@0 462 + computation of the repetition count cannot depended on the clip region,
michael@0 463 + and should only depend on the object extents, so we need to compute
michael@0 464 + the object extents for repeating gradients. */
michael@0 465 + return (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
michael@0 466 + source->type == CAIRO_PATTERN_TYPE_RADIAL) &&
michael@0 467 + (source->extend == CAIRO_EXTEND_REPEAT ||
michael@0 468 + source->extend == CAIRO_EXTEND_REFLECT);
michael@0 469 +}
michael@0 470 +
michael@0 471 static cairo_int_status_t
michael@0 472 _cairo_quartz_surface_fill (void *abstract_surface,
michael@0 473 cairo_operator_t op,
michael@0 474 const cairo_pattern_t *source,
michael@0 475 cairo_path_fixed_t *path,
michael@0 476 cairo_fill_rule_t fill_rule,
michael@0 477 double tolerance,
michael@0 478 cairo_antialias_t antialias,
michael@0 479 @@ -1926,17 +1997,27 @@ _cairo_quartz_surface_fill (void *abstra
michael@0 480 return CAIRO_STATUS_SUCCESS;
michael@0 481 }
michael@0 482
michael@0 483 CGContextSaveGState (surface->cgContext);
michael@0 484
michael@0 485 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
michael@0 486 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
michael@0 487
michael@0 488 - action = _cairo_quartz_setup_source (surface, source);
michael@0 489 + if (_cairo_quartz_source_needs_extents (source))
michael@0 490 + {
michael@0 491 + /* We don't need precise extents since these are only used to
michael@0 492 + compute the number of gradient reptitions needed to cover the
michael@0 493 + object. */
michael@0 494 + cairo_rectangle_int_t path_extents;
michael@0 495 + _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
michael@0 496 + action = _cairo_quartz_setup_source (surface, source, &path_extents);
michael@0 497 + } else {
michael@0 498 + action = _cairo_quartz_setup_source (surface, source, NULL);
michael@0 499 + }
michael@0 500
michael@0 501 CGContextBeginPath (surface->cgContext);
michael@0 502
michael@0 503 stroke.cgContext = surface->cgContext;
michael@0 504 stroke.ctm_inverse = NULL;
michael@0 505 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
michael@0 506 if (rv)
michael@0 507 goto BAIL;
michael@0 508 @@ -2059,17 +2140,24 @@ _cairo_quartz_surface_stroke (void *abst
michael@0 509
michael@0 510 CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
michael@0 511 if (fdash != sdash)
michael@0 512 free (fdash);
michael@0 513 }
michael@0 514
michael@0 515 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
michael@0 516
michael@0 517 - action = _cairo_quartz_setup_source (surface, source);
michael@0 518 + if (_cairo_quartz_source_needs_extents (source))
michael@0 519 + {
michael@0 520 + cairo_rectangle_int_t path_extents;
michael@0 521 + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
michael@0 522 + action = _cairo_quartz_setup_source (surface, source, &path_extents);
michael@0 523 + } else {
michael@0 524 + action = _cairo_quartz_setup_source (surface, source, NULL);
michael@0 525 + }
michael@0 526
michael@0 527 CGContextBeginPath (surface->cgContext);
michael@0 528
michael@0 529 stroke.cgContext = surface->cgContext;
michael@0 530 stroke.ctm_inverse = ctm_inverse;
michael@0 531 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
michael@0 532 if (rv)
michael@0 533 goto BAIL;
michael@0 534 @@ -2180,17 +2268,26 @@ _cairo_quartz_surface_show_glyphs (void
michael@0 535 if (op == CAIRO_OPERATOR_DEST)
michael@0 536 return CAIRO_STATUS_SUCCESS;
michael@0 537
michael@0 538 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
michael@0 539 return CAIRO_INT_STATUS_UNSUPPORTED;
michael@0 540
michael@0 541 CGContextSaveGState (surface->cgContext);
michael@0 542
michael@0 543 - action = _cairo_quartz_setup_source (surface, source);
michael@0 544 + if (_cairo_quartz_source_needs_extents (source))
michael@0 545 + {
michael@0 546 + cairo_rectangle_int_t glyph_extents;
michael@0 547 + _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
michael@0 548 + &glyph_extents);
michael@0 549 + action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
michael@0 550 + } else {
michael@0 551 + action = _cairo_quartz_setup_source (surface, source, NULL);
michael@0 552 + }
michael@0 553 +
michael@0 554 if (action == DO_SOLID || action == DO_PATTERN) {
michael@0 555 CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
michael@0 556 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
michael@0 557 CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
michael@0 558 isClipping = TRUE;
michael@0 559 } else {
michael@0 560 if (action != DO_NOTHING)
michael@0 561 rv = CAIRO_INT_STATUS_UNSUPPORTED;

mercurial