Changes.
[cairo/gpu.git] / src / cairo-quartz-surface.c
blob892a156df172fdbb1b8bd55a56ba7c03b106bba2
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright � 2006, 2007 Mozilla Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is Mozilla Corporation.
33 * Contributor(s):
34 * Vladimir Vukicevic <vladimir@mozilla.com>
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
38 #include "cairoint.h"
40 #include "cairo-quartz-private.h"
42 #include <dlfcn.h>
44 #ifndef RTLD_DEFAULT
45 #define RTLD_DEFAULT ((void *) 0)
46 #endif
48 /* The 10.5 SDK includes a funky new definition of FloatToFixed which
49 * causes all sorts of breakage; so reset to old-style definition
51 #ifdef FloatToFixed
52 #undef FloatToFixed
53 #define FloatToFixed(a) ((Fixed)((float)(a) * fixed1))
54 #endif
56 #include <limits.h>
58 #undef QUARTZ_DEBUG
60 #ifdef QUARTZ_DEBUG
61 #define ND(_x) fprintf _x
62 #else
63 #define ND(_x) do {} while(0)
64 #endif
66 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
68 /* This method is private, but it exists. Its params are are exposed
69 * as args to the NS* method, but not as CG.
71 enum PrivateCGCompositeMode {
72 kPrivateCGCompositeClear = 0,
73 kPrivateCGCompositeCopy = 1,
74 kPrivateCGCompositeSourceOver = 2,
75 kPrivateCGCompositeSourceIn = 3,
76 kPrivateCGCompositeSourceOut = 4,
77 kPrivateCGCompositeSourceAtop = 5,
78 kPrivateCGCompositeDestinationOver = 6,
79 kPrivateCGCompositeDestinationIn = 7,
80 kPrivateCGCompositeDestinationOut = 8,
81 kPrivateCGCompositeDestinationAtop = 9,
82 kPrivateCGCompositeXOR = 10,
83 kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
84 kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
86 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
87 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
88 CG_EXTERN void CGContextResetCTM (CGContextRef);
89 CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
90 CG_EXTERN void CGContextResetClip (CGContextRef);
91 CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
93 /* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
94 * has all the stuff we care about, just some of it isn't exported in the SDK.
96 #ifndef kCGBitmapByteOrder32Host
97 #define USE_10_3_WORKAROUNDS
98 #define kCGBitmapAlphaInfoMask 0x1F
99 #define kCGBitmapByteOrderMask 0x7000
100 #define kCGBitmapByteOrder32Host 0
102 typedef uint32_t CGBitmapInfo;
104 /* public in 10.4, present in 10.3.9 */
105 CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
106 CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
107 #endif
109 /* Some of these are present in earlier versions of the OS than where
110 * they are public; others are not public at all (CGContextCopyPath,
111 * CGContextReplacePathWithClipPath, many of the getters, etc.)
113 static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
114 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
115 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
116 static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
117 static bool (*CGContextGetShouldAntialiasFontsPtr) (CGContextRef) = NULL;
118 static bool (*CGContextGetShouldSmoothFontsPtr) (CGContextRef) = NULL;
119 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
120 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
121 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
122 static void (*CGContextReplacePathWithClipPathPtr) (CGContextRef) = NULL;
124 static SInt32 _cairo_quartz_osx_version = 0x0;
126 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
129 * Utility functions
132 #ifdef QUARTZ_DEBUG
133 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
134 static void quartz_image_to_png (CGImageRef, char *dest);
135 #endif
137 static cairo_quartz_surface_t *
138 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
139 cairo_content_t content,
140 unsigned int width,
141 unsigned int height);
143 /* Load all extra symbols */
144 static void quartz_ensure_symbols(void)
146 if (_cairo_quartz_symbol_lookup_done)
147 return;
149 CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
150 CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
151 CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
152 CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
153 CGContextGetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldAntialiasFonts");
154 CGContextGetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldSmoothFonts");
155 CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
156 CGContextReplacePathWithClipPathPtr = dlsym(RTLD_DEFAULT, "CGContextReplacePathWithClipPath");
157 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
158 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
160 if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
161 // assume 10.4
162 _cairo_quartz_osx_version = 0x1040;
165 _cairo_quartz_symbol_lookup_done = TRUE;
168 CGImageRef
169 _cairo_quartz_create_cgimage (cairo_format_t format,
170 unsigned int width,
171 unsigned int height,
172 unsigned int stride,
173 void *data,
174 cairo_bool_t interpolate,
175 CGColorSpaceRef colorSpaceOverride,
176 CGDataProviderReleaseDataCallback releaseCallback,
177 void *releaseInfo)
179 CGImageRef image = NULL;
180 CGDataProviderRef dataProvider = NULL;
181 CGColorSpaceRef colorSpace = colorSpaceOverride;
182 CGBitmapInfo bitinfo;
183 int bitsPerComponent, bitsPerPixel;
185 switch (format) {
186 case CAIRO_FORMAT_ARGB32:
187 if (colorSpace == NULL)
188 colorSpace = CGColorSpaceCreateDeviceRGB();
189 bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
190 bitsPerComponent = 8;
191 bitsPerPixel = 32;
192 break;
194 case CAIRO_FORMAT_RGB24:
195 if (colorSpace == NULL)
196 colorSpace = CGColorSpaceCreateDeviceRGB();
197 bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
198 bitsPerComponent = 8;
199 bitsPerPixel = 32;
200 break;
202 /* XXX -- should use CGImageMaskCreate! */
203 case CAIRO_FORMAT_A8:
204 if (colorSpace == NULL)
205 colorSpace = CGColorSpaceCreateDeviceGray();
206 bitinfo = kCGImageAlphaNone;
207 bitsPerComponent = 8;
208 bitsPerPixel = 8;
209 break;
211 case CAIRO_FORMAT_A1:
212 default:
213 return NULL;
216 dataProvider = CGDataProviderCreateWithData (releaseInfo,
217 data,
218 height * stride,
219 releaseCallback);
221 if (!dataProvider) {
222 // manually release
223 if (releaseCallback)
224 releaseCallback (releaseInfo, data, height * stride);
225 goto FINISH;
228 image = CGImageCreate (width, height,
229 bitsPerComponent,
230 bitsPerPixel,
231 stride,
232 colorSpace,
233 bitinfo,
234 dataProvider,
235 NULL,
236 interpolate,
237 kCGRenderingIntentDefault);
239 FINISH:
241 CGDataProviderRelease (dataProvider);
243 if (colorSpace != colorSpaceOverride)
244 CGColorSpaceRelease (colorSpace);
246 return image;
249 static inline cairo_bool_t
250 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) {
251 if (cgc == NULL)
252 return FALSE;
254 if (CGContextGetTypePtr) {
255 /* 4 is the type value of a bitmap context */
256 if (CGContextGetTypePtr(cgc) == 4)
257 return TRUE;
258 return FALSE;
261 /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
262 return CGBitmapContextGetBitsPerPixel(cgc) != 0;
265 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
267 #define CG_MAX_HEIGHT SHRT_MAX
268 #define CG_MAX_WIDTH USHRT_MAX
270 /* is the desired size of the surface within bounds? */
271 cairo_bool_t
272 _cairo_quartz_verify_surface_size(int width, int height)
274 /* hmmm, allow width, height == 0 ? */
275 if (width < 0 || height < 0) {
276 return FALSE;
279 if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) {
280 return FALSE;
283 return TRUE;
287 * Cairo path -> Quartz path conversion helpers
290 typedef struct _quartz_stroke {
291 CGContextRef cgContext;
292 cairo_matrix_t *ctm_inverse;
293 } quartz_stroke_t;
295 /* cairo path -> execute in context */
296 static cairo_status_t
297 _cairo_path_to_quartz_context_move_to (void *closure,
298 const cairo_point_t *point)
300 //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
301 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
302 double x = _cairo_fixed_to_double (point->x);
303 double y = _cairo_fixed_to_double (point->y);
305 if (stroke->ctm_inverse)
306 cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
308 CGContextMoveToPoint (stroke->cgContext, x, y);
309 return CAIRO_STATUS_SUCCESS;
312 static cairo_status_t
313 _cairo_path_to_quartz_context_line_to (void *closure,
314 const cairo_point_t *point)
316 //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
317 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
318 double x = _cairo_fixed_to_double (point->x);
319 double y = _cairo_fixed_to_double (point->y);
321 if (stroke->ctm_inverse)
322 cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
324 if (CGContextIsPathEmpty (stroke->cgContext))
325 CGContextMoveToPoint (stroke->cgContext, x, y);
326 else
327 CGContextAddLineToPoint (stroke->cgContext, x, y);
328 return CAIRO_STATUS_SUCCESS;
331 static cairo_status_t
332 _cairo_path_to_quartz_context_curve_to (void *closure,
333 const cairo_point_t *p0,
334 const cairo_point_t *p1,
335 const cairo_point_t *p2)
337 //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n",
338 // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
339 // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
340 // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)));
341 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
342 double x0 = _cairo_fixed_to_double (p0->x);
343 double y0 = _cairo_fixed_to_double (p0->y);
344 double x1 = _cairo_fixed_to_double (p1->x);
345 double y1 = _cairo_fixed_to_double (p1->y);
346 double x2 = _cairo_fixed_to_double (p2->x);
347 double y2 = _cairo_fixed_to_double (p2->y);
349 if (stroke->ctm_inverse) {
350 cairo_matrix_transform_point (stroke->ctm_inverse, &x0, &y0);
351 cairo_matrix_transform_point (stroke->ctm_inverse, &x1, &y1);
352 cairo_matrix_transform_point (stroke->ctm_inverse, &x2, &y2);
355 CGContextAddCurveToPoint (stroke->cgContext,
356 x0, y0, x1, y1, x2, y2);
357 return CAIRO_STATUS_SUCCESS;
360 static cairo_status_t
361 _cairo_path_to_quartz_context_close_path (void *closure)
363 //ND((stderr, "closepath\n"));
364 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
365 CGContextClosePath (stroke->cgContext);
366 return CAIRO_STATUS_SUCCESS;
369 static cairo_status_t
370 _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
371 quartz_stroke_t *stroke)
373 return _cairo_path_fixed_interpret (path,
374 CAIRO_DIRECTION_FORWARD,
375 _cairo_path_to_quartz_context_move_to,
376 _cairo_path_to_quartz_context_line_to,
377 _cairo_path_to_quartz_context_curve_to,
378 _cairo_path_to_quartz_context_close_path,
379 stroke);
383 * Misc helpers/callbacks
386 static PrivateCGCompositeMode
387 _cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op)
389 switch (op) {
390 case CAIRO_OPERATOR_CLEAR:
391 return kPrivateCGCompositeClear;
392 case CAIRO_OPERATOR_SOURCE:
393 return kPrivateCGCompositeCopy;
394 case CAIRO_OPERATOR_OVER:
395 return kPrivateCGCompositeSourceOver;
396 case CAIRO_OPERATOR_IN:
397 /* XXX This doesn't match image output */
398 return kPrivateCGCompositeSourceIn;
399 case CAIRO_OPERATOR_OUT:
400 /* XXX This doesn't match image output */
401 return kPrivateCGCompositeSourceOut;
402 case CAIRO_OPERATOR_ATOP:
403 return kPrivateCGCompositeSourceAtop;
405 case CAIRO_OPERATOR_DEST:
406 /* XXX this is handled specially (noop)! */
407 return kPrivateCGCompositeCopy;
408 case CAIRO_OPERATOR_DEST_OVER:
409 return kPrivateCGCompositeDestinationOver;
410 case CAIRO_OPERATOR_DEST_IN:
411 /* XXX This doesn't match image output */
412 return kPrivateCGCompositeDestinationIn;
413 case CAIRO_OPERATOR_DEST_OUT:
414 return kPrivateCGCompositeDestinationOut;
415 case CAIRO_OPERATOR_DEST_ATOP:
416 /* XXX This doesn't match image output */
417 return kPrivateCGCompositeDestinationAtop;
419 case CAIRO_OPERATOR_XOR:
420 return kPrivateCGCompositeXOR; /* This will generate strange results */
421 case CAIRO_OPERATOR_ADD:
422 return kPrivateCGCompositePlusLighter;
423 case CAIRO_OPERATOR_SATURATE:
424 /* XXX This doesn't match image output for SATURATE; there's no equivalent */
425 return kPrivateCGCompositePlusDarker; /* ??? */
429 return kPrivateCGCompositeCopy;
432 static inline CGLineCap
433 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
435 switch (ccap) {
436 case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
437 case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
438 case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
441 return kCGLineCapButt;
444 static inline CGLineJoin
445 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
447 switch (cjoin) {
448 case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
449 case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
450 case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
453 return kCGLineJoinMiter;
456 static inline CGInterpolationQuality
457 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
459 switch (filter) {
460 case CAIRO_FILTER_NEAREST:
461 return kCGInterpolationNone;
463 case CAIRO_FILTER_FAST:
464 return kCGInterpolationLow;
466 case CAIRO_FILTER_BEST:
467 case CAIRO_FILTER_GOOD:
468 case CAIRO_FILTER_BILINEAR:
469 case CAIRO_FILTER_GAUSSIAN:
470 return kCGInterpolationDefault;
473 return kCGInterpolationDefault;
476 static inline void
477 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
478 CGAffineTransform *dst)
480 dst->a = src->xx;
481 dst->b = src->yx;
482 dst->c = src->xy;
483 dst->d = src->yy;
484 dst->tx = src->x0;
485 dst->ty = src->y0;
488 typedef struct {
489 bool isClipping;
490 CGGlyph *cg_glyphs;
491 CGSize *cg_advances;
492 size_t nglyphs;
493 CGAffineTransform textTransform;
494 CGFontRef font;
495 CGPoint origin;
496 } unbounded_show_glyphs_t;
498 typedef struct {
499 CGPathRef cgPath;
500 cairo_fill_rule_t fill_rule;
501 } unbounded_stroke_fill_t;
503 typedef struct {
504 CGImageRef mask;
505 CGAffineTransform maskTransform;
506 } unbounded_mask_t;
508 typedef enum {
509 UNBOUNDED_STROKE_FILL,
510 UNBOUNDED_SHOW_GLYPHS,
511 UNBOUNDED_MASK
512 } unbounded_op_t;
514 typedef struct {
515 unbounded_op_t op;
516 union {
517 unbounded_stroke_fill_t stroke_fill;
518 unbounded_show_glyphs_t show_glyphs;
519 unbounded_mask_t mask;
520 } u;
521 } unbounded_op_data_t;
523 static void
524 _cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
525 unbounded_op_data_t *op,
526 cairo_antialias_t antialias)
528 CGColorSpaceRef gray;
529 CGRect clipBox, clipBoxRound;
530 CGContextRef cgc;
531 CGImageRef maskImage;
533 if (!CGContextClipToMaskPtr)
534 return;
536 clipBox = CGContextGetClipBoundingBox (surface->cgContext);
537 clipBoxRound = CGRectIntegral (clipBox);
539 gray = CGColorSpaceCreateDeviceGray ();
540 cgc = CGBitmapContextCreate (NULL,
541 clipBoxRound.size.width,
542 clipBoxRound.size.height,
544 clipBoxRound.size.width,
545 gray,
546 kCGImageAlphaNone);
547 CGColorSpaceRelease (gray);
549 if (!cgc)
550 return;
552 /* We want to mask out whatever we just rendered, so we fill the
553 * surface with white, and then we'll render with black.
555 CGContextSetRGBFillColor (cgc, 1.0f, 1.0f, 1.0f, 1.0f);
556 CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
558 CGContextSetRGBFillColor (cgc, 0.0f, 0.0f, 0.0f, 1.0f);
559 CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
561 CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
563 /* We need to either render the path that was given to us, or the glyph op */
564 if (op->op == UNBOUNDED_STROKE_FILL) {
565 CGContextBeginPath (cgc);
566 CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
568 if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
569 CGContextFillPath (cgc);
570 else
571 CGContextEOFillPath (cgc);
572 } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
573 CGContextSetFont (cgc, op->u.show_glyphs.font);
574 CGContextSetFontSize (cgc, 1.0);
575 CGContextSetTextMatrix (cgc, op->u.show_glyphs.textTransform);
576 CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
578 if (op->u.show_glyphs.isClipping) {
579 /* Note that the comment in show_glyphs about kCGTextClip
580 * and the text transform still applies here; however, the
581 * cg_advances we have were already transformed, so we
582 * don't have to do anything. */
583 CGContextSetTextDrawingMode (cgc, kCGTextClip);
584 CGContextSaveGState (cgc);
587 CGContextShowGlyphsWithAdvances (cgc,
588 op->u.show_glyphs.cg_glyphs,
589 op->u.show_glyphs.cg_advances,
590 op->u.show_glyphs.nglyphs);
592 if (op->u.show_glyphs.isClipping) {
593 CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
594 CGContextRestoreGState (cgc);
596 } else if (op->op == UNBOUNDED_MASK) {
597 CGContextSaveGState (cgc);
598 CGContextConcatCTM (cgc, op->u.mask.maskTransform);
599 CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f,
600 CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)),
601 op->u.mask.mask);
602 CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
603 CGContextRestoreGState (cgc);
606 /* Also mask out the portion of the clipbox that we rounded out, if any */
607 if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
608 CGContextBeginPath (cgc);
609 CGContextAddRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
610 CGContextAddRect (cgc, CGRectMake (clipBoxRound.origin.x - clipBox.origin.x,
611 clipBoxRound.origin.y - clipBox.origin.y,
612 clipBox.size.width,
613 clipBox.size.height));
614 CGContextEOFillPath (cgc);
617 maskImage = CGBitmapContextCreateImage (cgc);
618 CGContextRelease (cgc);
620 if (!maskImage)
621 return;
623 /* Then render with the mask */
624 CGContextSaveGState (surface->cgContext);
626 CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
627 CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
628 CGImageRelease (maskImage);
630 /* Finally, clear out the entire clipping region through our mask */
631 CGContextClearRect (surface->cgContext, clipBoxRound);
633 CGContextRestoreGState (surface->cgContext);
637 * Source -> Quartz setup and finish functions
640 static void
641 ComputeGradientValue (void *info, const float *in, float *out)
643 double fdist = *in;
644 const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
645 unsigned int i;
647 /* Put fdist back in the 0.0..1.0 range if we're doing
648 * REPEAT/REFLECT
650 if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
651 fdist = fdist - floor(fdist);
652 } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
653 fdist = fmod(fabs(fdist), 2.0);
654 if (fdist > 1.0) {
655 fdist = 2.0 - fdist;
659 for (i = 0; i < grad->n_stops; i++) {
660 if (grad->stops[i].offset > fdist)
661 break;
664 if (i == 0 || i == grad->n_stops) {
665 if (i == grad->n_stops)
666 --i;
667 out[0] = grad->stops[i].color.red;
668 out[1] = grad->stops[i].color.green;
669 out[2] = grad->stops[i].color.blue;
670 out[3] = grad->stops[i].color.alpha;
671 } else {
672 float ax = grad->stops[i-1].offset;
673 float bx = grad->stops[i].offset - ax;
674 float bp = (fdist - ax)/bx;
675 float ap = 1.0 - bp;
677 out[0] =
678 grad->stops[i-1].color.red * ap +
679 grad->stops[i].color.red * bp;
680 out[1] =
681 grad->stops[i-1].color.green * ap +
682 grad->stops[i].color.green * bp;
683 out[2] =
684 grad->stops[i-1].color.blue * ap +
685 grad->stops[i].color.blue * bp;
686 out[3] =
687 grad->stops[i-1].color.alpha * ap +
688 grad->stops[i].color.alpha * bp;
692 static CGFunctionRef
693 CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
695 cairo_pattern_t *pat;
696 float input_value_range[2] = { 0.f, 1.f };
697 float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
698 CGFunctionCallbacks callbacks = {
699 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
702 if (_cairo_pattern_create_copy (&pat, &gpat->base))
703 /* quartz doesn't deal very well with malloc failing, so there's
704 * not much point in us trying either */
705 return NULL;
707 return CGFunctionCreate (pat,
709 input_value_range,
711 output_value_ranges,
712 &callbacks);
715 static CGFunctionRef
716 CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
717 const cairo_gradient_pattern_t *gpat,
718 CGPoint *start, CGPoint *end,
719 CGAffineTransform matrix)
721 cairo_pattern_t *pat;
722 float input_value_range[2];
723 float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
724 CGFunctionCallbacks callbacks = {
725 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
728 CGPoint mstart, mend;
730 double dx, dy;
731 int x_rep_start = 0, x_rep_end = 0;
732 int y_rep_start = 0, y_rep_end = 0;
734 int rep_start, rep_end;
736 // figure out how many times we'd need to repeat the gradient pattern
737 // to cover the whole (transformed) surface area
738 mstart = CGPointApplyAffineTransform (*start, matrix);
739 mend = CGPointApplyAffineTransform (*end, matrix);
741 dx = fabs (mend.x - mstart.x);
742 dy = fabs (mend.y - mstart.y);
744 if (dx > 1e-6) {
745 x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx);
746 x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx);
748 if (mend.x < mstart.x) {
749 int swap = x_rep_end;
750 x_rep_end = x_rep_start;
751 x_rep_start = swap;
755 if (dy > 1e-6) {
756 y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy);
757 y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy);
759 if (mend.y < mstart.y) {
760 int swap = y_rep_end;
761 y_rep_end = y_rep_start;
762 y_rep_start = swap;
766 rep_start = MAX(x_rep_start, y_rep_start);
767 rep_end = MAX(x_rep_end, y_rep_end);
769 // extend the line between start and end by rep_start times from the start
770 // and rep_end times from the end
772 dx = end->x - start->x;
773 dy = end->y - start->y;
775 start->x = start->x - dx * rep_start;
776 start->y = start->y - dy * rep_start;
778 end->x = end->x + dx * rep_end;
779 end->y = end->y + dy * rep_end;
781 // set the input range for the function -- the function knows how to
782 // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
783 input_value_range[0] = 0.0 - 1.0 * rep_start;
784 input_value_range[1] = 1.0 + 1.0 * rep_end;
786 if (_cairo_pattern_create_copy (&pat, &gpat->base))
787 /* quartz doesn't deal very well with malloc failing, so there's
788 * not much point in us trying either */
789 return NULL;
791 return CGFunctionCreate (pat,
793 input_value_range,
795 output_value_ranges,
796 &callbacks);
799 /* Obtain a CGImageRef from a #cairo_surface_t * */
801 static void
802 DataProviderReleaseCallback (void *info, const void *data, size_t size)
804 cairo_surface_t *surface = (cairo_surface_t *) info;
805 cairo_surface_destroy (surface);
808 static cairo_status_t
809 _cairo_surface_to_cgimage (cairo_surface_t *target,
810 cairo_surface_t *source,
811 CGImageRef *image_out)
813 cairo_status_t status = CAIRO_STATUS_SUCCESS;
814 cairo_surface_type_t stype = cairo_surface_get_type (source);
815 cairo_image_surface_t *isurf;
816 CGImageRef image;
817 void *image_extra;
819 if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
820 cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
821 *image_out = CGImageRetain (surface->image);
822 return CAIRO_STATUS_SUCCESS;
825 if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
826 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
827 if (IS_EMPTY(surface)) {
828 *image_out = NULL;
829 return CAIRO_STATUS_SUCCESS;
832 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
833 *image_out = CGBitmapContextCreateImage (surface->cgContext);
834 if (*image_out)
835 return CAIRO_STATUS_SUCCESS;
839 if (stype != CAIRO_SURFACE_TYPE_IMAGE) {
840 status = _cairo_surface_acquire_source_image (source, &isurf, &image_extra);
841 if (status)
842 return status;
843 } else {
844 isurf = (cairo_image_surface_t *) source;
847 if (isurf->width == 0 || isurf->height == 0) {
848 *image_out = NULL;
849 } else {
850 cairo_image_surface_t *isurf_snap = NULL;
851 isurf_snap = (cairo_image_surface_t*) _cairo_surface_snapshot ((cairo_surface_t*) isurf);
852 if (isurf_snap == NULL)
853 return CAIRO_STATUS_NO_MEMORY;
855 if (isurf_snap->base.type != CAIRO_SURFACE_TYPE_IMAGE)
856 return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
858 image = _cairo_quartz_create_cgimage (isurf_snap->format,
859 isurf_snap->width,
860 isurf_snap->height,
861 isurf_snap->stride,
862 isurf_snap->data,
863 TRUE,
864 NULL,
865 DataProviderReleaseCallback,
866 isurf_snap);
868 *image_out = image;
871 if ((cairo_surface_t*) isurf != source)
872 _cairo_surface_release_source_image (source, isurf, image_extra);
874 return status;
877 /* Generic #cairo_pattern_t -> CGPattern function */
879 typedef struct {
880 CGImageRef image;
881 CGRect imageBounds;
882 cairo_bool_t do_reflect;
883 } SurfacePatternDrawInfo;
885 static void
886 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
888 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
890 CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
891 CGContextScaleCTM (context, 1, -1);
893 CGContextDrawImage (context, info->imageBounds, info->image);
894 if (info->do_reflect) {
895 /* draw 3 more copies of the image, flipped.
896 * DrawImage draws the image according to the current Y-direction into the rectangle given
897 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
898 * of the base image position, and the Y axis is extending upwards.
901 /* Make the y axis extend downwards, and draw a flipped image below */
902 CGContextScaleCTM (context, 1, -1);
903 CGContextDrawImage (context, info->imageBounds, info->image);
905 /* Shift over to the right, and flip vertically (translation is 2x,
906 * since we'll be flipping and thus rendering the rectangle "backwards"
908 CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
909 CGContextScaleCTM (context, -1, 1);
910 CGContextDrawImage (context, info->imageBounds, info->image);
912 /* Then unflip the Y-axis again, and draw the image above the point. */
913 CGContextScaleCTM (context, 1, -1);
914 CGContextDrawImage (context, info->imageBounds, info->image);
918 static void
919 SurfacePatternReleaseInfoFunc (void *ainfo)
921 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
923 CGImageRelease (info->image);
924 free (info);
927 static cairo_int_status_t
928 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
929 const cairo_pattern_t *apattern,
930 CGPatternRef *cgpat)
932 cairo_surface_pattern_t *spattern;
933 cairo_surface_t *pat_surf;
934 cairo_rectangle_int_t extents;
936 CGImageRef image;
937 CGRect pbounds;
938 CGAffineTransform ptransform, stransform;
939 CGPatternCallbacks cb = { 0,
940 SurfacePatternDrawFunc,
941 SurfacePatternReleaseInfoFunc };
942 SurfacePatternDrawInfo *info;
943 float rw, rh;
944 cairo_status_t status;
946 cairo_matrix_t m;
948 /* SURFACE is the only type we'll handle here */
949 if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE)
950 return CAIRO_INT_STATUS_UNSUPPORTED;
952 spattern = (cairo_surface_pattern_t *) apattern;
953 pat_surf = spattern->surface;
955 status = _cairo_surface_get_extents (pat_surf, &extents);
956 if (status)
957 return status;
959 status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
960 if (status != CAIRO_STATUS_SUCCESS)
961 return CAIRO_INT_STATUS_UNSUPPORTED;
963 if (image == NULL)
964 return CAIRO_INT_STATUS_NOTHING_TO_DO;
966 info = malloc(sizeof(SurfacePatternDrawInfo));
967 if (!info)
968 return CAIRO_STATUS_NO_MEMORY;
970 /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
971 * that the data will stick around for this image when the printer gets to it.
972 * Otherwise, the underlying data store may disappear from under us!
974 * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
975 * since the Quartz surfaces have a higher chance of sticking around. If the
976 * source is a quartz image surface, then it's set up to retain a ref to the
977 * image surface that it's backed by.
979 info->image = image;
980 info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
981 info->do_reflect = FALSE;
983 pbounds.origin.x = 0;
984 pbounds.origin.y = 0;
986 if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
987 pbounds.size.width = 2.0 * extents.width;
988 pbounds.size.height = 2.0 * extents.height;
989 info->do_reflect = TRUE;
990 } else {
991 pbounds.size.width = extents.width;
992 pbounds.size.height = extents.height;
994 rw = pbounds.size.width;
995 rh = pbounds.size.height;
997 m = spattern->base.matrix;
998 cairo_matrix_invert(&m);
999 _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
1001 /* The pattern matrix is relative to the bottom left, again; the
1002 * incoming cairo pattern matrix is relative to the upper left.
1003 * So we take the pattern matrix and the original context matrix,
1004 * which gives us the correct base translation/y flip.
1006 ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM);
1008 #ifdef QUARTZ_DEBUG
1009 ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
1010 ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
1011 CGAffineTransform xform = CGContextGetCTM(dest->cgContext);
1012 ND((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
1013 #endif
1015 *cgpat = CGPatternCreate (info,
1016 pbounds,
1017 ptransform,
1018 rw, rh,
1019 kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
1020 TRUE,
1021 &cb);
1023 return CAIRO_STATUS_SUCCESS;
1026 typedef enum {
1027 DO_SOLID,
1028 DO_SHADING,
1029 DO_PATTERN,
1030 DO_IMAGE,
1031 DO_UNSUPPORTED,
1032 DO_NOTHING,
1033 DO_TILED_IMAGE
1034 } cairo_quartz_action_t;
1036 static cairo_quartz_action_t
1037 _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
1038 const cairo_pattern_t *source)
1040 CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
1041 CGAffineTransform ctm;
1042 double x0, y0, w, h;
1044 cairo_surface_t *fallback;
1045 cairo_t *fallback_cr;
1046 CGImageRef img;
1047 cairo_pattern_t *source_copy;
1049 cairo_status_t status;
1051 if (clipBox.size.width == 0.0f ||
1052 clipBox.size.height == 0.0f)
1053 return DO_NOTHING;
1055 // the clipBox is in userspace, so:
1056 ctm = CGContextGetCTM (surface->cgContext);
1057 ctm = CGAffineTransformInvert (ctm);
1058 clipBox = CGRectApplyAffineTransform (clipBox, ctm);
1060 // get the Y flip right -- the CTM will always have a Y flip in place
1061 clipBox.origin.y = surface->extents.height - (clipBox.origin.y + clipBox.size.height);
1063 x0 = floor(clipBox.origin.x);
1064 y0 = floor(clipBox.origin.y);
1065 w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
1066 h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
1068 /* Create a temporary the size of the clip surface, and position
1069 * it so that the device origin coincides with the original surface */
1070 fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
1071 cairo_surface_set_device_offset (fallback, -x0, -y0);
1073 /* Paint the source onto our temporary */
1074 fallback_cr = cairo_create (fallback);
1075 cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE);
1077 /* Use a copy of the pattern because it is const and could be allocated
1078 * on the stack */
1079 status = _cairo_pattern_create_copy (&source_copy, source);
1080 cairo_set_source (fallback_cr, source_copy);
1081 cairo_pattern_destroy (source_copy);
1083 cairo_paint (fallback_cr);
1084 cairo_destroy (fallback_cr);
1086 status = _cairo_surface_to_cgimage ((cairo_surface_t*) surface, fallback, &img);
1087 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
1088 return DO_NOTHING;
1089 if (status)
1090 return DO_UNSUPPORTED;
1092 surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
1093 surface->sourceImage = img;
1094 surface->sourceImageSurface = fallback;
1095 surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
1097 return DO_IMAGE;
1100 static cairo_quartz_action_t
1101 _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
1102 const cairo_linear_pattern_t *lpat)
1104 const cairo_pattern_t *abspat = &lpat->base.base;
1105 cairo_matrix_t mat;
1106 CGPoint start, end;
1107 CGFunctionRef gradFunc;
1108 CGColorSpaceRef rgb;
1109 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
1111 if (lpat->base.n_stops == 0) {
1112 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
1113 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
1114 return DO_SOLID;
1117 mat = abspat->matrix;
1118 cairo_matrix_invert (&mat);
1119 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
1121 rgb = CGColorSpaceCreateDeviceRGB();
1123 start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
1124 _cairo_fixed_to_double (lpat->p1.y));
1125 end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
1126 _cairo_fixed_to_double (lpat->p2.y));
1128 if (abspat->extend == CAIRO_EXTEND_NONE ||
1129 abspat->extend == CAIRO_EXTEND_PAD)
1131 gradFunc = CreateGradientFunction (&lpat->base);
1132 } else {
1133 gradFunc = CreateRepeatingGradientFunction (surface,
1134 &lpat->base,
1135 &start, &end, surface->sourceTransform);
1138 surface->sourceShading = CGShadingCreateAxial (rgb,
1139 start, end,
1140 gradFunc,
1141 extend, extend);
1143 CGColorSpaceRelease(rgb);
1144 CGFunctionRelease(gradFunc);
1146 return DO_SHADING;
1149 static cairo_quartz_action_t
1150 _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
1151 const cairo_radial_pattern_t *rpat)
1153 const cairo_pattern_t *abspat = &rpat->base.base;
1154 cairo_matrix_t mat;
1155 CGPoint start, end;
1156 CGFunctionRef gradFunc;
1157 CGColorSpaceRef rgb;
1158 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
1160 if (rpat->base.n_stops == 0) {
1161 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
1162 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
1163 return DO_SOLID;
1166 if (abspat->extend == CAIRO_EXTEND_REPEAT ||
1167 abspat->extend == CAIRO_EXTEND_REFLECT)
1169 /* I started trying to map these to Quartz, but it's much harder
1170 * then the linear case (I think it would involve doing multiple
1171 * Radial shadings). So, instead, let's just render an image
1172 * for pixman to draw the shading into, and use that.
1174 return _cairo_quartz_setup_fallback_source (surface, &rpat->base.base);
1177 mat = abspat->matrix;
1178 cairo_matrix_invert (&mat);
1179 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
1181 rgb = CGColorSpaceCreateDeviceRGB();
1183 start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x),
1184 _cairo_fixed_to_double (rpat->c1.y));
1185 end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
1186 _cairo_fixed_to_double (rpat->c2.y));
1188 gradFunc = CreateGradientFunction (&rpat->base);
1190 surface->sourceShading = CGShadingCreateRadial (rgb,
1191 start,
1192 _cairo_fixed_to_double (rpat->r1),
1193 end,
1194 _cairo_fixed_to_double (rpat->r2),
1195 gradFunc,
1196 extend, extend);
1198 CGColorSpaceRelease(rgb);
1199 CGFunctionRelease(gradFunc);
1201 return DO_SHADING;
1204 static cairo_quartz_action_t
1205 _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
1206 const cairo_pattern_t *source)
1208 assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
1210 surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
1211 CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
1213 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1214 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1216 CGContextSetRGBStrokeColor (surface->cgContext,
1217 solid->color.red,
1218 solid->color.green,
1219 solid->color.blue,
1220 solid->color.alpha);
1221 CGContextSetRGBFillColor (surface->cgContext,
1222 solid->color.red,
1223 solid->color.green,
1224 solid->color.blue,
1225 solid->color.alpha);
1227 return DO_SOLID;
1230 if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
1231 const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
1232 return _cairo_quartz_setup_linear_source (surface, lpat);
1236 if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
1237 const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
1238 return _cairo_quartz_setup_radial_source (surface, rpat);
1242 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1243 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1245 const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1246 cairo_surface_t *pat_surf = spat->surface;
1247 CGImageRef img;
1248 cairo_matrix_t m = spat->base.matrix;
1249 cairo_rectangle_int_t extents;
1250 cairo_status_t status;
1251 CGAffineTransform xform;
1252 CGRect srcRect;
1253 cairo_fixed_t fw, fh;
1255 status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
1256 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
1257 return DO_NOTHING;
1258 if (status)
1259 return DO_UNSUPPORTED;
1261 surface->sourceImage = img;
1263 cairo_matrix_invert(&m);
1264 _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
1266 status = _cairo_surface_get_extents (pat_surf, &extents);
1267 if (status)
1268 return DO_UNSUPPORTED;
1270 if (source->extend == CAIRO_EXTEND_NONE) {
1271 surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
1272 return DO_IMAGE;
1275 /* Quartz seems to tile images at pixel-aligned regions only -- this
1276 * leads to seams if the image doesn't end up scaling to fill the
1277 * space exactly. The CGPattern tiling approach doesn't have this
1278 * problem. Check if we're going to fill up the space (within some
1279 * epsilon), and if not, fall back to the CGPattern type.
1282 xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
1283 surface->sourceTransform);
1285 srcRect = CGRectMake (0, 0, extents.width, extents.height);
1286 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1288 fw = _cairo_fixed_from_double (srcRect.size.width);
1289 fh = _cairo_fixed_from_double (srcRect.size.height);
1291 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1292 (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1294 /* We're good to use DrawTiledImage, but ensure that
1295 * the math works out */
1297 srcRect.size.width = round(srcRect.size.width);
1298 srcRect.size.height = round(srcRect.size.height);
1300 xform = CGAffineTransformInvert (xform);
1302 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1304 surface->sourceImageRect = srcRect;
1306 return DO_TILED_IMAGE;
1309 /* Fall through to generic SURFACE case */
1312 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1313 float patternAlpha = 1.0f;
1314 CGColorSpaceRef patternSpace;
1315 CGPatternRef pattern;
1316 cairo_int_status_t status;
1318 status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
1319 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
1320 return DO_NOTHING;
1321 if (status)
1322 return DO_UNSUPPORTED;
1324 // Save before we change the pattern, colorspace, etc. so that
1325 // we can restore and make sure that quartz releases our
1326 // pattern (which may be stack allocated)
1327 CGContextSaveGState(surface->cgContext);
1329 patternSpace = CGColorSpaceCreatePattern(NULL);
1330 CGContextSetFillColorSpace (surface->cgContext, patternSpace);
1331 CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
1332 CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
1333 CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
1334 CGColorSpaceRelease (patternSpace);
1336 /* Quartz likes to munge the pattern phase (as yet unexplained
1337 * why); force it to 0,0 as we've already baked in the correct
1338 * pattern translation into the pattern matrix
1340 CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
1342 surface->sourcePattern = pattern;
1344 return DO_PATTERN;
1347 return DO_UNSUPPORTED;
1350 static void
1351 _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
1352 const cairo_pattern_t *source)
1354 CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
1356 if (surface->sourceImage) {
1357 CGImageRelease(surface->sourceImage);
1358 surface->sourceImage = NULL;
1360 cairo_surface_destroy(surface->sourceImageSurface);
1361 surface->sourceImageSurface = NULL;
1364 if (surface->sourceShading) {
1365 CGShadingRelease(surface->sourceShading);
1366 surface->sourceShading = NULL;
1369 if (surface->sourcePattern) {
1370 CGPatternRelease(surface->sourcePattern);
1371 // To tear down the pattern and colorspace
1372 CGContextRestoreGState(surface->cgContext);
1374 surface->sourcePattern = NULL;
1379 * get source/dest image implementation
1382 /* Read the image from the surface's front buffer */
1383 static cairo_int_status_t
1384 _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
1385 cairo_image_surface_t **image_out)
1387 unsigned char *imageData;
1388 cairo_image_surface_t *isurf;
1390 if (IS_EMPTY(surface)) {
1391 *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1392 return CAIRO_STATUS_SUCCESS;
1395 if (surface->imageSurfaceEquiv) {
1396 *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
1397 return CAIRO_STATUS_SUCCESS;
1400 if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) {
1401 unsigned int stride;
1402 unsigned int bitinfo;
1403 unsigned int bpc, bpp;
1404 CGColorSpaceRef colorspace;
1405 unsigned int color_comps;
1407 imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
1409 #ifdef USE_10_3_WORKAROUNDS
1410 bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
1411 #else
1412 bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1413 #endif
1414 stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1415 bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1416 bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
1418 // let's hope they don't add YUV under us
1419 colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1420 color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
1422 // XXX TODO: We can handle all of these by converting to
1423 // pixman masks, including non-native-endian masks
1424 if (bpc != 8)
1425 return CAIRO_INT_STATUS_UNSUPPORTED;
1427 if (bpp != 32 && bpp != 8)
1428 return CAIRO_INT_STATUS_UNSUPPORTED;
1430 if (color_comps != 3 && color_comps != 1)
1431 return CAIRO_INT_STATUS_UNSUPPORTED;
1433 if (bpp == 32 && color_comps == 3 &&
1434 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1435 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1437 isurf = (cairo_image_surface_t *)
1438 cairo_image_surface_create_for_data (imageData,
1439 CAIRO_FORMAT_ARGB32,
1440 surface->extents.width,
1441 surface->extents.height,
1442 stride);
1443 } else if (bpp == 32 && color_comps == 3 &&
1444 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1445 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1447 isurf = (cairo_image_surface_t *)
1448 cairo_image_surface_create_for_data (imageData,
1449 CAIRO_FORMAT_RGB24,
1450 surface->extents.width,
1451 surface->extents.height,
1452 stride);
1453 } else if (bpp == 8 && color_comps == 1)
1455 isurf = (cairo_image_surface_t *)
1456 cairo_image_surface_create_for_data (imageData,
1457 CAIRO_FORMAT_A8,
1458 surface->extents.width,
1459 surface->extents.height,
1460 stride);
1461 } else {
1462 return CAIRO_INT_STATUS_UNSUPPORTED;
1464 } else {
1465 return CAIRO_INT_STATUS_UNSUPPORTED;
1468 *image_out = isurf;
1469 return CAIRO_STATUS_SUCCESS;
1473 * Cairo surface backend implementations
1476 static cairo_status_t
1477 _cairo_quartz_surface_finish (void *abstract_surface)
1479 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1481 ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1483 if (IS_EMPTY(surface))
1484 return CAIRO_STATUS_SUCCESS;
1486 /* Restore our saved gstate that we use to reset clipping */
1487 CGContextRestoreGState (surface->cgContext);
1489 CGContextRelease (surface->cgContext);
1491 surface->cgContext = NULL;
1493 if (surface->imageSurfaceEquiv) {
1494 cairo_surface_destroy (surface->imageSurfaceEquiv);
1495 surface->imageSurfaceEquiv = NULL;
1498 if (surface->imageData) {
1499 free (surface->imageData);
1500 surface->imageData = NULL;
1503 return CAIRO_STATUS_SUCCESS;
1506 static cairo_status_t
1507 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1508 cairo_image_surface_t **image_out,
1509 void **image_extra)
1511 cairo_int_status_t status;
1512 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1514 //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1516 status = _cairo_quartz_get_image (surface, image_out);
1517 if (status)
1518 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1520 *image_extra = NULL;
1522 return CAIRO_STATUS_SUCCESS;
1525 static cairo_surface_t *
1526 _cairo_quartz_surface_snapshot (void *abstract_surface)
1528 cairo_int_status_t status;
1529 cairo_quartz_surface_t *surface = abstract_surface;
1530 cairo_image_surface_t *image;
1532 if (surface->imageSurfaceEquiv)
1533 return NULL;
1535 status = _cairo_quartz_get_image (surface, &image);
1536 if (unlikely (status))
1537 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
1539 return &image->base;
1542 static void
1543 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1544 cairo_image_surface_t *image,
1545 void *image_extra)
1547 cairo_surface_destroy ((cairo_surface_t *) image);
1551 static cairo_status_t
1552 _cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
1553 cairo_rectangle_int_t *interest_rect,
1554 cairo_image_surface_t **image_out,
1555 cairo_rectangle_int_t *image_rect,
1556 void **image_extra)
1558 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1559 cairo_int_status_t status;
1561 ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
1563 status = _cairo_quartz_get_image (surface, image_out);
1564 if (status)
1565 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1567 *image_rect = surface->extents;
1568 *image_extra = NULL;
1570 return CAIRO_STATUS_SUCCESS;
1573 static void
1574 _cairo_quartz_surface_release_dest_image (void *abstract_surface,
1575 cairo_rectangle_int_t *interest_rect,
1576 cairo_image_surface_t *image,
1577 cairo_rectangle_int_t *image_rect,
1578 void *image_extra)
1580 //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1582 //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
1584 cairo_surface_destroy ((cairo_surface_t *) image);
1587 static cairo_surface_t *
1588 _cairo_quartz_surface_create_similar (void *abstract_surface,
1589 cairo_content_t content,
1590 int width,
1591 int height)
1593 /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
1595 cairo_format_t format;
1597 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1598 format = CAIRO_FORMAT_ARGB32;
1599 else if (content == CAIRO_CONTENT_COLOR)
1600 format = CAIRO_FORMAT_RGB24;
1601 else if (content == CAIRO_CONTENT_ALPHA)
1602 format = CAIRO_FORMAT_A8;
1603 else
1604 return NULL;
1606 // verify width and height of surface
1607 if (!_cairo_quartz_verify_surface_size(width, height)) {
1608 return _cairo_surface_create_in_error (_cairo_error
1609 (CAIRO_STATUS_INVALID_SIZE));
1612 return cairo_quartz_surface_create (format, width, height);
1615 static cairo_status_t
1616 _cairo_quartz_surface_clone_similar (void *abstract_surface,
1617 cairo_surface_t *src,
1618 cairo_content_t content,
1619 int src_x,
1620 int src_y,
1621 int width,
1622 int height,
1623 int *clone_offset_x,
1624 int *clone_offset_y,
1625 cairo_surface_t **clone_out)
1627 cairo_quartz_surface_t *new_surface = NULL;
1628 cairo_format_t new_format;
1629 CGImageRef quartz_image = NULL;
1630 cairo_status_t status;
1632 *clone_out = NULL;
1634 // verify width and height of surface
1635 if (!_cairo_quartz_verify_surface_size(width, height)) {
1636 return CAIRO_INT_STATUS_UNSUPPORTED;
1639 if (width == 0 || height == 0) {
1640 *clone_out = (cairo_surface_t*)
1641 _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
1642 width, height);
1643 *clone_offset_x = 0;
1644 *clone_offset_y = 0;
1645 return CAIRO_STATUS_SUCCESS;
1648 if (src->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
1649 cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
1651 if (IS_EMPTY(qsurf)) {
1652 *clone_out = (cairo_surface_t*)
1653 _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
1654 qsurf->extents.width, qsurf->extents.height);
1655 *clone_offset_x = 0;
1656 *clone_offset_y = 0;
1657 return CAIRO_STATUS_SUCCESS;
1661 status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
1662 if (status)
1663 return CAIRO_INT_STATUS_UNSUPPORTED;
1665 new_format = CAIRO_FORMAT_ARGB32; /* assumed */
1666 if (_cairo_surface_is_image (src)) {
1667 new_format = ((cairo_image_surface_t *) src)->format;
1670 new_surface = (cairo_quartz_surface_t *)
1671 cairo_quartz_surface_create (new_format, width, height);
1673 if (quartz_image == NULL)
1674 goto FINISH;
1676 if (!new_surface || new_surface->base.status) {
1677 CGImageRelease (quartz_image);
1678 return CAIRO_INT_STATUS_UNSUPPORTED;
1681 CGContextSaveGState (new_surface->cgContext);
1683 CGContextSetCompositeOperation (new_surface->cgContext,
1684 kPrivateCGCompositeCopy);
1686 CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
1687 CGContextDrawImage (new_surface->cgContext,
1688 CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)),
1689 quartz_image);
1691 CGContextRestoreGState (new_surface->cgContext);
1693 CGImageRelease (quartz_image);
1695 FINISH:
1696 *clone_offset_x = src_x;
1697 *clone_offset_y = src_y;
1698 *clone_out = (cairo_surface_t*) new_surface;
1700 return CAIRO_STATUS_SUCCESS;
1703 static cairo_int_status_t
1704 _cairo_quartz_surface_get_extents (void *abstract_surface,
1705 cairo_rectangle_int_t *extents)
1707 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1709 *extents = surface->extents;
1711 return CAIRO_STATUS_SUCCESS;
1714 static cairo_int_status_t
1715 _cairo_quartz_surface_paint (void *abstract_surface,
1716 cairo_operator_t op,
1717 const cairo_pattern_t *source,
1718 cairo_rectangle_int_t *extents)
1720 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1721 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1722 cairo_quartz_action_t action;
1724 ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
1726 if (IS_EMPTY(surface))
1727 return CAIRO_STATUS_SUCCESS;
1729 if (op == CAIRO_OPERATOR_DEST)
1730 return CAIRO_STATUS_SUCCESS;
1732 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1734 action = _cairo_quartz_setup_source (surface, source);
1736 if (action == DO_SOLID || action == DO_PATTERN) {
1737 CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1738 surface->extents.y,
1739 surface->extents.width,
1740 surface->extents.height));
1741 } else if (action == DO_SHADING) {
1742 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1743 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1744 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1745 CGContextSaveGState (surface->cgContext);
1747 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1748 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1749 CGContextScaleCTM (surface->cgContext, 1, -1);
1751 if (action == DO_IMAGE)
1752 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1753 else
1754 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1755 CGContextRestoreGState (surface->cgContext);
1756 } else if (action != DO_NOTHING) {
1757 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1760 _cairo_quartz_teardown_source (surface, source);
1762 ND((stderr, "-- paint\n"));
1763 return rv;
1766 static cairo_int_status_t
1767 _cairo_quartz_surface_fill (void *abstract_surface,
1768 cairo_operator_t op,
1769 const cairo_pattern_t *source,
1770 cairo_path_fixed_t *path,
1771 cairo_fill_rule_t fill_rule,
1772 double tolerance,
1773 cairo_antialias_t antialias,
1774 cairo_rectangle_int_t *extents)
1776 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1777 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1778 cairo_quartz_action_t action;
1779 quartz_stroke_t stroke;
1780 cairo_box_t box;
1781 CGPathRef path_for_unbounded = NULL;
1783 ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
1785 if (IS_EMPTY(surface))
1786 return CAIRO_STATUS_SUCCESS;
1788 if (op == CAIRO_OPERATOR_DEST)
1789 return CAIRO_STATUS_SUCCESS;
1791 /* Check whether the path would be a no-op */
1792 /* XXX handle unbounded ops */
1793 if (_cairo_path_fixed_is_empty(path) ||
1794 (_cairo_path_fixed_is_box(path, &box) &&
1795 box.p1.x == box.p2.x &&
1796 box.p1.y == box.p2.y))
1798 return CAIRO_STATUS_SUCCESS;
1801 CGContextSaveGState (surface->cgContext);
1803 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1804 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1806 action = _cairo_quartz_setup_source (surface, source);
1808 CGContextBeginPath (surface->cgContext);
1810 stroke.cgContext = surface->cgContext;
1811 stroke.ctm_inverse = NULL;
1812 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1813 if (rv)
1814 goto BAIL;
1816 if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
1817 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1819 if (action == DO_SOLID || action == DO_PATTERN) {
1820 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1821 CGContextFillPath (surface->cgContext);
1822 else
1823 CGContextEOFillPath (surface->cgContext);
1824 } else if (action == DO_SHADING) {
1826 // we have to clip and then paint the shading; we can't fill
1827 // with the shading
1828 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1829 CGContextClip (surface->cgContext);
1830 else
1831 CGContextEOClip (surface->cgContext);
1833 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1834 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1835 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1836 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1837 CGContextClip (surface->cgContext);
1838 else
1839 CGContextEOClip (surface->cgContext);
1841 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1842 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1843 CGContextScaleCTM (surface->cgContext, 1, -1);
1845 if (action == DO_IMAGE)
1846 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1847 else
1848 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1849 } else if (action != DO_NOTHING) {
1850 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1853 BAIL:
1854 _cairo_quartz_teardown_source (surface, source);
1856 CGContextRestoreGState (surface->cgContext);
1858 if (path_for_unbounded) {
1859 unbounded_op_data_t ub;
1860 ub.op = UNBOUNDED_STROKE_FILL;
1861 ub.u.stroke_fill.cgPath = path_for_unbounded;
1862 ub.u.stroke_fill.fill_rule = fill_rule;
1864 _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
1865 CGPathRelease (path_for_unbounded);
1868 ND((stderr, "-- fill\n"));
1869 return rv;
1872 static cairo_int_status_t
1873 _cairo_quartz_surface_stroke (void *abstract_surface,
1874 cairo_operator_t op,
1875 const cairo_pattern_t *source,
1876 cairo_path_fixed_t *path,
1877 cairo_stroke_style_t *style,
1878 cairo_matrix_t *ctm,
1879 cairo_matrix_t *ctm_inverse,
1880 double tolerance,
1881 cairo_antialias_t antialias,
1882 cairo_rectangle_int_t *extents)
1884 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1885 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1886 cairo_quartz_action_t action;
1887 quartz_stroke_t stroke;
1888 CGAffineTransform origCTM, strokeTransform;
1889 CGPathRef path_for_unbounded = NULL;
1891 ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
1893 if (IS_EMPTY(surface))
1894 return CAIRO_STATUS_SUCCESS;
1896 if (op == CAIRO_OPERATOR_DEST)
1897 return CAIRO_STATUS_SUCCESS;
1899 CGContextSaveGState (surface->cgContext);
1901 // Turning antialiasing off used to cause misrendering with
1902 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1903 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1904 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1905 CGContextSetLineWidth (surface->cgContext, style->line_width);
1906 CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1907 CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1908 CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1910 origCTM = CGContextGetCTM (surface->cgContext);
1912 _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1913 CGContextConcatCTM (surface->cgContext, strokeTransform);
1915 if (style->dash && style->num_dashes) {
1916 #define STATIC_DASH 32
1917 float sdash[STATIC_DASH];
1918 float *fdash = sdash;
1919 unsigned int max_dashes = style->num_dashes;
1920 unsigned int k;
1922 if (style->num_dashes%2)
1923 max_dashes *= 2;
1924 if (max_dashes > STATIC_DASH)
1925 fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
1926 if (fdash == NULL)
1927 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1929 for (k = 0; k < max_dashes; k++)
1930 fdash[k] = (float) style->dash[k % style->num_dashes];
1932 CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
1933 if (fdash != sdash)
1934 free (fdash);
1937 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1939 action = _cairo_quartz_setup_source (surface, source);
1941 CGContextBeginPath (surface->cgContext);
1943 stroke.cgContext = surface->cgContext;
1944 stroke.ctm_inverse = ctm_inverse;
1945 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1946 if (rv)
1947 goto BAIL;
1949 if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
1950 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1952 if (action == DO_SOLID || action == DO_PATTERN) {
1953 CGContextStrokePath (surface->cgContext);
1954 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1955 CGContextReplacePathWithStrokedPath (surface->cgContext);
1956 CGContextClip (surface->cgContext);
1958 CGContextSetCTM (surface->cgContext, origCTM);
1960 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1961 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1962 CGContextScaleCTM (surface->cgContext, 1, -1);
1964 if (action == DO_IMAGE)
1965 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1966 else
1967 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1968 } else if (action == DO_SHADING) {
1969 CGContextReplacePathWithStrokedPath (surface->cgContext);
1970 CGContextClip (surface->cgContext);
1972 CGContextSetCTM (surface->cgContext, origCTM);
1974 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1975 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1976 } else if (action != DO_NOTHING) {
1977 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1980 BAIL:
1981 _cairo_quartz_teardown_source (surface, source);
1983 CGContextRestoreGState (surface->cgContext);
1985 if (path_for_unbounded) {
1986 unbounded_op_data_t ub;
1988 CGContextBeginPath (surface->cgContext);
1990 /* recreate the stroke state, but without the CTM, as it's been already baked
1991 * into the path.
1993 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1994 CGContextSetLineWidth (surface->cgContext, style->line_width);
1995 CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1996 CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1997 CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1999 CGContextAddPath (surface->cgContext, path_for_unbounded);
2000 CGPathRelease (path_for_unbounded);
2002 CGContextReplacePathWithStrokedPath (surface->cgContext);
2003 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
2005 ub.op = UNBOUNDED_STROKE_FILL;
2006 ub.u.stroke_fill.cgPath = path_for_unbounded;
2007 ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
2009 _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
2011 CGPathRelease (path_for_unbounded);
2014 ND((stderr, "-- stroke\n"));
2015 return rv;
2018 #if CAIRO_HAS_QUARTZ_FONT
2019 static cairo_int_status_t
2020 _cairo_quartz_surface_show_glyphs (void *abstract_surface,
2021 cairo_operator_t op,
2022 const cairo_pattern_t *source,
2023 cairo_glyph_t *glyphs,
2024 int num_glyphs,
2025 cairo_scaled_font_t *scaled_font,
2026 int *remaining_glyphs,
2027 cairo_rectangle_int_t *extents)
2029 CGAffineTransform textTransform, ctm;
2030 #define STATIC_BUF_SIZE 64
2031 CGGlyph glyphs_static[STATIC_BUF_SIZE];
2032 CGSize cg_advances_static[STATIC_BUF_SIZE];
2033 CGGlyph *cg_glyphs = &glyphs_static[0];
2034 CGSize *cg_advances = &cg_advances_static[0];
2036 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
2037 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
2038 cairo_quartz_action_t action;
2039 float xprev, yprev;
2040 int i;
2041 CGFontRef cgfref = NULL;
2043 cairo_bool_t isClipping = FALSE;
2044 cairo_bool_t didForceFontSmoothing = FALSE;
2046 if (IS_EMPTY(surface))
2047 return CAIRO_STATUS_SUCCESS;
2049 if (num_glyphs <= 0)
2050 return CAIRO_STATUS_SUCCESS;
2052 if (op == CAIRO_OPERATOR_DEST)
2053 return CAIRO_STATUS_SUCCESS;
2055 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
2056 return CAIRO_INT_STATUS_UNSUPPORTED;
2058 CGContextSaveGState (surface->cgContext);
2060 action = _cairo_quartz_setup_source (surface, source);
2061 if (action == DO_SOLID || action == DO_PATTERN) {
2062 CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
2063 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
2064 CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
2065 isClipping = TRUE;
2066 } else {
2067 if (action != DO_NOTHING)
2068 rv = CAIRO_INT_STATUS_UNSUPPORTED;
2069 goto BAIL;
2072 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
2074 /* this doesn't addref */
2075 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
2076 CGContextSetFont (surface->cgContext, cgfref);
2077 CGContextSetFontSize (surface->cgContext, 1.0);
2079 switch (scaled_font->options.antialias) {
2080 case CAIRO_ANTIALIAS_SUBPIXEL:
2081 CGContextSetShouldAntialias (surface->cgContext, TRUE);
2082 CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
2083 if (CGContextSetAllowsFontSmoothingPtr &&
2084 !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
2086 didForceFontSmoothing = TRUE;
2087 CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
2089 break;
2090 case CAIRO_ANTIALIAS_NONE:
2091 CGContextSetShouldAntialias (surface->cgContext, FALSE);
2092 break;
2093 case CAIRO_ANTIALIAS_GRAY:
2094 CGContextSetShouldAntialias (surface->cgContext, TRUE);
2095 CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
2096 break;
2097 case CAIRO_ANTIALIAS_DEFAULT:
2098 /* Don't do anything */
2099 break;
2102 if (num_glyphs > STATIC_BUF_SIZE) {
2103 cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
2104 if (cg_glyphs == NULL) {
2105 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2106 goto BAIL;
2109 cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
2110 if (cg_advances == NULL) {
2111 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2112 goto BAIL;
2116 textTransform = CGAffineTransformMake (scaled_font->font_matrix.xx,
2117 scaled_font->font_matrix.yx,
2118 scaled_font->font_matrix.xy,
2119 scaled_font->font_matrix.yy,
2120 0., 0.);
2121 textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
2122 textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
2123 -scaled_font->ctm.yx,
2124 -scaled_font->ctm.xy,
2125 scaled_font->ctm.yy,
2126 0., 0.),
2127 textTransform);
2129 CGContextSetTextMatrix (surface->cgContext, textTransform);
2131 /* Convert our glyph positions to glyph advances. We need n-1 advances,
2132 * since the advance at index 0 is applied after glyph 0. */
2133 xprev = glyphs[0].x;
2134 yprev = glyphs[0].y;
2136 cg_glyphs[0] = glyphs[0].index;
2138 for (i = 1; i < num_glyphs; i++) {
2139 float xf = glyphs[i].x;
2140 float yf = glyphs[i].y;
2141 cg_glyphs[i] = glyphs[i].index;
2142 cg_advances[i-1].width = xf - xprev;
2143 cg_advances[i-1].height = yf - yprev;
2144 xprev = xf;
2145 yprev = yf;
2148 if (_cairo_quartz_osx_version >= 0x1050 && isClipping) {
2149 /* If we're clipping, OSX 10.5 (at least as of 10.5.2) has a
2150 * bug (apple bug ID #5834794) where the glyph
2151 * advances/positions are not transformed by the text matrix
2152 * if kCGTextClip is being used. So, we pre-transform here.
2153 * 10.4 does not have this problem (as of 10.4.11).
2155 for (i = 0; i < num_glyphs - 1; i++)
2156 cg_advances[i] = CGSizeApplyAffineTransform(cg_advances[i], textTransform);
2159 #if 0
2160 for (i = 0; i < num_glyphs; i++) {
2161 ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
2163 #endif
2165 /* Translate to the first glyph's position before drawing */
2166 ctm = CGContextGetCTM (surface->cgContext);
2167 CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
2169 CGContextShowGlyphsWithAdvances (surface->cgContext,
2170 cg_glyphs,
2171 cg_advances,
2172 num_glyphs);
2174 CGContextSetCTM (surface->cgContext, ctm);
2176 if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
2177 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
2178 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
2179 CGContextScaleCTM (surface->cgContext, 1, -1);
2181 if (action == DO_IMAGE)
2182 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
2183 else
2184 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
2185 } else if (action == DO_SHADING) {
2186 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
2187 CGContextDrawShading (surface->cgContext, surface->sourceShading);
2190 BAIL:
2191 _cairo_quartz_teardown_source (surface, source);
2193 if (didForceFontSmoothing)
2194 CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
2196 CGContextRestoreGState (surface->cgContext);
2198 if (rv == CAIRO_STATUS_SUCCESS &&
2199 cgfref &&
2200 !_cairo_operator_bounded_by_mask (op))
2202 unbounded_op_data_t ub;
2203 ub.op = UNBOUNDED_SHOW_GLYPHS;
2205 ub.u.show_glyphs.isClipping = isClipping;
2206 ub.u.show_glyphs.cg_glyphs = cg_glyphs;
2207 ub.u.show_glyphs.cg_advances = cg_advances;
2208 ub.u.show_glyphs.nglyphs = num_glyphs;
2209 ub.u.show_glyphs.textTransform = textTransform;
2210 ub.u.show_glyphs.font = cgfref;
2211 ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
2213 _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
2217 if (cg_advances != &cg_advances_static[0]) {
2218 free (cg_advances);
2221 if (cg_glyphs != &glyphs_static[0]) {
2222 free (cg_glyphs);
2225 return rv;
2227 #endif /* CAIRO_HAS_QUARTZ_FONT */
2229 static cairo_int_status_t
2230 _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
2231 cairo_operator_t op,
2232 const cairo_pattern_t *source,
2233 const cairo_surface_pattern_t *mask,
2234 cairo_rectangle_int_t *extents)
2236 cairo_rectangle_int_t mask_extents;
2237 CGRect rect;
2238 CGImageRef img;
2239 cairo_surface_t *pat_surf = mask->surface;
2240 cairo_status_t status = CAIRO_STATUS_SUCCESS;
2241 CGAffineTransform ctm, mask_matrix;
2243 status = _cairo_surface_get_extents (pat_surf, &mask_extents);
2244 if (status)
2245 return status;
2247 // everything would be masked out, so do nothing
2248 if (mask_extents.width == 0 || mask_extents.height == 0)
2249 return CAIRO_STATUS_SUCCESS;
2251 status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
2252 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
2253 return CAIRO_STATUS_SUCCESS;
2254 if (status)
2255 return status;
2257 rect = CGRectMake (0.0f, 0.0f, mask_extents.width, mask_extents.height);
2259 CGContextSaveGState (surface->cgContext);
2261 /* ClipToMask is essentially drawing an image, so we need to flip the CTM
2262 * to get the image to appear oriented the right way */
2263 ctm = CGContextGetCTM (surface->cgContext);
2265 _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
2266 CGContextConcatCTM (surface->cgContext, CGAffineTransformInvert(mask_matrix));
2267 CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height);
2268 CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f);
2270 CGContextClipToMaskPtr (surface->cgContext, rect, img);
2272 CGContextSetCTM (surface->cgContext, ctm);
2274 status = _cairo_quartz_surface_paint (surface, op, source, extents);
2276 CGContextRestoreGState (surface->cgContext);
2278 if (!_cairo_operator_bounded_by_mask (op)) {
2279 unbounded_op_data_t ub;
2280 ub.op = UNBOUNDED_MASK;
2281 ub.u.mask.mask = img;
2282 ub.u.mask.maskTransform = CGAffineTransformInvert(mask_matrix);
2283 _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
2286 CGImageRelease (img);
2288 return status;
2291 /* This is somewhat less than ideal, but it gets the job done;
2292 * it would be better to avoid calling back into cairo. This
2293 * creates a temporary surface to use as the mask.
2295 static cairo_int_status_t
2296 _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
2297 cairo_operator_t op,
2298 const cairo_pattern_t *source,
2299 const cairo_pattern_t *mask,
2300 cairo_rectangle_int_t *extents)
2302 int width = surface->extents.width - surface->extents.x;
2303 int height = surface->extents.height - surface->extents.y;
2305 cairo_surface_t *gradient_surf = NULL;
2306 cairo_t *gradient_surf_cr = NULL;
2308 cairo_surface_pattern_t surface_pattern;
2309 cairo_pattern_t *mask_copy;
2310 cairo_int_status_t status;
2312 /* Render the gradient to a surface */
2313 gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
2314 width,
2315 height);
2316 gradient_surf_cr = cairo_create(gradient_surf);
2318 /* make a copy of the pattern because because cairo_set_source doesn't take
2319 * a 'const cairo_pattern_t *' */
2320 _cairo_pattern_create_copy (&mask_copy, mask);
2321 cairo_set_source (gradient_surf_cr, mask_copy);
2322 cairo_pattern_destroy (mask_copy);
2324 cairo_set_operator (gradient_surf_cr, CAIRO_OPERATOR_SOURCE);
2325 cairo_paint (gradient_surf_cr);
2326 status = cairo_status (gradient_surf_cr);
2327 cairo_destroy (gradient_surf_cr);
2329 if (status)
2330 goto BAIL;
2332 _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf);
2334 status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, extents);
2336 _cairo_pattern_fini (&surface_pattern.base);
2338 BAIL:
2339 if (gradient_surf)
2340 cairo_surface_destroy (gradient_surf);
2342 return status;
2345 static cairo_int_status_t
2346 _cairo_quartz_surface_mask (void *abstract_surface,
2347 cairo_operator_t op,
2348 const cairo_pattern_t *source,
2349 const cairo_pattern_t *mask,
2350 cairo_rectangle_int_t *extents)
2352 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
2353 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
2355 ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
2357 if (IS_EMPTY(surface))
2358 return CAIRO_STATUS_SUCCESS;
2360 if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
2361 /* This is easy; we just need to paint with the alpha. */
2362 cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
2364 CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
2365 rv = _cairo_quartz_surface_paint (surface, op, source, extents);
2366 CGContextSetAlpha (surface->cgContext, 1.0);
2368 return rv;
2371 /* If we have CGContextClipToMask, we can do more complex masks */
2372 if (CGContextClipToMaskPtr) {
2373 /* For these, we can skip creating a temporary surface, since we already have one */
2374 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
2375 return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, extents);
2377 return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, extents);
2380 /* So, CGContextClipToMask is not present in 10.3.9, so we're
2381 * doomed; if we have imageData, we can do fallback, otherwise
2382 * just pretend success.
2384 if (surface->imageData)
2385 return CAIRO_INT_STATUS_UNSUPPORTED;
2387 return CAIRO_STATUS_SUCCESS;
2390 static cairo_int_status_t
2391 _cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
2392 cairo_path_fixed_t *path,
2393 cairo_fill_rule_t fill_rule,
2394 double tolerance,
2395 cairo_antialias_t antialias)
2397 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
2398 quartz_stroke_t stroke;
2399 cairo_status_t status;
2401 ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2403 if (IS_EMPTY(surface))
2404 return CAIRO_STATUS_SUCCESS;
2406 if (path == NULL) {
2407 /* If we're being asked to reset the clip, we can only do it
2408 * by restoring the gstate to our previous saved one, and
2409 * saving it again.
2411 * Note that this assumes that ALL quartz surface creation
2412 * functions will do a SaveGState first; we do this in create_internal.
2414 CGContextRestoreGState (surface->cgContext);
2415 CGContextSaveGState (surface->cgContext);
2416 } else {
2417 CGContextBeginPath (surface->cgContext);
2418 stroke.cgContext = surface->cgContext;
2419 stroke.ctm_inverse = NULL;
2421 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2423 /* path must not be empty. */
2424 CGContextMoveToPoint (surface->cgContext, 0, 0);
2425 status = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
2426 if (status)
2427 return status;
2429 if (fill_rule == CAIRO_FILL_RULE_WINDING)
2430 CGContextClip (surface->cgContext);
2431 else
2432 CGContextEOClip (surface->cgContext);
2435 ND((stderr, "-- intersect_clip_path\n"));
2437 return CAIRO_STATUS_SUCCESS;
2440 // XXXtodo implement show_page; need to figure out how to handle begin/end
2442 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2443 CAIRO_SURFACE_TYPE_QUARTZ,
2444 _cairo_quartz_surface_create_similar,
2445 _cairo_quartz_surface_finish,
2446 _cairo_quartz_surface_acquire_source_image,
2447 _cairo_quartz_surface_release_source_image,
2448 _cairo_quartz_surface_acquire_dest_image,
2449 _cairo_quartz_surface_release_dest_image,
2450 _cairo_quartz_surface_clone_similar,
2451 NULL, /* composite */
2452 NULL, /* fill_rectangles */
2453 NULL, /* composite_trapezoids */
2454 NULL, /* create_span_renderer */
2455 NULL, /* check_span_renderer */
2456 NULL, /* copy_page */
2457 NULL, /* show_page */
2458 NULL, /* set_clip_region */
2459 _cairo_quartz_surface_intersect_clip_path,
2460 _cairo_quartz_surface_get_extents,
2461 NULL, /* old_show_glyphs */
2462 NULL, /* get_font_options */
2463 NULL, /* flush */
2464 NULL, /* mark_dirty_rectangle */
2465 NULL, /* scaled_font_fini */
2466 NULL, /* scaled_glyph_fini */
2468 _cairo_quartz_surface_paint,
2469 _cairo_quartz_surface_mask,
2470 _cairo_quartz_surface_stroke,
2471 _cairo_quartz_surface_fill,
2472 #if CAIRO_HAS_QUARTZ_FONT
2473 _cairo_quartz_surface_show_glyphs,
2474 #else
2475 NULL, /* show_glyphs */
2476 #endif
2478 _cairo_quartz_surface_snapshot,
2479 NULL, /* is_similar */
2480 NULL, /* reset */
2481 NULL /* fill_stroke */
2484 cairo_quartz_surface_t *
2485 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2486 cairo_content_t content,
2487 unsigned int width,
2488 unsigned int height)
2490 cairo_quartz_surface_t *surface;
2492 quartz_ensure_symbols();
2494 /* Init the base surface */
2495 surface = malloc(sizeof(cairo_quartz_surface_t));
2496 if (surface == NULL)
2497 return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2499 memset(surface, 0, sizeof(cairo_quartz_surface_t));
2501 _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend,
2502 content);
2504 /* Save our extents */
2505 surface->extents.x = surface->extents.y = 0;
2506 surface->extents.width = width;
2507 surface->extents.height = height;
2509 if (IS_EMPTY(surface)) {
2510 surface->cgContext = NULL;
2511 surface->cgContextBaseCTM = CGAffineTransformIdentity;
2512 surface->imageData = NULL;
2513 return surface;
2516 /* Save so we can always get back to a known-good CGContext -- this is
2517 * required for proper behaviour of intersect_clip_path(NULL)
2519 CGContextSaveGState (cgContext);
2521 surface->cgContext = cgContext;
2522 surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2524 surface->imageData = NULL;
2525 surface->imageSurfaceEquiv = NULL;
2527 return surface;
2531 * cairo_quartz_surface_create_for_cg_context
2532 * @cgContext: the existing CGContext for which to create the surface
2533 * @width: width of the surface, in pixels
2534 * @height: height of the surface, in pixels
2536 * Creates a Quartz surface that wraps the given CGContext. The
2537 * CGContext is assumed to be in the standard Cairo coordinate space
2538 * (that is, with the origin at the upper left and the Y axis
2539 * increasing downward). If the CGContext is in the Quartz coordinate
2540 * space (with the origin at the bottom left), then it should be
2541 * flipped before this function is called. The flip can be accomplished
2542 * using a translate and a scale; for example:
2544 * <informalexample><programlisting>
2545 * CGContextTranslateCTM (cgContext, 0.0, height);
2546 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2547 * </programlisting></informalexample>
2549 * All Cairo operations are implemented in terms of Quartz operations,
2550 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2552 * Return value: the newly created Cairo surface.
2554 * Since: 1.4
2557 cairo_surface_t *
2558 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2559 unsigned int width,
2560 unsigned int height)
2562 cairo_quartz_surface_t *surf;
2564 CGContextRetain (cgContext);
2566 surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2567 width, height);
2568 if (surf->base.status) {
2569 CGContextRelease (cgContext);
2570 // create_internal will have set an error
2571 return (cairo_surface_t*) surf;
2574 return (cairo_surface_t *) surf;
2578 * cairo_quartz_surface_create
2579 * @format: format of pixels in the surface to create
2580 * @width: width of the surface, in pixels
2581 * @height: height of the surface, in pixels
2583 * Creates a Quartz surface backed by a CGBitmap. The surface is
2584 * created using the Device RGB (or Device Gray, for A8) color space.
2585 * All Cairo operations, including those that require software
2586 * rendering, will succeed on this surface.
2588 * Return value: the newly created surface.
2590 * Since: 1.4
2592 cairo_surface_t *
2593 cairo_quartz_surface_create (cairo_format_t format,
2594 unsigned int width,
2595 unsigned int height)
2597 cairo_quartz_surface_t *surf;
2598 CGContextRef cgc;
2599 CGColorSpaceRef cgColorspace;
2600 CGBitmapInfo bitinfo;
2601 void *imageData;
2602 int stride;
2603 int bitsPerComponent;
2605 // verify width and height of surface
2606 if (!_cairo_quartz_verify_surface_size(width, height))
2607 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2609 if (width == 0 || height == 0) {
2610 return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2611 width, height);
2614 if (format == CAIRO_FORMAT_ARGB32 ||
2615 format == CAIRO_FORMAT_RGB24)
2617 cgColorspace = CGColorSpaceCreateDeviceRGB();
2618 bitinfo = kCGBitmapByteOrder32Host;
2619 if (format == CAIRO_FORMAT_ARGB32)
2620 bitinfo |= kCGImageAlphaPremultipliedFirst;
2621 else
2622 bitinfo |= kCGImageAlphaNoneSkipFirst;
2623 bitsPerComponent = 8;
2625 /* The Apple docs say that for best performance, the stride and the data
2626 * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2627 * so we don't have to anything special on allocation.
2629 stride = width * 4;
2630 stride += (16 - (stride & 15)) & 15;
2631 } else if (format == CAIRO_FORMAT_A8) {
2632 cgColorspace = CGColorSpaceCreateDeviceGray();
2633 if (width % 4 == 0)
2634 stride = width;
2635 else
2636 stride = (width & ~3) + 4;
2637 bitinfo = kCGImageAlphaNone;
2638 bitsPerComponent = 8;
2639 } else if (format == CAIRO_FORMAT_A1) {
2640 /* I don't think we can usefully support this, as defined by
2641 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2642 * quantities.
2644 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2645 } else {
2646 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2649 imageData = _cairo_malloc_ab (height, stride);
2650 if (!imageData) {
2651 CGColorSpaceRelease (cgColorspace);
2652 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2655 /* zero the memory to match the image surface behaviour */
2656 memset (imageData, 0, height * stride);
2658 cgc = CGBitmapContextCreate (imageData,
2659 width,
2660 height,
2661 bitsPerComponent,
2662 stride,
2663 cgColorspace,
2664 bitinfo);
2665 CGColorSpaceRelease (cgColorspace);
2667 if (!cgc) {
2668 free (imageData);
2669 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2672 /* flip the Y axis */
2673 CGContextTranslateCTM (cgc, 0.0, height);
2674 CGContextScaleCTM (cgc, 1.0, -1.0);
2676 surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2677 width, height);
2678 if (surf->base.status) {
2679 CGContextRelease (cgc);
2680 free (imageData);
2681 // create_internal will have set an error
2682 return (cairo_surface_t*) surf;
2685 surf->imageData = imageData;
2686 surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2688 return (cairo_surface_t *) surf;
2692 * cairo_quartz_surface_get_cg_context
2693 * @surface: the Cairo Quartz surface
2695 * Returns the CGContextRef that the given Quartz surface is backed
2696 * by.
2698 * Return value: the CGContextRef for the given surface.
2700 * Since: 1.4
2702 CGContextRef
2703 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2705 cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
2707 if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
2708 return NULL;
2710 return quartz->cgContext;
2714 /* Debug stuff */
2716 #ifdef QUARTZ_DEBUG
2718 #include <Movies.h>
2720 void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
2722 Handle dataRef = NULL;
2723 OSType dataRefType;
2724 CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII);
2726 GraphicsExportComponent grex = 0;
2727 unsigned long sizeWritten;
2729 ComponentResult result;
2731 // create the data reference
2732 result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
2733 0, &dataRef, &dataRefType);
2735 if (NULL != dataRef && noErr == result) {
2736 // get the PNG exporter
2737 result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG,
2738 &grex);
2740 if (grex) {
2741 // tell the exporter where to find its source image
2742 result = GraphicsExportSetInputCGImage(grex, inImageRef);
2744 if (noErr == result) {
2745 // tell the exporter where to save the exporter image
2746 result = GraphicsExportSetOutputDataReference(grex, dataRef,
2747 dataRefType);
2749 if (noErr == result) {
2750 // write the PNG file
2751 result = GraphicsExportDoExport(grex, &sizeWritten);
2755 // remember to close the component
2756 CloseComponent(grex);
2759 // remember to dispose of the data reference handle
2760 DisposeHandle(dataRef);
2764 void
2765 quartz_image_to_png (CGImageRef imgref, char *dest)
2767 static int sctr = 0;
2768 char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2770 if (dest == NULL) {
2771 fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2772 sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2773 sctr++;
2774 dest = sptr;
2777 ExportCGImageToPNGFile(imgref, dest);
2780 void
2781 quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2783 static int sctr = 0;
2784 char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2786 if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2787 fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2788 return;
2791 if (dest == NULL) {
2792 fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2793 sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2794 sctr++;
2795 dest = sptr;
2798 CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2799 if (imgref == NULL) {
2800 fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2801 return;
2804 ExportCGImageToPNGFile(imgref, dest);
2806 CGImageRelease(imgref);
2809 #endif /* QUARTZ_DEBUG */