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