1 --- a/src/cairo-quartz-font.c 2012-11-13 18:20:00.000000000 -0800
2 +++ b/src/cairo-quartz-font.c 2012-11-13 18:06:56.000000000 -0800
3 @@ -90,8 +90,9 @@ static int (*CGFontGetAscentPtr) (CGFont
4 static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
5 static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
7 -/* Not public anymore in 64-bits nor in 10.7 */
8 -static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
9 +/* CTFontCreateWithGraphicsFont is not public until 10.5. */
10 +typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
11 +static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
13 static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
14 static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
15 @@ -130,7 +131,7 @@ quartz_font_ensure_symbols(void)
16 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
17 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
19 - FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
20 + CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
22 if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
23 CGFontGetGlyphBBoxesPtr &&
24 @@ -155,6 +156,7 @@ struct _cairo_quartz_font_face {
25 cairo_font_face_t base;
32 @@ -239,6 +241,10 @@ _cairo_quartz_font_face_destroy (void *a
34 cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
36 + if (font_face->ctFont) {
37 + CFRelease (font_face->ctFont);
40 CGFontRelease (font_face->cgFont);
43 @@ -363,6 +369,12 @@ cairo_quartz_font_face_create_for_cgfont
45 font_face->cgFont = CGFontRetain (font);
47 + if (CTFontCreateWithGraphicsFontPtr) {
48 + font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
50 + font_face->ctFont = NULL;
53 _cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
55 return &font_face->base;
56 @@ -782,49 +794,10 @@ _cairo_quartz_scaled_font_get_cg_font_re
61 - * compat with old ATSUI backend
65 - * cairo_quartz_font_face_create_for_atsu_font_id
66 - * @font_id: an ATSUFontID for the font.
68 - * Creates a new font for the Quartz font backend based on an
69 - * #ATSUFontID. This font can then be used with
70 - * cairo_set_font_face() or cairo_scaled_font_create().
72 - * Return value: a newly created #cairo_font_face_t. Free with
73 - * cairo_font_face_destroy() when you are done using it.
78 -cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
80 +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
82 - quartz_font_ensure_symbols();
84 - if (FMGetATSFontRefFromFontPtr != NULL) {
85 - ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
86 - CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
87 - cairo_font_face_t *ff;
89 - ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
91 - CGFontRelease (cgFont);
95 - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
96 - return (cairo_font_face_t *)&_cairo_font_face_nil;
100 -/* This is the old name for the above function, exported for compat purposes */
101 -cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
102 + cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
105 -cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
107 - return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
108 + return ffont->ctFont;
110 --- a/src/cairo-quartz-image-surface.c 2010-06-18 04:47:13.000000000 -0700
111 +++ b/src/cairo-quartz-image-surface.c 2012-11-13 18:06:56.000000000 -0800
112 @@ -148,6 +148,8 @@ _cairo_quartz_image_surface_flush (void
113 surface->image = newImage;
114 CGImageRelease (oldImage);
116 + surface->base.is_clear = surface->imageSurface->base.is_clear;
118 return CAIRO_STATUS_SUCCESS;
121 @@ -270,6 +272,8 @@ cairo_quartz_image_surface_create (cairo
122 qisurf->image = image;
123 qisurf->imageSurface = image_surface;
125 + qisurf->base.is_clear = image_surface->base.is_clear;
127 return &qisurf->base;
130 --- a/src/cairo-quartz-private.h 2010-12-25 06:21:34.000000000 -0800
131 +++ b/src/cairo-quartz-private.h 2012-11-13 18:06:56.000000000 -0800
132 @@ -50,6 +50,9 @@ typedef CGFloat cairo_quartz_float_t;
133 typedef float cairo_quartz_float_t;
136 +/* define CTFontRef for pre-10.5 SDKs */
137 +typedef const struct __CTFont *CTFontRef;
139 typedef struct cairo_quartz_surface {
140 cairo_surface_t base;
142 @@ -60,21 +63,22 @@ typedef struct cairo_quartz_surface {
143 cairo_surface_t *imageSurfaceEquiv;
145 cairo_surface_clipper_t clipper;
146 - cairo_rectangle_int_t extents;
148 - /* These are stored while drawing operations are in place, set up
149 - * by quartz_setup_source() and quartz_finish_source()
151 + * If non-null, this is a CGImage representing the contents of the surface.
152 + * We clear this out before any painting into the surface, so that we
153 + * don't force a copy to be created.
155 - CGAffineTransform sourceTransform;
156 + CGImageRef bitmapContextImage;
158 - CGImageRef sourceImage;
159 - cairo_surface_t *sourceImageSurface;
160 - CGRect sourceImageRect;
162 + * If non-null, this is the CGLayer for the surface.
164 + CGLayerRef cgLayer;
166 - CGShadingRef sourceShading;
167 - CGPatternRef sourcePattern;
168 + cairo_rectangle_int_t extents;
170 - CGInterpolationQuality oldInterpolationQuality;
171 + cairo_bool_t ownsData;
172 } cairo_quartz_surface_t;
174 typedef struct cairo_quartz_image_surface {
175 @@ -103,6 +107,9 @@ _cairo_quartz_create_cgimage (cairo_form
177 _cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
180 +_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
184 # error Cairo was not compiled with support for the quartz backend
185 --- a/src/cairo-quartz-surface.c 2012-11-13 18:20:00.000000000 -0800
186 +++ b/src/cairo-quartz-surface.c 2012-11-13 18:06:56.000000000 -0800
189 #include "cairo-error-private.h"
190 #include "cairo-surface-clipper-private.h"
191 +#include "cairo-gstate-private.h"
192 +#include "cairo-private.h"
197 * This macro can be used to conditionally compile backend-specific code.
200 +/* Here are some of the differences between cairo and CoreGraphics
201 + - cairo has only a single source active at once vs. CoreGraphics having
202 + separate sources for stroke and fill
205 /* This method is private, but it exists. Its params are are exposed
206 * as args to the NS* method, but not as CG.
208 @@ -126,6 +133,12 @@ static void (*CGContextSetShouldAntialia
209 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
210 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
211 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
212 +static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
214 +/* CTFontDrawGlyphs is not available until 10.7 */
215 +static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
217 +static SInt32 _cairo_quartz_osx_version = 0x0;
219 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
221 @@ -160,6 +173,14 @@ static void quartz_ensure_symbols(void)
222 CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
223 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
224 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
225 + CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
227 + CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
229 + if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
231 + _cairo_quartz_osx_version = 0x1050;
234 _cairo_quartz_symbol_lookup_done = TRUE;
236 @@ -430,6 +446,7 @@ _cairo_quartz_cairo_operator_to_quartz_c
237 case CAIRO_OPERATOR_HSL_LUMINOSITY:
240 + return kPrivateCGCompositeClear;
244 @@ -598,10 +615,13 @@ _cairo_quartz_cairo_matrix_to_quartz (co
248 - CGSize *cg_advances;
250 + CGSize *cg_advances;
251 + CGPoint *cg_positions;
254 CGAffineTransform textTransform;
256 + cairo_scaled_font_t *scaled_font;
258 } unbounded_show_glyphs_t;
260 @@ -679,12 +699,6 @@ _cairo_quartz_fixup_unbounded_operation
262 CGContextEOFillPath (cgc);
263 } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
264 - CGContextSetFont (cgc, op->u.show_glyphs.font);
265 - CGContextSetFontSize (cgc, 1.0);
266 - CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
267 - CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
268 - CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
270 if (op->u.show_glyphs.isClipping) {
271 /* Note that the comment in show_glyphs about kCGTextClip
272 * and the text transform still applies here; however, the
273 @@ -693,12 +707,25 @@ _cairo_quartz_fixup_unbounded_operation
274 CGContextSetTextDrawingMode (cgc, kCGTextClip);
275 CGContextSaveGState (cgc);
277 + CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
278 + CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
279 + if (CTFontDrawGlyphsPtr) {
280 + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
281 + op->u.show_glyphs.cg_glyphs,
282 + op->u.show_glyphs.u.cg_positions,
283 + op->u.show_glyphs.nglyphs,
286 + CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
287 + CGContextSetFontSize (cgc, 1.0);
288 + CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
290 + CGContextShowGlyphsWithAdvances (cgc,
291 + op->u.show_glyphs.cg_glyphs,
292 + op->u.show_glyphs.u.cg_advances,
293 + op->u.show_glyphs.nglyphs);
295 - CGContextShowGlyphsWithAdvances (cgc,
296 - op->u.show_glyphs.cg_glyphs,
297 - op->u.show_glyphs.cg_advances,
298 - op->u.show_glyphs.nglyphs);
301 if (op->u.show_glyphs.isClipping) {
302 CGContextClearRect (cgc, clipBoxRound);
303 CGContextRestoreGState (cgc);
304 @@ -1102,12 +1129,12 @@ DataProviderReleaseCallback (void *info,
306 quartz_source_image_t *source_img = info;
307 _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
308 + cairo_surface_destroy (source_img->surface);
312 static cairo_status_t
313 -_cairo_surface_to_cgimage (cairo_surface_t *target,
314 - cairo_surface_t *source,
315 +_cairo_surface_to_cgimage (cairo_surface_t *source,
316 CGImageRef *image_out)
318 cairo_status_t status;
319 @@ -1127,9 +1154,14 @@ _cairo_surface_to_cgimage (cairo_surface
322 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
323 - *image_out = CGBitmapContextCreateImage (surface->cgContext);
325 - return CAIRO_STATUS_SUCCESS;
326 + if (!surface->bitmapContextImage) {
327 + surface->bitmapContextImage =
328 + CGBitmapContextCreateImage (surface->cgContext);
330 + if (surface->bitmapContextImage) {
331 + *image_out = CGImageRetain (surface->bitmapContextImage);
332 + return CAIRO_STATUS_SUCCESS;
337 @@ -1137,10 +1169,11 @@ _cairo_surface_to_cgimage (cairo_surface
338 if (source_img == NULL)
339 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
341 - source_img->surface = source;
342 + source_img->surface = cairo_surface_reference(source);
344 status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
346 + cairo_surface_destroy (source_img->surface);
350 @@ -1251,7 +1284,7 @@ _cairo_quartz_cairo_repeating_surface_pa
351 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
354 - status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
355 + status = _cairo_surface_to_cgimage (pat_surf, &image);
359 @@ -1322,16 +1355,43 @@ typedef enum {
369 } cairo_quartz_action_t;
371 -static cairo_quartz_action_t
372 +/* State used during a drawing operation. */
374 + CGContextRef context;
375 + cairo_quartz_action_t action;
377 + // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
378 + CGAffineTransform transform;
380 + // Used with DO_IMAGE and DO_TILED_IMAGE
382 + cairo_surface_t *imageSurface;
384 + // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
387 + // Used with DO_LAYER
390 + // Used with DO_SHADING
391 + CGShadingRef shading;
393 + // Used with DO_PATTERN
394 + CGPatternRef pattern;
395 +} cairo_quartz_drawing_state_t;
398 _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
399 - const cairo_pattern_t *source)
400 + const cairo_pattern_t *source,
401 + cairo_quartz_drawing_state_t *state)
403 - CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
404 + CGRect clipBox = CGContextGetClipBoundingBox (state->context);
407 cairo_surface_t *fallback;
408 @@ -1340,8 +1400,10 @@ _cairo_quartz_setup_fallback_source (cai
409 cairo_status_t status;
411 if (clipBox.size.width == 0.0f ||
412 - clipBox.size.height == 0.0f)
414 + clipBox.size.height == 0.0f) {
415 + state->action = DO_NOTHING;
419 x0 = floor(clipBox.origin.x);
420 y0 = floor(clipBox.origin.y);
421 @@ -1384,18 +1446,21 @@ _cairo_quartz_setup_fallback_source (cai
425 - status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
427 - return DO_UNSUPPORTED;
431 - surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
432 - surface->sourceImage = img;
433 - surface->sourceImageSurface = fallback;
434 - surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
435 + status = _cairo_surface_to_cgimage (fallback, &img);
437 + state->action = DO_UNSUPPORTED;
441 + state->action = DO_NOTHING;
446 + state->imageRect = CGRectMake (0.0, 0.0, w, h);
447 + state->image = img;
448 + state->imageSurface = fallback;
449 + state->transform = CGAffineTransformMakeTranslation (x0, y0);
450 + state->action = DO_IMAGE;
454 @@ -1411,10 +1476,11 @@ based on the extents of the object (the
455 we don't want the rasterization of the entire gradient to depend on the
458 -static cairo_quartz_action_t
460 _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
461 const cairo_linear_pattern_t *lpat,
462 - cairo_rectangle_int_t *extents)
463 + cairo_rectangle_int_t *extents,
464 + cairo_quartz_drawing_state_t *state)
466 const cairo_pattern_t *abspat = &lpat->base.base;
468 @@ -1424,9 +1490,10 @@ _cairo_quartz_setup_linear_source (cairo
469 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
471 if (lpat->base.n_stops == 0) {
472 - CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
473 - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
475 + CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
476 + CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
477 + state->action = DO_SOLID;
481 if (lpat->p1.x == lpat->p2.x &&
482 @@ -1436,12 +1503,13 @@ _cairo_quartz_setup_linear_source (cairo
483 * Whatever the correct behaviour is, let's at least have only pixman's
484 * implementation to worry about.
486 - return _cairo_quartz_setup_fallback_source (surface, abspat);
487 + _cairo_quartz_setup_fallback_source (surface, abspat, state);
491 mat = abspat->matrix;
492 cairo_matrix_invert (&mat);
493 - _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
494 + _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
496 rgb = CGColorSpaceCreateDeviceRGB();
498 @@ -1461,21 +1529,22 @@ _cairo_quartz_setup_linear_source (cairo
502 - surface->sourceShading = CGShadingCreateAxial (rgb,
506 + state->shading = CGShadingCreateAxial (rgb,
511 CGColorSpaceRelease(rgb);
512 CGFunctionRelease(gradFunc);
515 + state->action = DO_SHADING;
518 -static cairo_quartz_action_t
520 _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
521 const cairo_radial_pattern_t *rpat,
522 - cairo_rectangle_int_t *extents)
523 + cairo_rectangle_int_t *extents,
524 + cairo_quartz_drawing_state_t *state)
526 const cairo_pattern_t *abspat = &rpat->base.base;
528 @@ -1494,9 +1563,10 @@ _cairo_quartz_setup_radial_source (cairo
529 double centerDistance = sqrt (dx*dx + dy*dy);
531 if (rpat->base.n_stops == 0) {
532 - CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
533 - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
535 + CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
536 + CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
537 + state->action = DO_SOLID;
541 if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
542 @@ -1507,12 +1577,13 @@ _cairo_quartz_setup_radial_source (cairo
543 * implementation to worry about.
544 * Note that this also catches the cases where r1 == r2.
546 - return _cairo_quartz_setup_fallback_source (surface, abspat);
547 + _cairo_quartz_setup_fallback_source (surface, abspat, state);
551 mat = abspat->matrix;
552 cairo_matrix_invert (&mat);
553 - _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
554 + _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
556 rgb = CGColorSpaceCreateDeviceRGB();
558 @@ -1531,90 +1602,79 @@ _cairo_quartz_setup_radial_source (cairo
562 - surface->sourceShading = CGShadingCreateRadial (rgb,
569 + state->shading = CGShadingCreateRadial (rgb,
577 CGColorSpaceRelease(rgb);
578 CGFunctionRelease(gradFunc);
581 + state->action = DO_SHADING;
584 -static cairo_quartz_action_t
585 -_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
586 - const cairo_pattern_t *source,
587 - cairo_rectangle_int_t *extents)
589 +_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
590 + const cairo_surface_pattern_t *spat,
591 + cairo_rectangle_int_t *extents,
592 + cairo_quartz_drawing_state_t *state)
594 - assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
596 - surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
597 - CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
598 + const cairo_pattern_t *source = &spat->base;
599 + CGContextRef context = state->context;
601 - if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
602 - cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
604 - CGContextSetRGBStrokeColor (surface->cgContext,
606 - solid->color.green,
608 - solid->color.alpha);
609 - CGContextSetRGBFillColor (surface->cgContext,
611 - solid->color.green,
613 - solid->color.alpha);
618 - if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
619 - const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
620 - return _cairo_quartz_setup_linear_source (surface, lpat, extents);
623 - if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
624 - const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
625 - return _cairo_quartz_setup_radial_source (surface, rpat, extents);
628 - if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
629 - (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
630 + if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
631 + (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
633 - const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
634 cairo_surface_t *pat_surf = spat->surface;
636 cairo_matrix_t m = spat->base.matrix;
637 cairo_rectangle_int_t extents;
638 - cairo_status_t status;
639 CGAffineTransform xform;
641 cairo_fixed_t fw, fh;
642 cairo_bool_t is_bounded;
643 + cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
644 + cairo_status_t status;
646 - status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
648 - return DO_UNSUPPORTED;
651 + cairo_matrix_invert(&m);
652 + _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
654 - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
655 + /* Draw nonrepeating CGLayer surface using DO_LAYER */
656 + if (!repeat && cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
657 + cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
658 + if (quartz_surf->cgLayer) {
659 + state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
660 + state->layer = quartz_surf->cgLayer;
661 + state->action = DO_LAYER;
666 + status = _cairo_surface_to_cgimage (pat_surf, &img);
668 + state->action = DO_UNSUPPORTED;
672 + state->action = DO_NOTHING;
676 - surface->sourceImage = img;
677 + /* XXXroc what is this for? */
678 + CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
680 - cairo_matrix_invert(&m);
681 - _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
682 + state->image = img;
684 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
687 - if (source->extend == CAIRO_EXTEND_NONE) {
688 - surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
691 + state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
692 + state->action = DO_IMAGE;
696 /* Quartz seems to tile images at pixel-aligned regions only -- this
697 @@ -1624,8 +1684,8 @@ _cairo_quartz_setup_source (cairo_quartz
698 * epsilon), and if not, fall back to the CGPattern type.
701 - xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
702 - surface->sourceTransform);
703 + xform = CGAffineTransformConcat (CGContextGetCTM (context),
706 srcRect = CGRectMake (0, 0, extents.width, extents.height);
707 srcRect = CGRectApplyAffineTransform (srcRect, xform);
708 @@ -1646,101 +1706,218 @@ _cairo_quartz_setup_source (cairo_quartz
710 srcRect = CGRectApplyAffineTransform (srcRect, xform);
712 - surface->sourceImageRect = srcRect;
714 - return DO_TILED_IMAGE;
715 + state->imageRect = srcRect;
716 + state->action = DO_TILED_IMAGE;
720 /* Fall through to generic SURFACE case */
723 - if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
724 - cairo_quartz_float_t patternAlpha = 1.0f;
725 - CGColorSpaceRef patternSpace;
726 - CGPatternRef pattern;
727 - cairo_int_status_t status;
729 - status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
730 - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
733 - return DO_UNSUPPORTED;
735 - // Save before we change the pattern, colorspace, etc. so that
736 - // we can restore and make sure that quartz releases our
737 - // pattern (which may be stack allocated)
738 - CGContextSaveGState(surface->cgContext);
740 - patternSpace = CGColorSpaceCreatePattern(NULL);
741 - CGContextSetFillColorSpace (surface->cgContext, patternSpace);
742 - CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
743 - CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
744 - CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
745 - CGColorSpaceRelease (patternSpace);
747 - /* Quartz likes to munge the pattern phase (as yet unexplained
748 - * why); force it to 0,0 as we've already baked in the correct
749 - * pattern translation into the pattern matrix
751 - CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
753 - surface->sourcePattern = pattern;
754 + CGFloat patternAlpha = 1.0f;
755 + CGColorSpaceRef patternSpace;
756 + CGPatternRef pattern;
757 + cairo_int_status_t status;
760 + status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
761 + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
762 + state->action = DO_NOTHING;
766 + state->action = DO_UNSUPPORTED;
770 - return DO_UNSUPPORTED;
771 + patternSpace = CGColorSpaceCreatePattern (NULL);
772 + CGContextSetFillColorSpace (context, patternSpace);
773 + CGContextSetFillPattern (context, pattern, &patternAlpha);
774 + CGContextSetStrokeColorSpace (context, patternSpace);
775 + CGContextSetStrokePattern (context, pattern, &patternAlpha);
776 + CGColorSpaceRelease (patternSpace);
778 + /* Quartz likes to munge the pattern phase (as yet unexplained
779 + * why); force it to 0,0 as we've already baked in the correct
780 + * pattern translation into the pattern matrix
782 + CGContextSetPatternPhase (context, CGSizeMake(0,0));
784 + state->pattern = pattern;
785 + state->action = DO_PATTERN;
790 + * Call this before any operation that can modify the contents of a
791 + * cairo_quartz_surface_t.
794 -_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
795 - const cairo_pattern_t *source)
796 +_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
798 - CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
799 + if (surface->bitmapContextImage) {
800 + CGImageRelease (surface->bitmapContextImage);
801 + surface->bitmapContextImage = NULL;
805 - if (surface->sourceImage) {
806 - CGImageRelease(surface->sourceImage);
807 - surface->sourceImage = NULL;
809 + * Sets up internal state to be used to draw the source mask, stored in
810 + * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
811 + * surface->cgContext.
813 +static cairo_quartz_drawing_state_t
814 +_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
815 + const cairo_pattern_t *source,
816 + cairo_operator_t op,
817 + cairo_rectangle_int_t *extents)
819 + CGContextRef context = surface->cgContext;
820 + cairo_quartz_drawing_state_t state;
821 + cairo_status_t status;
823 - cairo_surface_destroy(surface->sourceImageSurface);
824 - surface->sourceImageSurface = NULL;
825 + state.context = context;
826 + state.image = NULL;
827 + state.imageSurface = NULL;
828 + state.layer = NULL;
829 + state.shading = NULL;
830 + state.pattern = NULL;
832 + _cairo_quartz_surface_will_change (surface);
834 + // Save before we change the pattern, colorspace, etc. so that
835 + // we can restore and make sure that quartz releases our
836 + // pattern (which may be stack allocated)
837 + CGContextSaveGState(context);
839 + CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
841 + status = _cairo_quartz_surface_set_cairo_operator (surface, op);
842 + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
843 + state.action = DO_NOTHING;
847 + state.action = DO_UNSUPPORTED;
851 - if (surface->sourceShading) {
852 - CGShadingRelease(surface->sourceShading);
853 - surface->sourceShading = NULL;
854 + if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
855 + cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
857 + CGContextSetRGBStrokeColor (context,
859 + solid->color.green,
861 + solid->color.alpha);
862 + CGContextSetRGBFillColor (context,
864 + solid->color.green,
866 + solid->color.alpha);
868 + state.action = DO_SOLID;
872 + if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
873 + const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
874 + _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
878 + if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
879 + const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
880 + _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
884 - if (surface->sourcePattern) {
885 - CGPatternRelease(surface->sourcePattern);
886 - // To tear down the pattern and colorspace
887 - CGContextRestoreGState(surface->cgContext);
888 + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
889 + if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) &&
890 + CGContextGetAlphaPtr &&
891 + CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
892 + // Quartz won't touch pixels outside the bounds of the
893 + // source surface, so we can just go ahead and use Copy here
894 + // to accelerate things.
895 + // Quartz won't necessarily be able to do this optimization internally;
896 + // for CGLayer surfaces, we can know all the pixels are opaque
897 + // (because it's CONTENT_COLOR), but Quartz won't know.
898 + CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
901 - surface->sourcePattern = NULL;
902 + const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
903 + _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
908 + state.action = DO_UNSUPPORTED;
913 + * 1) Tears down internal state used to draw the source
914 + * 2) Does CGContextRestoreGState(state->context)
917 -_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
918 +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
920 - assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
921 + if (state->image) {
922 + CGImageRelease(state->image);
925 - CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
926 - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
927 - CGContextScaleCTM (surface->cgContext, 1, -1);
929 - if (action == DO_IMAGE) {
930 - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
931 - if (!_cairo_operator_bounded_by_source(op)) {
932 - CGContextBeginPath (surface->cgContext);
933 - CGContextAddRect (surface->cgContext, surface->sourceImageRect);
934 - CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
935 - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
936 - CGContextEOFillPath (surface->cgContext);
937 + if (state->imageSurface) {
938 + cairo_surface_destroy(state->imageSurface);
941 + if (state->shading) {
942 + CGShadingRelease(state->shading);
945 + if (state->pattern) {
946 + CGPatternRelease(state->pattern);
949 + CGContextRestoreGState(state->context);
954 +_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
957 + ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
958 + (state->layer && state->action == DO_LAYER)));
960 + CGContextConcatCTM (state->context, state->transform);
961 + CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
962 + CGContextScaleCTM (state->context, 1, -1);
964 + if (state->action == DO_TILED_IMAGE) {
965 + CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
966 + /* no need to worry about unbounded operators, since tiled images
967 + fill the entire clip region */
969 + if (state->action == DO_LAYER) {
970 + /* Note that according to Apple docs it's completely legal
971 + * to draw a CGLayer to any CGContext, even one it wasn't
974 + CGContextSetInterpolationQuality (state->context, kCGInterpolationNone);
975 + CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
978 + CGContextDrawImage (state->context, state->imageRect, state->image);
981 + /* disable this EXTEND_NONE correctness code because we use this path
982 + * for both EXTEND_NONE and EXTEND_PAD */
983 + if (0 && !_cairo_operator_bounded_by_source (op)) {
984 + CGContextBeginPath (state->context);
985 + CGContextAddRect (state->context, state->imageRect);
986 + CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
987 + CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
988 + CGContextEOFillPath (state->context);
991 - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
996 @@ -1762,6 +1939,7 @@ _cairo_quartz_get_image (cairo_quartz_su
999 if (surface->imageSurfaceEquiv) {
1000 + CGContextFlush(surface->cgContext);
1001 *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
1002 return CAIRO_STATUS_SUCCESS;
1004 @@ -1773,6 +1951,7 @@ _cairo_quartz_get_image (cairo_quartz_su
1005 CGColorSpaceRef colorspace;
1006 unsigned int color_comps;
1008 + CGContextFlush(surface->cgContext);
1009 imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
1011 #ifdef USE_10_3_WORKAROUNDS
1012 @@ -1860,53 +2039,79 @@ _cairo_quartz_surface_finish (void *abst
1014 surface->cgContext = NULL;
1016 + if (surface->bitmapContextImage) {
1017 + CGImageRelease (surface->bitmapContextImage);
1018 + surface->bitmapContextImage = NULL;
1021 if (surface->imageSurfaceEquiv) {
1022 + if (surface->ownsData)
1023 + _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
1024 cairo_surface_destroy (surface->imageSurfaceEquiv);
1025 surface->imageSurfaceEquiv = NULL;
1026 + } else if (surface->imageData && surface->ownsData) {
1027 + free (surface->imageData);
1030 - if (surface->imageData) {
1031 - free (surface->imageData);
1032 - surface->imageData = NULL;
1033 + surface->imageData = NULL;
1035 + if (surface->cgLayer) {
1036 + CGLayerRelease (surface->cgLayer);
1039 return CAIRO_STATUS_SUCCESS;
1042 static cairo_status_t
1043 -_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1044 - cairo_image_surface_t **image_out,
1045 - void **image_extra)
1046 +_cairo_quartz_surface_acquire_image (void *abstract_surface,
1047 + cairo_image_surface_t **image_out,
1048 + void **image_extra)
1050 cairo_int_status_t status;
1051 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1053 - //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1055 - status = _cairo_quartz_get_image (surface, image_out);
1057 - return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1059 *image_extra = NULL;
1061 - return CAIRO_STATUS_SUCCESS;
1063 + /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
1065 -static cairo_surface_t *
1066 -_cairo_quartz_surface_snapshot (void *abstract_surface)
1068 - cairo_int_status_t status;
1069 - cairo_quartz_surface_t *surface = abstract_surface;
1070 - cairo_image_surface_t *image;
1071 + status = _cairo_quartz_get_image (surface, image_out);
1073 - if (surface->imageSurfaceEquiv)
1075 + if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
1076 + /* copy the layer into a Quartz bitmap context so we can get the data */
1077 + cairo_surface_t *tmp =
1078 + cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
1079 + surface->extents.width,
1080 + surface->extents.height);
1081 + cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
1083 + /* if surface creation failed, we won't have a Quartz surface here */
1084 + if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
1085 + tmp_surface->imageSurfaceEquiv) {
1086 + CGContextSaveGState (tmp_surface->cgContext);
1087 + CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
1088 + CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
1089 + /* Note that according to Apple docs it's completely legal
1090 + * to draw a CGLayer to any CGContext, even one it wasn't
1093 + CGContextDrawLayerAtPoint (tmp_surface->cgContext,
1094 + CGPointMake (0.0, 0.0),
1095 + surface->cgLayer);
1096 + CGContextRestoreGState (tmp_surface->cgContext);
1098 + *image_out = (cairo_image_surface_t*)
1099 + cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
1100 + *image_extra = tmp;
1101 + status = CAIRO_STATUS_SUCCESS;
1103 + cairo_surface_destroy (tmp);
1107 - status = _cairo_quartz_get_image (surface, &image);
1108 - if (unlikely (status))
1109 - return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
1111 + return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1113 - return &image->base;
1114 + return CAIRO_STATUS_SUCCESS;
1118 @@ -1915,6 +2120,10 @@ _cairo_quartz_surface_release_source_ima
1121 cairo_surface_destroy ((cairo_surface_t *) image);
1123 + if (image_extra) {
1124 + cairo_surface_destroy ((cairo_surface_t *) image_extra);
1129 @@ -1926,18 +2135,16 @@ _cairo_quartz_surface_acquire_dest_image
1132 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1133 - cairo_int_status_t status;
1135 ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
1137 - status = _cairo_quartz_get_image (surface, image_out);
1139 - return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1141 *image_rect = surface->extents;
1142 *image_extra = NULL;
1144 - return CAIRO_STATUS_SUCCESS;
1145 + _cairo_quartz_surface_will_change (surface);
1147 + return _cairo_quartz_surface_acquire_image (abstract_surface,
1148 + image_out, image_extra);
1152 @@ -1947,11 +2154,31 @@ _cairo_quartz_surface_release_dest_image
1153 cairo_rectangle_int_t *image_rect,
1156 - //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1158 - //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
1159 + /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
1161 cairo_surface_destroy ((cairo_surface_t *) image);
1163 + if (image_extra) {
1164 + /* we need to write the data from the temp surface back to the layer */
1165 + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1166 + cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
1168 + cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
1170 + cairo_surface_destroy (&tmp_surface->base);
1174 + CGContextSaveGState (surface->cgContext);
1175 + CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
1176 + CGContextScaleCTM (surface->cgContext, 1, -1);
1177 + CGContextDrawImage (surface->cgContext,
1178 + CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
1180 + CGContextRestoreGState (surface->cgContext);
1182 + cairo_surface_destroy (&tmp_surface->base);
1186 static cairo_surface_t *
1187 @@ -1960,10 +2187,13 @@ _cairo_quartz_surface_create_similar (vo
1191 - /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
1193 + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1194 cairo_format_t format;
1196 + if (surface->cgLayer)
1197 + return cairo_quartz_surface_create_cg_layer (abstract_surface, content,
1200 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1201 format = CAIRO_FORMAT_ARGB32;
1202 else if (content == CAIRO_CONTENT_COLOR)
1203 @@ -2027,7 +2257,7 @@ _cairo_quartz_surface_clone_similar (voi
1207 - status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
1208 + status = _cairo_surface_to_cgimage (src, &quartz_image);
1210 return CAIRO_INT_STATUS_UNSUPPORTED;
1212 @@ -2087,7 +2317,7 @@ _cairo_quartz_surface_paint_cg (void *ab
1214 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1215 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1216 - cairo_quartz_action_t action;
1217 + cairo_quartz_drawing_state_t state;
1219 ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
1221 @@ -2098,31 +2328,24 @@ _cairo_quartz_surface_paint_cg (void *ab
1225 - rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
1226 - if (unlikely (rv))
1227 - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
1228 + state = _cairo_quartz_setup_state (surface, source, op, NULL);
1230 - action = _cairo_quartz_setup_source (surface, source, NULL);
1232 - if (action == DO_SOLID || action == DO_PATTERN) {
1233 - CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1234 - surface->extents.y,
1235 - surface->extents.width,
1236 - surface->extents.height));
1237 - } else if (action == DO_SHADING) {
1238 - CGContextSaveGState (surface->cgContext);
1239 - CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1240 - CGContextDrawShading (surface->cgContext, surface->sourceShading);
1241 - CGContextRestoreGState (surface->cgContext);
1242 - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1243 - CGContextSaveGState (surface->cgContext);
1244 - _cairo_quartz_draw_image (surface, op, action);
1245 - CGContextRestoreGState (surface->cgContext);
1246 - } else if (action != DO_NOTHING) {
1247 + if (state.action == DO_SOLID || state.action == DO_PATTERN) {
1248 + CGContextFillRect (state.context, CGRectMake(surface->extents.x,
1249 + surface->extents.y,
1250 + surface->extents.width,
1251 + surface->extents.height));
1252 + } else if (state.action == DO_SHADING) {
1253 + CGContextConcatCTM (state.context, state.transform);
1254 + CGContextDrawShading (state.context, state.shading);
1255 + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
1256 + state.action == DO_LAYER) {
1257 + _cairo_quartz_draw_image (&state, op);
1258 + } else if (state.action != DO_NOTHING) {
1259 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1262 - _cairo_quartz_teardown_source (surface, source);
1263 + _cairo_quartz_teardown_state (&state);
1265 ND((stderr, "-- paint\n"));
1267 @@ -2186,7 +2409,7 @@ _cairo_quartz_surface_fill_cg (void *abs
1269 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1270 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1271 - cairo_quartz_action_t action;
1272 + cairo_quartz_drawing_state_t state;
1273 CGPathRef path_for_unbounded = NULL;
1275 ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
1276 @@ -2198,14 +2421,6 @@ _cairo_quartz_surface_fill_cg (void *abs
1280 - rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
1281 - if (unlikely (rv))
1282 - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
1284 - CGContextSaveGState (surface->cgContext);
1286 - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1288 if (_cairo_quartz_source_needs_extents (source))
1290 /* We don't need precise extents since these are only used to
1291 @@ -2213,46 +2428,47 @@ _cairo_quartz_surface_fill_cg (void *abs
1293 cairo_rectangle_int_t path_extents;
1294 _cairo_path_fixed_approximate_fill_extents (path, &path_extents);
1295 - action = _cairo_quartz_setup_source (surface, source, &path_extents);
1296 + state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
1298 - action = _cairo_quartz_setup_source (surface, source, NULL);
1299 + state = _cairo_quartz_setup_state (surface, source, op, NULL);
1302 - _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
1303 + CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
1305 + _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
1307 if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
1308 - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1309 + path_for_unbounded = CGContextCopyPathPtr (state.context);
1311 - if (action == DO_SOLID || action == DO_PATTERN) {
1312 + if (state.action == DO_SOLID || state.action == DO_PATTERN) {
1313 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1314 - CGContextFillPath (surface->cgContext);
1315 + CGContextFillPath (state.context);
1317 - CGContextEOFillPath (surface->cgContext);
1318 - } else if (action == DO_SHADING) {
1319 + CGContextEOFillPath (state.context);
1320 + } else if (state.action == DO_SHADING) {
1322 // we have to clip and then paint the shading; we can't fill
1324 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1325 - CGContextClip (surface->cgContext);
1326 + CGContextClip (state.context);
1328 - CGContextEOClip (surface->cgContext);
1329 + CGContextEOClip (state.context);
1331 - CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1332 - CGContextDrawShading (surface->cgContext, surface->sourceShading);
1333 - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1334 + CGContextConcatCTM (state.context, state.transform);
1335 + CGContextDrawShading (state.context, state.shading);
1336 + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
1337 + state.action == DO_LAYER) {
1338 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1339 - CGContextClip (surface->cgContext);
1340 + CGContextClip (state.context);
1342 - CGContextEOClip (surface->cgContext);
1343 + CGContextEOClip (state.context);
1345 - _cairo_quartz_draw_image (surface, op, action);
1346 - } else if (action != DO_NOTHING) {
1347 + _cairo_quartz_draw_image (&state, op);
1348 + } else if (state.action != DO_NOTHING) {
1349 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1352 - _cairo_quartz_teardown_source (surface, source);
1354 - CGContextRestoreGState (surface->cgContext);
1355 + _cairo_quartz_teardown_state (&state);
1357 if (path_for_unbounded) {
1358 unbounded_op_data_t ub;
1359 @@ -2319,7 +2535,7 @@ _cairo_quartz_surface_stroke_cg (void *a
1361 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1362 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1363 - cairo_quartz_action_t action;
1364 + cairo_quartz_drawing_state_t state;
1365 CGAffineTransform origCTM, strokeTransform;
1366 CGPathRef path_for_unbounded = NULL;
1368 @@ -2336,16 +2552,25 @@ _cairo_quartz_surface_stroke_cg (void *a
1370 return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
1372 + if (_cairo_quartz_source_needs_extents (source))
1374 + cairo_rectangle_int_t path_extents;
1375 + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
1376 + state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
1378 + state = _cairo_quartz_setup_state (surface, source, op, NULL);
1381 // Turning antialiasing off used to cause misrendering with
1382 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1383 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1384 - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1385 - CGContextSetLineWidth (surface->cgContext, style->line_width);
1386 - CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1387 - CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1388 - CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1389 + CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
1390 + CGContextSetLineWidth (state.context, style->line_width);
1391 + CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1392 + CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1393 + CGContextSetMiterLimit (state.context, style->miter_limit);
1395 - origCTM = CGContextGetCTM (surface->cgContext);
1396 + origCTM = CGContextGetCTM (state.context);
1398 if (style->dash && style->num_dashes) {
1399 #define STATIC_DASH 32
1400 @@ -2368,72 +2593,62 @@ _cairo_quartz_surface_stroke_cg (void *a
1404 - CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
1405 + CGContextSetLineDash (state.context, 0, NULL, 0);
1407 - CGContextSaveGState (surface->cgContext);
1409 + _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
1411 - if (_cairo_quartz_source_needs_extents (source))
1413 - cairo_rectangle_int_t path_extents;
1414 - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
1415 - action = _cairo_quartz_setup_source (surface, source, &path_extents);
1417 - action = _cairo_quartz_setup_source (surface, source, NULL);
1420 - _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
1421 + _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1422 + CGContextConcatCTM (state.context, strokeTransform);
1424 if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
1425 - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1427 - _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1428 - CGContextConcatCTM (surface->cgContext, strokeTransform);
1429 + path_for_unbounded = CGContextCopyPathPtr (state.context);
1431 - if (action == DO_SOLID || action == DO_PATTERN) {
1432 - CGContextStrokePath (surface->cgContext);
1433 - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1434 - CGContextReplacePathWithStrokedPath (surface->cgContext);
1435 - CGContextClip (surface->cgContext);
1437 - CGContextSetCTM (surface->cgContext, origCTM);
1438 - _cairo_quartz_draw_image (surface, op, action);
1439 - } else if (action == DO_SHADING) {
1440 - CGContextReplacePathWithStrokedPath (surface->cgContext);
1441 - CGContextClip (surface->cgContext);
1443 - CGContextSetCTM (surface->cgContext, origCTM);
1445 - CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1446 - CGContextDrawShading (surface->cgContext, surface->sourceShading);
1447 - } else if (action != DO_NOTHING) {
1448 + if (state.action == DO_SOLID || state.action == DO_PATTERN) {
1449 + CGContextStrokePath (state.context);
1450 + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
1451 + state.action == DO_LAYER) {
1452 + CGContextReplacePathWithStrokedPath (state.context);
1453 + CGContextClip (state.context);
1455 + CGContextSetCTM (state.context, origCTM);
1456 + _cairo_quartz_draw_image (&state, op);
1457 + } else if (state.action == DO_SHADING) {
1458 + CGContextReplacePathWithStrokedPath (state.context);
1459 + CGContextClip (state.context);
1461 + CGContextSetCTM (state.context, origCTM);
1463 + CGContextConcatCTM (state.context, state.transform);
1464 + CGContextDrawShading (state.context, state.shading);
1465 + } else if (state.action != DO_NOTHING) {
1466 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1470 - _cairo_quartz_teardown_source (surface, source);
1472 - CGContextRestoreGState (surface->cgContext);
1474 if (path_for_unbounded) {
1475 unbounded_op_data_t ub;
1476 ub.op = UNBOUNDED_STROKE_FILL;
1477 ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
1479 - CGContextBeginPath (surface->cgContext);
1480 - CGContextAddPath (surface->cgContext, path_for_unbounded);
1481 + CGContextBeginPath (state.context);
1482 + CGContextAddPath (state.context, path_for_unbounded);
1483 CGPathRelease (path_for_unbounded);
1485 - CGContextSaveGState (surface->cgContext);
1486 - CGContextConcatCTM (surface->cgContext, strokeTransform);
1487 - CGContextReplacePathWithStrokedPath (surface->cgContext);
1488 - CGContextRestoreGState (surface->cgContext);
1489 + CGContextSaveGState (state.context);
1490 + CGContextConcatCTM (state.context, strokeTransform);
1491 + CGContextReplacePathWithStrokedPath (state.context);
1492 + CGContextRestoreGState (state.context);
1494 - ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
1495 + ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context);
1497 _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
1498 CGPathRelease (ub.u.stroke_fill.cgPath);
1502 + _cairo_quartz_teardown_state (&state);
1504 ND((stderr, "-- stroke\n"));
1507 @@ -2490,18 +2705,22 @@ _cairo_quartz_surface_show_glyphs_cg (vo
1508 CGGlyph glyphs_static[STATIC_BUF_SIZE];
1509 CGSize cg_advances_static[STATIC_BUF_SIZE];
1510 CGGlyph *cg_glyphs = &glyphs_static[0];
1511 + /* We'll use the cg_advances array for either advances or positions,
1512 + depending which API we're using to actually draw. The types involved
1513 + have the same size, so this is safe. */
1514 CGSize *cg_advances = &cg_advances_static[0];
1516 cairo_rectangle_int_t glyph_extents;
1517 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1518 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1519 - cairo_quartz_action_t action;
1520 + cairo_quartz_drawing_state_t state;
1521 cairo_quartz_float_t xprev, yprev;
1523 CGFontRef cgfref = NULL;
1525 cairo_bool_t isClipping = FALSE;
1526 cairo_bool_t didForceFontSmoothing = FALSE;
1527 + cairo_antialias_t effective_antialiasing;
1529 if (IS_EMPTY(surface))
1530 return CAIRO_STATUS_SUCCESS;
1531 @@ -2516,54 +2735,51 @@ _cairo_quartz_surface_show_glyphs_cg (vo
1535 - rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
1536 - if (unlikely (rv))
1537 - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
1539 - CGContextSaveGState (surface->cgContext);
1541 if (_cairo_quartz_source_needs_extents (source) &&
1542 !_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
1543 &glyph_extents, NULL))
1545 - action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
1546 + state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
1548 - action = _cairo_quartz_setup_source (surface, source, NULL);
1549 + state = _cairo_quartz_setup_state (surface, source, op, NULL);
1552 - if (action == DO_SOLID || action == DO_PATTERN) {
1553 - CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
1554 - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
1555 - CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
1556 + if (state.action == DO_SOLID || state.action == DO_PATTERN) {
1557 + CGContextSetTextDrawingMode (state.context, kCGTextFill);
1558 + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
1559 + state.action == DO_SHADING || state.action == DO_LAYER) {
1560 + CGContextSetTextDrawingMode (state.context, kCGTextClip);
1563 - if (action != DO_NOTHING)
1564 + if (state.action != DO_NOTHING)
1565 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1569 /* this doesn't addref */
1570 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1571 - CGContextSetFont (surface->cgContext, cgfref);
1572 - CGContextSetFontSize (surface->cgContext, 1.0);
1573 + CGContextSetFont (state.context, cgfref);
1574 + CGContextSetFontSize (state.context, 1.0);
1576 + effective_antialiasing = scaled_font->options.antialias;
1578 switch (scaled_font->options.antialias) {
1579 case CAIRO_ANTIALIAS_SUBPIXEL:
1580 - CGContextSetShouldAntialias (surface->cgContext, TRUE);
1581 - CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
1582 + CGContextSetShouldAntialias (state.context, TRUE);
1583 + CGContextSetShouldSmoothFonts (state.context, TRUE);
1584 if (CGContextSetAllowsFontSmoothingPtr &&
1585 - !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
1586 + !CGContextGetAllowsFontSmoothingPtr (state.context))
1588 didForceFontSmoothing = TRUE;
1589 - CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
1590 + CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
1593 case CAIRO_ANTIALIAS_NONE:
1594 - CGContextSetShouldAntialias (surface->cgContext, FALSE);
1595 + CGContextSetShouldAntialias (state.context, FALSE);
1597 case CAIRO_ANTIALIAS_GRAY:
1598 - CGContextSetShouldAntialias (surface->cgContext, TRUE);
1599 - CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
1600 + CGContextSetShouldAntialias (state.context, TRUE);
1601 + CGContextSetShouldSmoothFonts (state.context, FALSE);
1603 case CAIRO_ANTIALIAS_DEFAULT:
1604 /* Don't do anything */
1605 @@ -2584,57 +2800,84 @@ _cairo_quartz_surface_show_glyphs_cg (vo
1609 + /* scale(1,-1) * scaled_font->scale */
1610 textTransform = CGAffineTransformMake (scaled_font->scale.xx,
1611 scaled_font->scale.yx,
1612 -scaled_font->scale.xy,
1613 -scaled_font->scale.yy,
1615 - _cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform);
1617 - CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
1618 + /* scaled_font->scale_inverse * scale(1,-1) */
1619 + invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
1620 + -scaled_font->scale_inverse.yx,
1621 + scaled_font->scale_inverse.xy,
1622 + -scaled_font->scale_inverse.yy,
1625 - /* Convert our glyph positions to glyph advances. We need n-1 advances,
1626 - * since the advance at index 0 is applied after glyph 0. */
1627 - xprev = glyphs[0].x;
1628 - yprev = glyphs[0].y;
1630 - cg_glyphs[0] = glyphs[0].index;
1632 - for (i = 1; i < num_glyphs; i++) {
1633 - cairo_quartz_float_t xf = glyphs[i].x;
1634 - cairo_quartz_float_t yf = glyphs[i].y;
1635 - cg_glyphs[i] = glyphs[i].index;
1636 - cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
1640 + CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
1642 /* Translate to the first glyph's position before drawing */
1643 - ctm = CGContextGetCTM (surface->cgContext);
1644 - CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
1645 - CGContextConcatCTM (surface->cgContext, textTransform);
1647 - CGContextShowGlyphsWithAdvances (surface->cgContext,
1652 - CGContextSetCTM (surface->cgContext, ctm);
1653 + ctm = CGContextGetCTM (state.context);
1654 + CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
1655 + CGContextConcatCTM (state.context, textTransform);
1657 + if (CTFontDrawGlyphsPtr) {
1658 + /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
1659 + * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
1660 + * fonts like Apple Color Emoji will render properly.
1661 + * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
1662 + * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
1664 + CGPoint *cg_positions = (CGPoint*) cg_advances;
1665 + cairo_quartz_float_t origin_x = glyphs[0].x;
1666 + cairo_quartz_float_t origin_y = glyphs[0].y;
1668 + for (i = 0; i < num_glyphs; i++) {
1669 + CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
1670 + cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
1671 + cg_glyphs[i] = glyphs[i].index;
1674 - if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1675 - _cairo_quartz_draw_image (surface, op, action);
1676 - } else if (action == DO_SHADING) {
1677 - CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1678 - CGContextDrawShading (surface->cgContext, surface->sourceShading);
1679 + CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
1680 + cg_glyphs, cg_positions, num_glyphs, state.context);
1682 + /* Convert our glyph positions to glyph advances. We need n-1 advances,
1683 + * since the advance at index 0 is applied after glyph 0. */
1684 + xprev = glyphs[0].x;
1685 + yprev = glyphs[0].y;
1687 + cg_glyphs[0] = glyphs[0].index;
1689 + for (i = 1; i < num_glyphs; i++) {
1690 + cairo_quartz_float_t xf = glyphs[i].x;
1691 + cairo_quartz_float_t yf = glyphs[i].y;
1692 + cg_glyphs[i] = glyphs[i].index;
1693 + cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
1698 + CGContextShowGlyphsWithAdvances (state.context,
1704 + CGContextSetCTM (state.context, ctm);
1706 + if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
1707 + state.action == DO_LAYER) {
1708 + _cairo_quartz_draw_image (&state, op);
1709 + } else if (state.action == DO_SHADING) {
1710 + CGContextConcatCTM (state.context, state.transform);
1711 + CGContextDrawShading (state.context, state.shading);
1715 - _cairo_quartz_teardown_source (surface, source);
1717 if (didForceFontSmoothing)
1718 - CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
1719 + CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
1721 - CGContextRestoreGState (surface->cgContext);
1722 + _cairo_quartz_teardown_state (&state);
1724 if (rv == CAIRO_STATUS_SUCCESS &&
1726 @@ -2645,10 +2888,17 @@ BAIL:
1728 ub.u.show_glyphs.isClipping = isClipping;
1729 ub.u.show_glyphs.cg_glyphs = cg_glyphs;
1730 - ub.u.show_glyphs.cg_advances = cg_advances;
1731 + if (CTFontDrawGlyphsPtr) {
1732 + /* we're using Core Text API: the cg_advances array was
1733 + reused (above) for glyph positions */
1734 + CGPoint *cg_positions = (CGPoint*) cg_advances;
1735 + ub.u.show_glyphs.u.cg_positions = cg_positions;
1737 + ub.u.show_glyphs.u.cg_advances = cg_advances;
1739 ub.u.show_glyphs.nglyphs = num_glyphs;
1740 ub.u.show_glyphs.textTransform = textTransform;
1741 - ub.u.show_glyphs.font = cgfref;
1742 + ub.u.show_glyphs.scaled_font = scaled_font;
1743 ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
1745 _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
1746 @@ -2717,7 +2967,7 @@ _cairo_quartz_surface_mask_with_surface
1747 cairo_status_t status = CAIRO_STATUS_SUCCESS;
1748 CGAffineTransform ctm, mask_matrix;
1750 - status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
1751 + status = _cairo_surface_to_cgimage (pat_surf, &img);
1755 @@ -2820,7 +3070,9 @@ _cairo_quartz_surface_mask_cg (void *abs
1759 - if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
1760 + /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
1761 + if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
1762 + op == CAIRO_OPERATOR_OVER) {
1763 /* This is easy; we just need to paint with the alpha. */
1764 cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
1766 @@ -2834,8 +3086,11 @@ _cairo_quartz_surface_mask_cg (void *abs
1767 /* If we have CGContextClipToMask, we can do more complex masks */
1768 if (CGContextClipToMaskPtr) {
1769 /* For these, we can skip creating a temporary surface, since we already have one */
1770 - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
1771 + /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
1772 + if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
1773 + mask->extend == CAIRO_EXTEND_NONE) {
1774 return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
1777 return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
1779 @@ -2920,13 +3175,24 @@ _cairo_quartz_surface_clipper_intersect_
1780 return CAIRO_STATUS_SUCCESS;
1783 +static cairo_status_t
1784 +_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
1786 + int width, int height)
1788 + cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1789 + _cairo_quartz_surface_will_change (surface);
1790 + return CAIRO_STATUS_SUCCESS;
1794 // XXXtodo implement show_page; need to figure out how to handle begin/end
1796 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
1797 CAIRO_SURFACE_TYPE_QUARTZ,
1798 _cairo_quartz_surface_create_similar,
1799 _cairo_quartz_surface_finish,
1800 - _cairo_quartz_surface_acquire_source_image,
1801 + _cairo_quartz_surface_acquire_image,
1802 _cairo_quartz_surface_release_source_image,
1803 _cairo_quartz_surface_acquire_dest_image,
1804 _cairo_quartz_surface_release_dest_image,
1805 @@ -2942,7 +3208,7 @@ static const struct _cairo_surface_backe
1806 NULL, /* old_show_glyphs */
1807 NULL, /* get_font_options */
1809 - NULL, /* mark_dirty_rectangle */
1810 + _cairo_quartz_surface_mark_dirty_rectangle,
1811 NULL, /* scaled_font_fini */
1812 NULL, /* scaled_glyph_fini */
1814 @@ -2952,7 +3218,7 @@ static const struct _cairo_surface_backe
1815 _cairo_quartz_surface_fill,
1816 _cairo_quartz_surface_show_glyphs,
1818 - _cairo_quartz_surface_snapshot,
1819 + NULL, /* snapshot */
1820 NULL, /* is_similar */
1821 NULL /* fill_stroke */
1823 @@ -3004,6 +3270,9 @@ _cairo_quartz_surface_create_internal (C
1825 surface->imageData = NULL;
1826 surface->imageSurfaceEquiv = NULL;
1827 + surface->bitmapContextImage = NULL;
1828 + surface->cgLayer = NULL;
1829 + surface->ownsData = TRUE;
1833 @@ -3056,6 +3325,81 @@ cairo_quartz_surface_create_for_cg_conte
1837 + * cairo_quartz_cglayer_surface_create_similar
1838 + * @surface: The returned surface can be efficiently drawn into this
1839 + * destination surface (if tiling is not used)."
1840 + * @content: the content type of the surface
1841 + * @width: width of the surface, in pixels
1842 + * @height: height of the surface, in pixels
1844 + * Creates a Quartz surface backed by a CGLayer, if the given surface
1845 + * is a Quartz surface; the CGLayer is created to match the surface's
1846 + * Quartz context. Otherwise just calls cairo_surface_create_similar.
1847 + * The returned surface can be efficiently blitted to the given surface,
1848 + * but tiling and 'extend' modes other than NONE are not so efficient.
1850 + * Return value: the newly created surface.
1855 +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
1856 + cairo_content_t content,
1857 + unsigned int width,
1858 + unsigned int height)
1860 + cairo_quartz_surface_t *surf;
1863 + CGContextRef cgContext;
1865 + cgContext = cairo_quartz_surface_get_cg_context (surface);
1867 + return cairo_surface_create_similar (surface, content,
1871 + if (!_cairo_quartz_verify_surface_size(width, height))
1872 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
1874 + /* If we pass zero width or height into CGLayerCreateWithContext below,
1877 + if (width == 0 || height == 0) {
1878 + return (cairo_surface_t*)
1879 + _cairo_quartz_surface_create_internal (NULL, content,
1883 + layer = CGLayerCreateWithContext (cgContext,
1884 + CGSizeMake (width, height),
1887 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1889 + ctx = CGLayerGetContext (layer);
1890 + CGContextSetInterpolationQuality (ctx, kCGInterpolationNone);
1891 + /* Flip it when we draw into it, so that when we finally composite it
1892 + * to a flipped target, the directions match and Quartz will optimize
1893 + * the composition properly
1895 + CGContextTranslateCTM (ctx, 0, height);
1896 + CGContextScaleCTM (ctx, 1, -1);
1898 + CGContextRetain (ctx);
1899 + surf = _cairo_quartz_surface_create_internal (ctx, content,
1901 + if (surf->base.status) {
1902 + CGLayerRelease (layer);
1903 + // create_internal will have set an error
1904 + return (cairo_surface_t*) surf;
1906 + surf->cgLayer = layer;
1908 + return (cairo_surface_t *) surf;
1912 * cairo_quartz_surface_create
1913 * @format: format of pixels in the surface to create
1914 * @width: width of the surface, in pixels
1915 @@ -3075,13 +3419,93 @@ cairo_quartz_surface_create (cairo_forma
1917 unsigned int height)
1920 + unsigned char *data;
1922 + if (!_cairo_quartz_verify_surface_size(width, height))
1923 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
1925 + if (width == 0 || height == 0) {
1926 + return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
1930 + if (format == CAIRO_FORMAT_ARGB32 ||
1931 + format == CAIRO_FORMAT_RGB24)
1933 + stride = width * 4;
1934 + } else if (format == CAIRO_FORMAT_A8) {
1936 + } else if (format == CAIRO_FORMAT_A1) {
1937 + /* I don't think we can usefully support this, as defined by
1938 + * cairo_format_t -- these are 1-bit pixels stored in 32-bit
1941 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
1943 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
1946 + /* The Apple docs say that for best performance, the stride and the data
1947 + * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
1948 + * so we don't have to anything special on allocation.
1950 + stride = (stride + 15) & ~15;
1952 + data = _cairo_malloc_ab (height, stride);
1954 + return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1957 + /* zero the memory to match the image surface behaviour */
1958 + memset (data, 0, height * stride);
1960 + cairo_quartz_surface_t *surf;
1961 + surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
1962 + (data, format, width, height, stride);
1963 + if (surf->base.status) {
1965 + return (cairo_surface_t *) surf;
1968 + // We created this data, so we can delete it.
1969 + surf->ownsData = TRUE;
1971 + return (cairo_surface_t *) surf;
1975 + * cairo_quartz_surface_create_for_data
1976 + * @data: a pointer to a buffer supplied by the application in which
1977 + * to write contents. This pointer must be suitably aligned for any
1978 + * kind of variable, (for example, a pointer returned by malloc).
1979 + * @format: format of pixels in the surface to create
1980 + * @width: width of the surface, in pixels
1981 + * @height: height of the surface, in pixels
1983 + * Creates a Quartz surface backed by a CGBitmap. The surface is
1984 + * created using the Device RGB (or Device Gray, for A8) color space.
1985 + * All Cairo operations, including those that require software
1986 + * rendering, will succeed on this surface.
1988 + * Return value: the newly created surface.
1993 +cairo_quartz_surface_create_for_data (unsigned char *data,
1994 + cairo_format_t format,
1995 + unsigned int width,
1996 + unsigned int height,
1997 + unsigned int stride)
1999 cairo_quartz_surface_t *surf;
2001 CGColorSpaceRef cgColorspace;
2002 CGBitmapInfo bitinfo;
2005 + void *imageData = data;
2006 int bitsPerComponent;
2009 // verify width and height of surface
2010 if (!_cairo_quartz_verify_surface_size(width, height))
2011 @@ -3102,10 +3526,8 @@ cairo_quartz_surface_create (cairo_forma
2013 bitinfo |= kCGImageAlphaNoneSkipFirst;
2014 bitsPerComponent = 8;
2015 - stride = width * 4;
2016 } else if (format == CAIRO_FORMAT_A8) {
2017 cgColorspace = NULL;
2019 bitinfo = kCGImageAlphaOnly;
2020 bitsPerComponent = 8;
2021 } else if (format == CAIRO_FORMAT_A1) {
2022 @@ -3118,21 +3540,6 @@ cairo_quartz_surface_create (cairo_forma
2023 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2026 - /* The Apple docs say that for best performance, the stride and the data
2027 - * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2028 - * so we don't have to anything special on allocation.
2030 - stride = (stride + 15) & ~15;
2032 - imageData = _cairo_malloc_ab (height, stride);
2034 - CGColorSpaceRelease (cgColorspace);
2035 - return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2038 - /* zero the memory to match the image surface behaviour */
2039 - memset (imageData, 0, height * stride);
2041 cgc = CGBitmapContextCreate (imageData,
2044 @@ -3161,7 +3568,19 @@ cairo_quartz_surface_create (cairo_forma
2047 surf->imageData = imageData;
2048 - surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2050 + cairo_surface_t* tmpImageSurfaceEquiv =
2051 + cairo_image_surface_create_for_data (imageData, format,
2052 + width, height, stride);
2054 + if (cairo_surface_status (tmpImageSurfaceEquiv)) {
2055 + // Tried & failed to create an imageSurfaceEquiv!
2056 + cairo_surface_destroy (tmpImageSurfaceEquiv);
2057 + surf->imageSurfaceEquiv = NULL;
2059 + surf->imageSurfaceEquiv = tmpImageSurfaceEquiv;
2060 + surf->ownsData = FALSE;
2063 return (cairo_surface_t *) surf;
2065 @@ -3193,6 +3612,74 @@ _cairo_surface_is_quartz (const cairo_su
2066 return surface->backend == &cairo_quartz_surface_backend;
2070 +cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
2073 + cairo_surface_t *surface = cr->gstate->target;
2074 + cairo_clip_t *clip = &cr->gstate->clip;
2075 + cairo_status_t status;
2077 + cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
2079 + if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
2082 + if (!clip->path) {
2083 + if (clip->all_clipped) {
2084 + /* Save the state before we set an empty clip rect so that
2085 + * our previous clip will be restored */
2087 + /* _cairo_surface_clipper_set_clip doesn't deal with
2088 + * clip->all_clipped because drawing is normally discarded earlier */
2089 + CGRect empty = {{0,0}, {0,0}};
2090 + CGContextClipToRect (quartz->cgContext, empty);
2091 + CGContextSaveGState (quartz->cgContext);
2093 + return quartz->cgContext;
2096 + /* an empty clip is represented by NULL */
2100 + status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
2102 + /* Save the state after we set the clip so that it persists
2103 + * after we restore */
2104 + CGContextSaveGState (quartz->cgContext);
2106 + if (unlikely (status))
2109 + return quartz->cgContext;
2113 +cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
2115 + cairo_surface_t *surface = cr->gstate->target;
2117 + cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
2119 + if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
2122 + CGContextRestoreGState (quartz->cgContext);
2126 +cairo_quartz_surface_get_image (cairo_surface_t *surface)
2128 + cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
2129 + cairo_image_surface_t *image;
2131 + if (_cairo_quartz_get_image(quartz, &image))
2134 + return (cairo_surface_t *)image;
2140 --- a/src/cairo-quartz.h 2012-11-13 18:20:00.000000000 -0800
2141 +++ b/src/cairo-quartz.h 2012-11-13 18:06:56.000000000 -0800
2142 @@ -50,6 +50,19 @@ cairo_quartz_surface_create (cairo_forma
2143 unsigned int height);
2145 cairo_public cairo_surface_t *
2146 +cairo_quartz_surface_create_for_data (unsigned char *data,
2147 + cairo_format_t format,
2148 + unsigned int width,
2149 + unsigned int height,
2150 + unsigned int stride);
2152 +cairo_public cairo_surface_t *
2153 +cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
2154 + cairo_content_t content,
2155 + unsigned int width,
2156 + unsigned int height);
2158 +cairo_public cairo_surface_t *
2159 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2161 unsigned int height);
2162 @@ -57,6 +70,15 @@ cairo_quartz_surface_create_for_cg_conte
2163 cairo_public CGContextRef
2164 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
2166 +cairo_public CGContextRef
2167 +cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
2170 +cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
2172 +cairo_public cairo_surface_t *
2173 +cairo_quartz_surface_get_image (cairo_surface_t *surface);
2175 #if CAIRO_HAS_QUARTZ_FONT
2178 @@ -66,8 +88,10 @@ cairo_quartz_surface_get_cg_context (cai
2179 cairo_public cairo_font_face_t *
2180 cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
2183 cairo_public cairo_font_face_t *
2184 cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
2187 #endif /* CAIRO_HAS_QUARTZ_FONT */