added MouseWheel event support for Silverlight 3.0
[moon.git] / cairo / src / cairo-quartz-surface.c
blob41fde0be150fa47600b107463c348290b6ed1f4c
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 #include "cairoint.h"
39 #include "cairo-quartz-private.h"
41 #include <dlfcn.h>
43 /* The 10.5 SDK includes a funky new definition of FloatToFixed which
44 * causes all sorts of breakage; so reset to old-style definition
46 #ifdef FloatToFixed
47 #undef FloatToFixed
48 #define FloatToFixed(a) ((Fixed)((float)(a) * fixed1))
49 #endif
51 #include <limits.h>
53 #undef QUARTZ_DEBUG
55 #ifdef QUARTZ_DEBUG
56 #define ND(_x) fprintf _x
57 #else
58 #define ND(_x) do {} while(0)
59 #endif
61 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
63 /* This method is private, but it exists. Its params are are exposed
64 * as args to the NS* method, but not as CG.
66 enum PrivateCGCompositeMode {
67 kPrivateCGCompositeClear = 0,
68 kPrivateCGCompositeCopy = 1,
69 kPrivateCGCompositeSourceOver = 2,
70 kPrivateCGCompositeSourceIn = 3,
71 kPrivateCGCompositeSourceOut = 4,
72 kPrivateCGCompositeSourceAtop = 5,
73 kPrivateCGCompositeDestinationOver = 6,
74 kPrivateCGCompositeDestinationIn = 7,
75 kPrivateCGCompositeDestinationOut = 8,
76 kPrivateCGCompositeDestinationAtop = 9,
77 kPrivateCGCompositeXOR = 10,
78 kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
79 kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
81 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
82 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
83 CG_EXTERN void CGContextResetCTM (CGContextRef);
84 CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
85 CG_EXTERN void CGContextResetClip (CGContextRef);
86 CG_EXTERN CGSize CGContextGetPatternPhase (CGContextRef);
88 /* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
89 * has all the stuff we care about, just some of it isn't exported in the SDK.
91 #ifndef kCGBitmapByteOrder32Host
92 #define USE_10_3_WORKAROUNDS
93 #define kCGBitmapAlphaInfoMask 0x1F
94 #define kCGBitmapByteOrderMask 0x7000
95 #define kCGBitmapByteOrder32Host 0
97 typedef uint32_t CGBitmapInfo;
99 /* public in 10.4, present in 10.3.9 */
100 CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
101 CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
102 #endif
104 /* Some of these are present in earlier versions of the OS than where
105 * they are public; others are not public at all (CGContextCopyPath,
106 * CGContextReplacePathWithClipPath, many of the getters, etc.)
108 static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
109 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
110 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
111 static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
112 static bool (*CGContextGetShouldAntialiasFontsPtr) (CGContextRef) = NULL;
113 static bool (*CGContextGetShouldSmoothFontsPtr) (CGContextRef) = NULL;
114 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
115 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
116 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
117 static void (*CGContextReplacePathWithClipPathPtr) (CGContextRef) = NULL;
119 static SInt32 _cairo_quartz_osx_version = 0x0;
121 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
124 * Utility functions
127 #ifdef QUARTZ_DEBUG
128 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
129 static void quartz_image_to_png (CGImageRef, char *dest);
130 #endif
132 static cairo_quartz_surface_t *
133 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
134 cairo_content_t content,
135 unsigned int width,
136 unsigned int height);
138 /* Load all extra symbols */
139 static void quartz_ensure_symbols(void)
141 if (_cairo_quartz_symbol_lookup_done)
142 return;
144 CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
145 CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
146 CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
147 CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
148 CGContextGetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldAntialiasFonts");
149 CGContextGetShouldSmoothFontsPtr = dlsym(RTLD_DEFAULT, "CGContextGetShouldSmoothFonts");
150 CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
151 CGContextReplacePathWithClipPathPtr = dlsym(RTLD_DEFAULT, "CGContextReplacePathWithClipPath");
152 CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
153 CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
155 if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
156 // assume 10.4
157 _cairo_quartz_osx_version = 0x1040;
160 _cairo_quartz_symbol_lookup_done = TRUE;
163 CGImageRef
164 _cairo_quartz_create_cgimage (cairo_format_t format,
165 unsigned int width,
166 unsigned int height,
167 unsigned int stride,
168 void *data,
169 cairo_bool_t interpolate,
170 CGColorSpaceRef colorSpaceOverride,
171 CGDataProviderReleaseDataCallback releaseCallback,
172 void *releaseInfo)
174 CGImageRef image = NULL;
175 CGDataProviderRef dataProvider = NULL;
176 CGColorSpaceRef colorSpace = colorSpaceOverride;
177 CGBitmapInfo bitinfo;
178 int bitsPerComponent, bitsPerPixel;
180 switch (format) {
181 case CAIRO_FORMAT_ARGB32:
182 if (colorSpace == NULL)
183 colorSpace = CGColorSpaceCreateDeviceRGB();
184 bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
185 bitsPerComponent = 8;
186 bitsPerPixel = 32;
187 break;
189 case CAIRO_FORMAT_RGB24:
190 if (colorSpace == NULL)
191 colorSpace = CGColorSpaceCreateDeviceRGB();
192 bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
193 bitsPerComponent = 8;
194 bitsPerPixel = 32;
195 break;
197 /* XXX -- should use CGImageMaskCreate! */
198 case CAIRO_FORMAT_A8:
199 if (colorSpace == NULL)
200 colorSpace = CGColorSpaceCreateDeviceGray();
201 bitinfo = kCGImageAlphaNone;
202 bitsPerComponent = 8;
203 bitsPerPixel = 8;
204 break;
206 case CAIRO_FORMAT_A1:
207 default:
208 return NULL;
211 dataProvider = CGDataProviderCreateWithData (releaseInfo,
212 data,
213 height * stride,
214 releaseCallback);
216 if (!dataProvider) {
217 // manually release
218 if (releaseCallback)
219 releaseCallback (releaseInfo, data, height * stride);
220 goto FINISH;
223 image = CGImageCreate (width, height,
224 bitsPerComponent,
225 bitsPerPixel,
226 stride,
227 colorSpace,
228 bitinfo,
229 dataProvider,
230 NULL,
231 interpolate,
232 kCGRenderingIntentDefault);
234 FINISH:
236 CGDataProviderRelease (dataProvider);
238 if (colorSpace != colorSpaceOverride)
239 CGColorSpaceRelease (colorSpace);
241 return image;
244 static inline cairo_bool_t
245 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) {
246 if (cgc == NULL)
247 return FALSE;
249 if (CGContextGetTypePtr) {
250 /* 4 is the type value of a bitmap context */
251 if (CGContextGetTypePtr(cgc) == 4)
252 return TRUE;
253 return FALSE;
256 /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
257 return CGBitmapContextGetBitsPerPixel(cgc) != 0;
260 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
262 #define CG_MAX_HEIGHT SHRT_MAX
263 #define CG_MAX_WIDTH USHRT_MAX
265 /* is the desired size of the surface within bounds? */
266 cairo_bool_t
267 _cairo_quartz_verify_surface_size(int width, int height)
269 /* hmmm, allow width, height == 0 ? */
270 if (width < 0 || height < 0) {
271 return FALSE;
274 if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) {
275 return FALSE;
278 return TRUE;
282 * Cairo path -> Quartz path conversion helpers
285 typedef struct _quartz_stroke {
286 CGContextRef cgContext;
287 cairo_matrix_t *ctm_inverse;
288 } quartz_stroke_t;
290 /* cairo path -> execute in context */
291 static cairo_status_t
292 _cairo_path_to_quartz_context_move_to (void *closure, cairo_point_t *point)
294 //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
295 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
296 double x = _cairo_fixed_to_double (point->x);
297 double y = _cairo_fixed_to_double (point->y);
299 if (stroke->ctm_inverse)
300 cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
302 CGContextMoveToPoint (stroke->cgContext, x, y);
303 return CAIRO_STATUS_SUCCESS;
306 static cairo_status_t
307 _cairo_path_to_quartz_context_line_to (void *closure, cairo_point_t *point)
309 //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
310 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
311 double x = _cairo_fixed_to_double (point->x);
312 double y = _cairo_fixed_to_double (point->y);
314 if (stroke->ctm_inverse)
315 cairo_matrix_transform_point (stroke->ctm_inverse, &x, &y);
317 if (CGContextIsPathEmpty (stroke->cgContext))
318 CGContextMoveToPoint (stroke->cgContext, x, y);
319 else
320 CGContextAddLineToPoint (stroke->cgContext, x, y);
321 return CAIRO_STATUS_SUCCESS;
324 static cairo_status_t
325 _cairo_path_to_quartz_context_curve_to (void *closure, cairo_point_t *p0, cairo_point_t *p1, cairo_point_t *p2)
327 //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n",
328 // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
329 // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
330 // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)));
331 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
332 double x0 = _cairo_fixed_to_double (p0->x);
333 double y0 = _cairo_fixed_to_double (p0->y);
334 double x1 = _cairo_fixed_to_double (p1->x);
335 double y1 = _cairo_fixed_to_double (p1->y);
336 double x2 = _cairo_fixed_to_double (p2->x);
337 double y2 = _cairo_fixed_to_double (p2->y);
339 if (stroke->ctm_inverse) {
340 cairo_matrix_transform_point (stroke->ctm_inverse, &x0, &y0);
341 cairo_matrix_transform_point (stroke->ctm_inverse, &x1, &y1);
342 cairo_matrix_transform_point (stroke->ctm_inverse, &x2, &y2);
345 CGContextAddCurveToPoint (stroke->cgContext,
346 x0, y0, x1, y1, x2, y2);
347 return CAIRO_STATUS_SUCCESS;
350 static cairo_status_t
351 _cairo_path_to_quartz_context_close_path (void *closure)
353 //ND((stderr, "closepath\n"));
354 quartz_stroke_t *stroke = (quartz_stroke_t *)closure;
355 CGContextClosePath (stroke->cgContext);
356 return CAIRO_STATUS_SUCCESS;
359 static cairo_status_t
360 _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
361 quartz_stroke_t *stroke)
363 return _cairo_path_fixed_interpret (path,
364 CAIRO_DIRECTION_FORWARD,
365 _cairo_path_to_quartz_context_move_to,
366 _cairo_path_to_quartz_context_line_to,
367 _cairo_path_to_quartz_context_curve_to,
368 _cairo_path_to_quartz_context_close_path,
369 stroke);
373 * Misc helpers/callbacks
376 static PrivateCGCompositeMode
377 _cairo_quartz_cairo_operator_to_quartz (cairo_operator_t op)
379 switch (op) {
380 case CAIRO_OPERATOR_CLEAR:
381 return kPrivateCGCompositeClear;
382 case CAIRO_OPERATOR_SOURCE:
383 return kPrivateCGCompositeCopy;
384 case CAIRO_OPERATOR_OVER:
385 return kPrivateCGCompositeSourceOver;
386 case CAIRO_OPERATOR_IN:
387 /* XXX This doesn't match image output */
388 return kPrivateCGCompositeSourceIn;
389 case CAIRO_OPERATOR_OUT:
390 /* XXX This doesn't match image output */
391 return kPrivateCGCompositeSourceOut;
392 case CAIRO_OPERATOR_ATOP:
393 return kPrivateCGCompositeSourceAtop;
395 case CAIRO_OPERATOR_DEST:
396 /* XXX this is handled specially (noop)! */
397 return kPrivateCGCompositeCopy;
398 case CAIRO_OPERATOR_DEST_OVER:
399 return kPrivateCGCompositeDestinationOver;
400 case CAIRO_OPERATOR_DEST_IN:
401 /* XXX This doesn't match image output */
402 return kPrivateCGCompositeDestinationIn;
403 case CAIRO_OPERATOR_DEST_OUT:
404 return kPrivateCGCompositeDestinationOut;
405 case CAIRO_OPERATOR_DEST_ATOP:
406 /* XXX This doesn't match image output */
407 return kPrivateCGCompositeDestinationAtop;
409 case CAIRO_OPERATOR_XOR:
410 return kPrivateCGCompositeXOR; /* This will generate strange results */
411 case CAIRO_OPERATOR_ADD:
412 return kPrivateCGCompositePlusLighter;
413 case CAIRO_OPERATOR_SATURATE:
414 /* XXX This doesn't match image output for SATURATE; there's no equivalent */
415 return kPrivateCGCompositePlusDarker; /* ??? */
419 return kPrivateCGCompositeCopy;
422 static inline CGLineCap
423 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
425 switch (ccap) {
426 case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
427 case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
428 case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
431 return kCGLineCapButt;
434 static inline CGLineJoin
435 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
437 switch (cjoin) {
438 case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
439 case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
440 case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
443 return kCGLineJoinMiter;
446 static inline CGInterpolationQuality
447 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
449 switch (filter) {
450 case CAIRO_FILTER_NEAREST:
451 return kCGInterpolationNone;
453 case CAIRO_FILTER_FAST:
454 return kCGInterpolationLow;
456 case CAIRO_FILTER_BEST:
457 case CAIRO_FILTER_GOOD:
458 case CAIRO_FILTER_BILINEAR:
459 case CAIRO_FILTER_GAUSSIAN:
460 return kCGInterpolationDefault;
463 return kCGInterpolationDefault;
466 static inline void
467 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
468 CGAffineTransform *dst)
470 dst->a = src->xx;
471 dst->b = src->yx;
472 dst->c = src->xy;
473 dst->d = src->yy;
474 dst->tx = src->x0;
475 dst->ty = src->y0;
478 typedef struct {
479 bool isClipping;
480 CGGlyph *cg_glyphs;
481 CGSize *cg_advances;
482 size_t nglyphs;
483 CGAffineTransform textTransform;
484 CGFontRef font;
485 CGPoint origin;
486 } unbounded_show_glyphs_t;
488 typedef struct {
489 CGPathRef cgPath;
490 cairo_fill_rule_t fill_rule;
491 } unbounded_stroke_fill_t;
493 typedef struct {
494 CGImageRef mask;
495 CGAffineTransform maskTransform;
496 } unbounded_mask_t;
498 typedef enum {
499 UNBOUNDED_STROKE_FILL,
500 UNBOUNDED_SHOW_GLYPHS,
501 UNBOUNDED_MASK
502 } unbounded_op_t;
504 typedef struct {
505 unbounded_op_t op;
506 union {
507 unbounded_stroke_fill_t stroke_fill;
508 unbounded_show_glyphs_t show_glyphs;
509 unbounded_mask_t mask;
510 } u;
511 } unbounded_op_data_t;
513 static void
514 _cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
515 unbounded_op_data_t *op,
516 cairo_antialias_t antialias)
518 CGColorSpaceRef gray;
519 CGRect clipBox, clipBoxRound;
520 CGContextRef cgc;
521 CGImageRef maskImage;
523 if (!CGContextClipToMaskPtr)
524 return;
526 clipBox = CGContextGetClipBoundingBox (surface->cgContext);
527 clipBoxRound = CGRectIntegral (clipBox);
529 gray = CGColorSpaceCreateDeviceGray ();
530 cgc = CGBitmapContextCreate (NULL,
531 clipBoxRound.size.width,
532 clipBoxRound.size.height,
534 clipBoxRound.size.width,
535 gray,
536 kCGImageAlphaNone);
537 CGColorSpaceRelease (gray);
539 if (!cgc)
540 return;
542 /* We want to mask out whatever we just rendered, so we fill the
543 * surface with white, and then we'll render with black.
545 CGContextSetRGBFillColor (cgc, 1.0f, 1.0f, 1.0f, 1.0f);
546 CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
548 CGContextSetRGBFillColor (cgc, 0.0f, 0.0f, 0.0f, 1.0f);
549 CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
551 CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
553 /* We need to either render the path that was given to us, or the glyph op */
554 if (op->op == UNBOUNDED_STROKE_FILL) {
555 CGContextBeginPath (cgc);
556 CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
558 if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
559 CGContextFillPath (cgc);
560 else
561 CGContextEOFillPath (cgc);
562 } else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
563 CGContextSetFont (cgc, op->u.show_glyphs.font);
564 CGContextSetFontSize (cgc, 1.0);
565 CGContextSetTextMatrix (cgc, op->u.show_glyphs.textTransform);
566 CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
568 if (op->u.show_glyphs.isClipping) {
569 /* Note that the comment in show_glyphs about kCGTextClip
570 * and the text transform still applies here; however, the
571 * cg_advances we have were already transformed, so we
572 * don't have to do anything. */
573 CGContextSetTextDrawingMode (cgc, kCGTextClip);
574 CGContextSaveGState (cgc);
577 CGContextShowGlyphsWithAdvances (cgc,
578 op->u.show_glyphs.cg_glyphs,
579 op->u.show_glyphs.cg_advances,
580 op->u.show_glyphs.nglyphs);
582 if (op->u.show_glyphs.isClipping) {
583 CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
584 CGContextRestoreGState (cgc);
586 } else if (op->op == UNBOUNDED_MASK) {
587 CGContextSaveGState (cgc);
588 CGContextConcatCTM (cgc, op->u.mask.maskTransform);
589 CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f,
590 CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)),
591 op->u.mask.mask);
592 CGContextFillRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
593 CGContextRestoreGState (cgc);
596 /* Also mask out the portion of the clipbox that we rounded out, if any */
597 if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
598 CGContextBeginPath (cgc);
599 CGContextAddRect (cgc, CGRectMake (0.0, 0.0, clipBoxRound.size.width, clipBoxRound.size.height));
600 CGContextAddRect (cgc, CGRectMake (clipBoxRound.origin.x - clipBox.origin.x,
601 clipBoxRound.origin.y - clipBox.origin.y,
602 clipBox.size.width,
603 clipBox.size.height));
604 CGContextEOFillPath (cgc);
607 maskImage = CGBitmapContextCreateImage (cgc);
608 CGContextRelease (cgc);
610 if (!maskImage)
611 return;
613 /* Then render with the mask */
614 CGContextSaveGState (surface->cgContext);
616 CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
617 CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
618 CGImageRelease (maskImage);
620 /* Finally, clear out the entire clipping region through our mask */
621 CGContextClearRect (surface->cgContext, clipBoxRound);
623 CGContextRestoreGState (surface->cgContext);
627 * Source -> Quartz setup and finish functions
630 static void
631 ComputeGradientValue (void *info, const float *in, float *out)
633 double fdist = *in;
634 cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
635 unsigned int i;
637 /* Put fdist back in the 0.0..1.0 range if we're doing
638 * REPEAT/REFLECT
640 if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
641 fdist = fdist - floor(fdist);
642 } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
643 fdist = fmod(fabs(fdist), 2.0);
644 if (fdist > 1.0) {
645 fdist = 2.0 - fdist;
649 for (i = 0; i < grad->n_stops; i++) {
650 if (grad->stops[i].offset > fdist)
651 break;
654 if (i == 0 || i == grad->n_stops) {
655 if (i == grad->n_stops)
656 --i;
657 out[0] = grad->stops[i].color.red;
658 out[1] = grad->stops[i].color.green;
659 out[2] = grad->stops[i].color.blue;
660 out[3] = grad->stops[i].color.alpha;
661 } else {
662 float ax = grad->stops[i-1].offset;
663 float bx = grad->stops[i].offset - ax;
664 float bp = (fdist - ax)/bx;
665 float ap = 1.0 - bp;
667 out[0] =
668 grad->stops[i-1].color.red * ap +
669 grad->stops[i].color.red * bp;
670 out[1] =
671 grad->stops[i-1].color.green * ap +
672 grad->stops[i].color.green * bp;
673 out[2] =
674 grad->stops[i-1].color.blue * ap +
675 grad->stops[i].color.blue * bp;
676 out[3] =
677 grad->stops[i-1].color.alpha * ap +
678 grad->stops[i].color.alpha * bp;
682 static CGFunctionRef
683 CreateGradientFunction (cairo_gradient_pattern_t *gpat)
685 float input_value_range[2] = { 0.f, 1.f };
686 float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
687 CGFunctionCallbacks callbacks = {
688 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
691 return CGFunctionCreate (gpat,
693 input_value_range,
695 output_value_ranges,
696 &callbacks);
699 static CGFunctionRef
700 CreateRepeatingGradientFunction (cairo_quartz_surface_t *surface,
701 cairo_gradient_pattern_t *gpat,
702 CGPoint *start, CGPoint *end,
703 CGAffineTransform matrix)
705 float input_value_range[2];
706 float output_value_ranges[8] = { 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f };
707 CGFunctionCallbacks callbacks = {
708 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
711 CGPoint mstart, mend;
713 double dx, dy;
714 int x_rep_start = 0, x_rep_end = 0;
715 int y_rep_start = 0, y_rep_end = 0;
717 int rep_start, rep_end;
719 // figure out how many times we'd need to repeat the gradient pattern
720 // to cover the whole (transformed) surface area
721 mstart = CGPointApplyAffineTransform (*start, matrix);
722 mend = CGPointApplyAffineTransform (*end, matrix);
724 dx = fabs (mend.x - mstart.x);
725 dy = fabs (mend.y - mstart.y);
727 if (dx > 1e-6) {
728 x_rep_start = (int) ceil(MIN(mstart.x, mend.x) / dx);
729 x_rep_end = (int) ceil((surface->extents.width - MAX(mstart.x, mend.x)) / dx);
731 if (mend.x < mstart.x) {
732 int swap = x_rep_end;
733 x_rep_end = x_rep_start;
734 x_rep_start = swap;
738 if (dy > 1e-6) {
739 y_rep_start = (int) ceil(MIN(mstart.y, mend.y) / dy);
740 y_rep_end = (int) ceil((surface->extents.width - MAX(mstart.y, mend.y)) / dy);
742 if (mend.y < mstart.y) {
743 int swap = y_rep_end;
744 y_rep_end = y_rep_start;
745 y_rep_start = swap;
749 rep_start = MAX(x_rep_start, y_rep_start);
750 rep_end = MAX(x_rep_end, y_rep_end);
752 // extend the line between start and end by rep_start times from the start
753 // and rep_end times from the end
755 dx = end->x - start->x;
756 dy = end->y - start->y;
758 start->x = start->x - dx * rep_start;
759 start->y = start->y - dy * rep_start;
761 end->x = end->x + dx * rep_end;
762 end->y = end->y + dy * rep_end;
764 // set the input range for the function -- the function knows how to
765 // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
766 input_value_range[0] = 0.0 - 1.0 * rep_start;
767 input_value_range[1] = 1.0 + 1.0 * rep_end;
769 return CGFunctionCreate (gpat,
771 input_value_range,
773 output_value_ranges,
774 &callbacks);
777 /* Obtain a CGImageRef from a #cairo_surface_t * */
779 static void
780 DataProviderReleaseCallback (void *info, const void *data, size_t size)
782 cairo_surface_t *surface = (cairo_surface_t *) info;
783 cairo_surface_destroy (surface);
786 static cairo_status_t
787 _cairo_surface_to_cgimage (cairo_surface_t *target,
788 cairo_surface_t *source,
789 CGImageRef *image_out)
791 cairo_status_t status = CAIRO_STATUS_SUCCESS;
792 cairo_surface_type_t stype = cairo_surface_get_type (source);
793 cairo_image_surface_t *isurf;
794 CGImageRef image;
795 void *image_extra;
797 if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
798 cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
799 *image_out = CGImageRetain (surface->image);
800 return CAIRO_STATUS_SUCCESS;
803 if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
804 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
805 if (IS_EMPTY(surface)) {
806 *image_out = NULL;
807 return CAIRO_STATUS_SUCCESS;
810 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
811 *image_out = CGBitmapContextCreateImage (surface->cgContext);
812 if (*image_out)
813 return CAIRO_STATUS_SUCCESS;
817 if (stype != CAIRO_SURFACE_TYPE_IMAGE) {
818 status = _cairo_surface_acquire_source_image (source, &isurf, &image_extra);
819 if (status)
820 return status;
821 } else {
822 isurf = (cairo_image_surface_t *) source;
825 if (isurf->width == 0 || isurf->height == 0) {
826 *image_out = NULL;
827 } else {
828 cairo_image_surface_t *isurf_snap = NULL;
829 isurf_snap = (cairo_image_surface_t*) _cairo_surface_snapshot ((cairo_surface_t*) isurf);
830 if (isurf_snap == NULL)
831 return CAIRO_STATUS_NO_MEMORY;
833 if (isurf_snap->base.type != CAIRO_SURFACE_TYPE_IMAGE)
834 return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
836 image = _cairo_quartz_create_cgimage (isurf_snap->format,
837 isurf_snap->width,
838 isurf_snap->height,
839 isurf_snap->stride,
840 isurf_snap->data,
841 TRUE,
842 NULL,
843 DataProviderReleaseCallback,
844 isurf_snap);
846 *image_out = image;
849 if ((cairo_surface_t*) isurf != source)
850 _cairo_surface_release_source_image (source, isurf, image_extra);
852 return status;
855 /* Generic #cairo_pattern_t -> CGPattern function */
857 typedef struct {
858 CGImageRef image;
859 CGRect imageBounds;
860 cairo_bool_t do_reflect;
861 } SurfacePatternDrawInfo;
863 static void
864 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
866 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
868 CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
869 CGContextScaleCTM (context, 1, -1);
871 CGContextDrawImage (context, info->imageBounds, info->image);
872 if (info->do_reflect) {
873 /* draw 3 more copies of the image, flipped.
874 * DrawImage draws the image according to the current Y-direction into the rectangle given
875 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
876 * of the base image position, and the Y axis is extending upwards.
879 /* Make the y axis extend downwards, and draw a flipped image below */
880 CGContextScaleCTM (context, 1, -1);
881 CGContextDrawImage (context, info->imageBounds, info->image);
883 /* Shift over to the right, and flip vertically (translation is 2x,
884 * since we'll be flipping and thus rendering the rectangle "backwards"
886 CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
887 CGContextScaleCTM (context, -1, 1);
888 CGContextDrawImage (context, info->imageBounds, info->image);
890 /* Then unflip the Y-axis again, and draw the image above the point. */
891 CGContextScaleCTM (context, 1, -1);
892 CGContextDrawImage (context, info->imageBounds, info->image);
896 static void
897 SurfacePatternReleaseInfoFunc (void *ainfo)
899 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
901 CGImageRelease (info->image);
902 free (info);
905 static cairo_int_status_t
906 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
907 cairo_pattern_t *apattern,
908 CGPatternRef *cgpat)
910 cairo_surface_pattern_t *spattern;
911 cairo_surface_t *pat_surf;
912 cairo_rectangle_int_t extents;
914 CGImageRef image;
915 CGRect pbounds;
916 CGAffineTransform ptransform, stransform;
917 CGPatternCallbacks cb = { 0,
918 SurfacePatternDrawFunc,
919 SurfacePatternReleaseInfoFunc };
920 SurfacePatternDrawInfo *info;
921 float rw, rh;
922 cairo_status_t status;
924 cairo_matrix_t m;
926 /* SURFACE is the only type we'll handle here */
927 if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE)
928 return CAIRO_INT_STATUS_UNSUPPORTED;
930 spattern = (cairo_surface_pattern_t *) apattern;
931 pat_surf = spattern->surface;
933 status = _cairo_surface_get_extents (pat_surf, &extents);
934 if (status)
935 return status;
937 status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
938 if (status != CAIRO_STATUS_SUCCESS)
939 return CAIRO_INT_STATUS_UNSUPPORTED;
941 if (image == NULL)
942 return CAIRO_INT_STATUS_NOTHING_TO_DO;
944 info = malloc(sizeof(SurfacePatternDrawInfo));
945 if (!info)
946 return CAIRO_STATUS_NO_MEMORY;
948 /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
949 * that the data will stick around for this image when the printer gets to it.
950 * Otherwise, the underlying data store may disappear from under us!
952 * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
953 * since the Quartz surfaces have a higher chance of sticking around. If the
954 * source is a quartz image surface, then it's set up to retain a ref to the
955 * image surface that it's backed by.
957 info->image = image;
958 info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
959 info->do_reflect = FALSE;
961 pbounds.origin.x = 0;
962 pbounds.origin.y = 0;
964 if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
965 pbounds.size.width = 2.0 * extents.width;
966 pbounds.size.height = 2.0 * extents.height;
967 info->do_reflect = TRUE;
968 } else {
969 pbounds.size.width = extents.width;
970 pbounds.size.height = extents.height;
972 rw = pbounds.size.width;
973 rh = pbounds.size.height;
975 m = spattern->base.matrix;
976 cairo_matrix_invert(&m);
977 _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
979 /* The pattern matrix is relative to the bottom left, again; the
980 * incoming cairo pattern matrix is relative to the upper left.
981 * So we take the pattern matrix and the original context matrix,
982 * which gives us the correct base translation/y flip.
984 ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM);
986 #ifdef QUARTZ_DEBUG
987 ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
988 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));
989 CGAffineTransform xform = CGContextGetCTM(dest->cgContext);
990 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));
991 #endif
993 *cgpat = CGPatternCreate (info,
994 pbounds,
995 ptransform,
996 rw, rh,
997 kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
998 TRUE,
999 &cb);
1001 return CAIRO_STATUS_SUCCESS;
1004 typedef enum {
1005 DO_SOLID,
1006 DO_SHADING,
1007 DO_PATTERN,
1008 DO_IMAGE,
1009 DO_UNSUPPORTED,
1010 DO_NOTHING,
1011 DO_TILED_IMAGE
1012 } cairo_quartz_action_t;
1014 static cairo_quartz_action_t
1015 _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
1016 cairo_pattern_t *source)
1018 CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
1019 CGAffineTransform ctm;
1020 double x0, y0, w, h;
1022 cairo_surface_t *fallback;
1023 cairo_t *fallback_cr;
1024 CGImageRef img;
1026 cairo_status_t status;
1028 if (clipBox.size.width == 0.0f ||
1029 clipBox.size.height == 0.0f)
1030 return DO_NOTHING;
1032 // the clipBox is in userspace, so:
1033 ctm = CGContextGetCTM (surface->cgContext);
1034 ctm = CGAffineTransformInvert (ctm);
1035 clipBox = CGRectApplyAffineTransform (clipBox, ctm);
1037 // get the Y flip right -- the CTM will always have a Y flip in place
1038 clipBox.origin.y = surface->extents.height - (clipBox.origin.y + clipBox.size.height);
1040 x0 = floor(clipBox.origin.x);
1041 y0 = floor(clipBox.origin.y);
1042 w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
1043 h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
1045 /* Create a temporary the size of the clip surface, and position
1046 * it so that the device origin coincides with the original surface */
1047 fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
1048 cairo_surface_set_device_offset (fallback, -x0, -y0);
1050 /* Paint the source onto our temporary */
1051 fallback_cr = cairo_create (fallback);
1052 cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE);
1053 cairo_set_source (fallback_cr, source);
1054 cairo_paint (fallback_cr);
1055 cairo_destroy (fallback_cr);
1057 status = _cairo_surface_to_cgimage ((cairo_surface_t*) surface, fallback, &img);
1058 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
1059 return DO_NOTHING;
1060 if (status)
1061 return DO_UNSUPPORTED;
1063 surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
1064 surface->sourceImage = img;
1065 surface->sourceImageSurface = fallback;
1066 surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
1068 return DO_IMAGE;
1071 static cairo_quartz_action_t
1072 _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
1073 cairo_linear_pattern_t *lpat)
1075 cairo_pattern_t *abspat = (cairo_pattern_t *) lpat;
1076 cairo_matrix_t mat;
1077 CGPoint start, end;
1078 CGFunctionRef gradFunc;
1079 CGColorSpaceRef rgb;
1080 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
1082 if (lpat->base.n_stops == 0) {
1083 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
1084 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
1085 return DO_SOLID;
1088 cairo_pattern_get_matrix (abspat, &mat);
1089 cairo_matrix_invert (&mat);
1090 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
1092 rgb = CGColorSpaceCreateDeviceRGB();
1094 start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
1095 _cairo_fixed_to_double (lpat->p1.y));
1096 end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
1097 _cairo_fixed_to_double (lpat->p2.y));
1099 // ref will be released by the CGShading's destructor
1100 cairo_pattern_reference ((cairo_pattern_t*) lpat);
1102 if (abspat->extend == CAIRO_EXTEND_NONE ||
1103 abspat->extend == CAIRO_EXTEND_PAD)
1105 gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) lpat);
1106 } else {
1107 gradFunc = CreateRepeatingGradientFunction (surface,
1108 (cairo_gradient_pattern_t*) lpat,
1109 &start, &end, surface->sourceTransform);
1112 surface->sourceShading = CGShadingCreateAxial (rgb,
1113 start, end,
1114 gradFunc,
1115 extend, extend);
1117 CGColorSpaceRelease(rgb);
1118 CGFunctionRelease(gradFunc);
1120 return DO_SHADING;
1123 static cairo_quartz_action_t
1124 _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
1125 cairo_radial_pattern_t *rpat)
1127 cairo_pattern_t *abspat = (cairo_pattern_t *)rpat;
1128 cairo_matrix_t mat;
1129 CGPoint start, end;
1130 CGFunctionRef gradFunc;
1131 CGColorSpaceRef rgb;
1132 bool extend = abspat->extend == CAIRO_EXTEND_PAD;
1134 if (rpat->base.n_stops == 0) {
1135 CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
1136 CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
1137 return DO_SOLID;
1140 if (abspat->extend == CAIRO_EXTEND_REPEAT ||
1141 abspat->extend == CAIRO_EXTEND_REFLECT)
1143 /* I started trying to map these to Quartz, but it's much harder
1144 * then the linear case (I think it would involve doing multiple
1145 * Radial shadings). So, instead, let's just render an image
1146 * for pixman to draw the shading into, and use that.
1148 return _cairo_quartz_setup_fallback_source (surface, (cairo_pattern_t*) rpat);
1151 cairo_pattern_get_matrix (abspat, &mat);
1152 cairo_matrix_invert (&mat);
1153 _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
1155 rgb = CGColorSpaceCreateDeviceRGB();
1157 start = CGPointMake (_cairo_fixed_to_double (rpat->c1.x),
1158 _cairo_fixed_to_double (rpat->c1.y));
1159 end = CGPointMake (_cairo_fixed_to_double (rpat->c2.x),
1160 _cairo_fixed_to_double (rpat->c2.y));
1162 // ref will be released by the CGShading's destructor
1163 cairo_pattern_reference ((cairo_pattern_t*) rpat);
1165 gradFunc = CreateGradientFunction ((cairo_gradient_pattern_t*) rpat);
1167 surface->sourceShading = CGShadingCreateRadial (rgb,
1168 start,
1169 _cairo_fixed_to_double (rpat->r1),
1170 end,
1171 _cairo_fixed_to_double (rpat->r2),
1172 gradFunc,
1173 extend, extend);
1175 CGColorSpaceRelease(rgb);
1176 CGFunctionRelease(gradFunc);
1178 return DO_SHADING;
1181 static cairo_quartz_action_t
1182 _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
1183 cairo_pattern_t *source)
1185 assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
1187 surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
1188 CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
1190 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1191 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1193 CGContextSetRGBStrokeColor (surface->cgContext,
1194 solid->color.red,
1195 solid->color.green,
1196 solid->color.blue,
1197 solid->color.alpha);
1198 CGContextSetRGBFillColor (surface->cgContext,
1199 solid->color.red,
1200 solid->color.green,
1201 solid->color.blue,
1202 solid->color.alpha);
1204 return DO_SOLID;
1207 if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
1208 cairo_linear_pattern_t *lpat = (cairo_linear_pattern_t *)source;
1209 return _cairo_quartz_setup_linear_source (surface, lpat);
1213 if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
1214 cairo_radial_pattern_t *rpat = (cairo_radial_pattern_t *)source;
1215 return _cairo_quartz_setup_radial_source (surface, rpat);
1219 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1220 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1222 cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) source;
1223 cairo_surface_t *pat_surf = spat->surface;
1224 CGImageRef img;
1225 cairo_matrix_t m = spat->base.matrix;
1226 cairo_rectangle_int_t extents;
1227 cairo_status_t status;
1228 CGAffineTransform xform;
1229 CGRect srcRect;
1230 cairo_fixed_t fw, fh;
1232 status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
1233 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
1234 return DO_NOTHING;
1235 if (status)
1236 return DO_UNSUPPORTED;
1238 surface->sourceImage = img;
1240 cairo_matrix_invert(&m);
1241 _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
1243 status = _cairo_surface_get_extents (pat_surf, &extents);
1244 if (status)
1245 return DO_UNSUPPORTED;
1247 if (source->extend == CAIRO_EXTEND_NONE) {
1248 surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
1249 return DO_IMAGE;
1252 /* Quartz seems to tile images at pixel-aligned regions only -- this
1253 * leads to seams if the image doesn't end up scaling to fill the
1254 * space exactly. The CGPattern tiling approach doesn't have this
1255 * problem. Check if we're going to fill up the space (within some
1256 * epsilon), and if not, fall back to the CGPattern type.
1259 xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
1260 surface->sourceTransform);
1262 srcRect = CGRectMake (0, 0, extents.width, extents.height);
1263 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1265 fw = _cairo_fixed_from_double (srcRect.size.width);
1266 fh = _cairo_fixed_from_double (srcRect.size.height);
1268 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1269 (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1271 /* We're good to use DrawTiledImage, but ensure that
1272 * the math works out */
1274 srcRect.size.width = round(srcRect.size.width);
1275 srcRect.size.height = round(srcRect.size.height);
1277 xform = CGAffineTransformInvert (xform);
1279 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1281 surface->sourceImageRect = srcRect;
1283 return DO_TILED_IMAGE;
1286 /* Fall through to generic SURFACE case */
1289 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1290 float patternAlpha = 1.0f;
1291 CGColorSpaceRef patternSpace;
1292 CGPatternRef pattern;
1293 cairo_int_status_t status;
1295 status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
1296 if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
1297 return DO_NOTHING;
1298 if (status)
1299 return DO_UNSUPPORTED;
1301 // Save before we change the pattern, colorspace, etc. so that
1302 // we can restore and make sure that quartz releases our
1303 // pattern (which may be stack allocated)
1304 CGContextSaveGState(surface->cgContext);
1306 patternSpace = CGColorSpaceCreatePattern(NULL);
1307 CGContextSetFillColorSpace (surface->cgContext, patternSpace);
1308 CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
1309 CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
1310 CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
1311 CGColorSpaceRelease (patternSpace);
1313 /* Quartz likes to munge the pattern phase (as yet unexplained
1314 * why); force it to 0,0 as we've already baked in the correct
1315 * pattern translation into the pattern matrix
1317 CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
1319 surface->sourcePattern = pattern;
1321 return DO_PATTERN;
1324 return DO_UNSUPPORTED;
1327 static void
1328 _cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
1329 cairo_pattern_t *source)
1331 CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
1333 if (surface->sourceImage) {
1334 CGImageRelease(surface->sourceImage);
1335 surface->sourceImage = NULL;
1337 cairo_surface_destroy(surface->sourceImageSurface);
1338 surface->sourceImageSurface = NULL;
1341 if (surface->sourceShading) {
1342 CGShadingRelease(surface->sourceShading);
1343 surface->sourceShading = NULL;
1346 if (surface->sourcePattern) {
1347 CGPatternRelease(surface->sourcePattern);
1348 // To tear down the pattern and colorspace
1349 CGContextRestoreGState(surface->cgContext);
1351 surface->sourcePattern = NULL;
1356 * get source/dest image implementation
1359 /* Read the image from the surface's front buffer */
1360 static cairo_int_status_t
1361 _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
1362 cairo_image_surface_t **image_out)
1364 unsigned char *imageData;
1365 cairo_image_surface_t *isurf;
1367 if (IS_EMPTY(surface)) {
1368 *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1369 return CAIRO_STATUS_SUCCESS;
1372 if (surface->imageSurfaceEquiv) {
1373 *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
1374 return CAIRO_STATUS_SUCCESS;
1377 if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) {
1378 unsigned int stride;
1379 unsigned int bitinfo;
1380 unsigned int bpc, bpp;
1381 CGColorSpaceRef colorspace;
1382 unsigned int color_comps;
1384 imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
1386 #ifdef USE_10_3_WORKAROUNDS
1387 bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
1388 #else
1389 bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1390 #endif
1391 stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1392 bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1393 bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
1395 // let's hope they don't add YUV under us
1396 colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1397 color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
1399 // XXX TODO: We can handle all of these by converting to
1400 // pixman masks, including non-native-endian masks
1401 if (bpc != 8)
1402 return CAIRO_INT_STATUS_UNSUPPORTED;
1404 if (bpp != 32 && bpp != 8)
1405 return CAIRO_INT_STATUS_UNSUPPORTED;
1407 if (color_comps != 3 && color_comps != 1)
1408 return CAIRO_INT_STATUS_UNSUPPORTED;
1410 if (bpp == 32 && color_comps == 3 &&
1411 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1412 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1414 isurf = (cairo_image_surface_t *)
1415 cairo_image_surface_create_for_data (imageData,
1416 CAIRO_FORMAT_ARGB32,
1417 surface->extents.width,
1418 surface->extents.height,
1419 stride);
1420 } else if (bpp == 32 && color_comps == 3 &&
1421 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1422 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1424 isurf = (cairo_image_surface_t *)
1425 cairo_image_surface_create_for_data (imageData,
1426 CAIRO_FORMAT_RGB24,
1427 surface->extents.width,
1428 surface->extents.height,
1429 stride);
1430 } else if (bpp == 8 && color_comps == 1)
1432 isurf = (cairo_image_surface_t *)
1433 cairo_image_surface_create_for_data (imageData,
1434 CAIRO_FORMAT_A8,
1435 surface->extents.width,
1436 surface->extents.height,
1437 stride);
1438 } else {
1439 return CAIRO_INT_STATUS_UNSUPPORTED;
1441 } else {
1442 return CAIRO_INT_STATUS_UNSUPPORTED;
1445 *image_out = isurf;
1446 return CAIRO_STATUS_SUCCESS;
1450 * Cairo surface backend implementations
1453 static cairo_status_t
1454 _cairo_quartz_surface_finish (void *abstract_surface)
1456 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1458 ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1460 if (IS_EMPTY(surface))
1461 return CAIRO_STATUS_SUCCESS;
1463 /* Restore our saved gstate that we use to reset clipping */
1464 CGContextRestoreGState (surface->cgContext);
1466 CGContextRelease (surface->cgContext);
1468 surface->cgContext = NULL;
1470 if (surface->imageSurfaceEquiv) {
1471 cairo_surface_destroy (surface->imageSurfaceEquiv);
1472 surface->imageSurfaceEquiv = NULL;
1475 if (surface->imageData) {
1476 free (surface->imageData);
1477 surface->imageData = NULL;
1480 return CAIRO_STATUS_SUCCESS;
1483 static cairo_status_t
1484 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1485 cairo_image_surface_t **image_out,
1486 void **image_extra)
1488 cairo_int_status_t status;
1489 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1491 //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1493 status = _cairo_quartz_get_image (surface, image_out);
1494 if (status)
1495 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1497 *image_extra = NULL;
1499 return CAIRO_STATUS_SUCCESS;
1502 static void
1503 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1504 cairo_image_surface_t *image,
1505 void *image_extra)
1507 cairo_surface_destroy ((cairo_surface_t *) image);
1511 static cairo_status_t
1512 _cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
1513 cairo_rectangle_int_t *interest_rect,
1514 cairo_image_surface_t **image_out,
1515 cairo_rectangle_int_t *image_rect,
1516 void **image_extra)
1518 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1519 cairo_int_status_t status;
1521 ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
1523 status = _cairo_quartz_get_image (surface, image_out);
1524 if (status)
1525 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1527 *image_rect = surface->extents;
1528 *image_extra = NULL;
1530 return CAIRO_STATUS_SUCCESS;
1533 static void
1534 _cairo_quartz_surface_release_dest_image (void *abstract_surface,
1535 cairo_rectangle_int_t *interest_rect,
1536 cairo_image_surface_t *image,
1537 cairo_rectangle_int_t *image_rect,
1538 void *image_extra)
1540 //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1542 //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
1544 cairo_surface_destroy ((cairo_surface_t *) image);
1547 static cairo_surface_t *
1548 _cairo_quartz_surface_create_similar (void *abstract_surface,
1549 cairo_content_t content,
1550 int width,
1551 int height)
1553 /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
1555 cairo_format_t format;
1557 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1558 format = CAIRO_FORMAT_ARGB32;
1559 else if (content == CAIRO_CONTENT_COLOR)
1560 format = CAIRO_FORMAT_RGB24;
1561 else if (content == CAIRO_CONTENT_ALPHA)
1562 format = CAIRO_FORMAT_A8;
1563 else
1564 return NULL;
1566 // verify width and height of surface
1567 if (!_cairo_quartz_verify_surface_size(width, height)) {
1568 return _cairo_surface_create_in_error (_cairo_error
1569 (CAIRO_STATUS_NO_MEMORY));
1572 return cairo_quartz_surface_create (format, width, height);
1575 static cairo_status_t
1576 _cairo_quartz_surface_clone_similar (void *abstract_surface,
1577 cairo_surface_t *src,
1578 int src_x,
1579 int src_y,
1580 int width,
1581 int height,
1582 int *clone_offset_x,
1583 int *clone_offset_y,
1584 cairo_surface_t **clone_out)
1586 cairo_quartz_surface_t *new_surface = NULL;
1587 cairo_format_t new_format;
1588 CGImageRef quartz_image = NULL;
1589 cairo_status_t status;
1591 *clone_out = NULL;
1593 // verify width and height of surface
1594 if (!_cairo_quartz_verify_surface_size(width, height)) {
1595 return CAIRO_INT_STATUS_UNSUPPORTED;
1598 if (width == 0 || height == 0) {
1599 *clone_out = (cairo_surface_t*)
1600 _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
1601 width, height);
1602 *clone_offset_x = 0;
1603 *clone_offset_y = 0;
1604 return CAIRO_STATUS_SUCCESS;
1607 if (src->backend->type == CAIRO_SURFACE_TYPE_QUARTZ) {
1608 cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
1610 if (IS_EMPTY(qsurf)) {
1611 *clone_out = (cairo_surface_t*)
1612 _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
1613 qsurf->extents.width, qsurf->extents.height);
1614 *clone_offset_x = 0;
1615 *clone_offset_y = 0;
1616 return CAIRO_STATUS_SUCCESS;
1620 status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
1621 if (status)
1622 return CAIRO_INT_STATUS_UNSUPPORTED;
1624 new_format = CAIRO_FORMAT_ARGB32; /* assumed */
1625 if (_cairo_surface_is_image (src)) {
1626 new_format = ((cairo_image_surface_t *) src)->format;
1629 new_surface = (cairo_quartz_surface_t *)
1630 cairo_quartz_surface_create (new_format, width, height);
1632 if (quartz_image == NULL)
1633 goto FINISH;
1635 if (!new_surface || new_surface->base.status) {
1636 CGImageRelease (quartz_image);
1637 return CAIRO_INT_STATUS_UNSUPPORTED;
1640 CGContextSaveGState (new_surface->cgContext);
1642 CGContextSetCompositeOperation (new_surface->cgContext,
1643 kPrivateCGCompositeCopy);
1645 CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
1646 CGContextDrawImage (new_surface->cgContext,
1647 CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)),
1648 quartz_image);
1650 CGContextRestoreGState (new_surface->cgContext);
1652 CGImageRelease (quartz_image);
1654 FINISH:
1655 *clone_offset_x = src_x;
1656 *clone_offset_y = src_y;
1657 *clone_out = (cairo_surface_t*) new_surface;
1659 return CAIRO_STATUS_SUCCESS;
1662 static cairo_int_status_t
1663 _cairo_quartz_surface_get_extents (void *abstract_surface,
1664 cairo_rectangle_int_t *extents)
1666 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1668 *extents = surface->extents;
1670 return CAIRO_STATUS_SUCCESS;
1673 static cairo_int_status_t
1674 _cairo_quartz_surface_paint (void *abstract_surface,
1675 cairo_operator_t op,
1676 cairo_pattern_t *source)
1678 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1679 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1680 cairo_quartz_action_t action;
1682 ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
1684 if (IS_EMPTY(surface))
1685 return CAIRO_STATUS_SUCCESS;
1687 if (op == CAIRO_OPERATOR_DEST)
1688 return CAIRO_STATUS_SUCCESS;
1690 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1692 action = _cairo_quartz_setup_source (surface, source);
1694 if (action == DO_SOLID || action == DO_PATTERN) {
1695 CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
1696 surface->extents.y,
1697 surface->extents.width,
1698 surface->extents.height));
1699 } else if (action == DO_SHADING) {
1700 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1701 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1702 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1703 CGContextSaveGState (surface->cgContext);
1705 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1706 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1707 CGContextScaleCTM (surface->cgContext, 1, -1);
1709 if (action == DO_IMAGE)
1710 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1711 else
1712 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1713 CGContextRestoreGState (surface->cgContext);
1714 } else if (action != DO_NOTHING) {
1715 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1718 _cairo_quartz_teardown_source (surface, source);
1720 ND((stderr, "-- paint\n"));
1721 return rv;
1724 static cairo_int_status_t
1725 _cairo_quartz_surface_fill (void *abstract_surface,
1726 cairo_operator_t op,
1727 cairo_pattern_t *source,
1728 cairo_path_fixed_t *path,
1729 cairo_fill_rule_t fill_rule,
1730 double tolerance,
1731 cairo_antialias_t antialias)
1733 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1734 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1735 cairo_quartz_action_t action;
1736 quartz_stroke_t stroke;
1737 cairo_box_t box;
1738 CGPathRef path_for_unbounded = NULL;
1740 ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
1742 if (IS_EMPTY(surface))
1743 return CAIRO_STATUS_SUCCESS;
1745 if (op == CAIRO_OPERATOR_DEST)
1746 return CAIRO_STATUS_SUCCESS;
1748 /* Check whether the path would be a no-op */
1749 /* XXX handle unbounded ops */
1750 if (_cairo_path_fixed_is_empty(path) ||
1751 (_cairo_path_fixed_is_box(path, &box) &&
1752 box.p1.x == box.p2.x &&
1753 box.p1.y == box.p2.y))
1755 return CAIRO_STATUS_SUCCESS;
1758 CGContextSaveGState (surface->cgContext);
1760 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1761 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1763 action = _cairo_quartz_setup_source (surface, source);
1765 CGContextBeginPath (surface->cgContext);
1767 stroke.cgContext = surface->cgContext;
1768 stroke.ctm_inverse = NULL;
1769 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1770 if (rv)
1771 goto BAIL;
1773 if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
1774 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1776 if (action == DO_SOLID || action == DO_PATTERN) {
1777 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1778 CGContextFillPath (surface->cgContext);
1779 else
1780 CGContextEOFillPath (surface->cgContext);
1781 } else if (action == DO_SHADING) {
1783 // we have to clip and then paint the shading; we can't fill
1784 // with the shading
1785 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1786 CGContextClip (surface->cgContext);
1787 else
1788 CGContextEOClip (surface->cgContext);
1790 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1791 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1792 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1793 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1794 CGContextClip (surface->cgContext);
1795 else
1796 CGContextEOClip (surface->cgContext);
1798 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1799 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1800 CGContextScaleCTM (surface->cgContext, 1, -1);
1802 if (action == DO_IMAGE)
1803 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1804 else
1805 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1806 } else if (action != DO_NOTHING) {
1807 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1810 BAIL:
1811 _cairo_quartz_teardown_source (surface, source);
1813 CGContextRestoreGState (surface->cgContext);
1815 if (path_for_unbounded) {
1816 unbounded_op_data_t ub;
1817 ub.op = UNBOUNDED_STROKE_FILL;
1818 ub.u.stroke_fill.cgPath = path_for_unbounded;
1819 ub.u.stroke_fill.fill_rule = fill_rule;
1821 _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
1822 CGPathRelease (path_for_unbounded);
1825 ND((stderr, "-- fill\n"));
1826 return rv;
1829 static cairo_int_status_t
1830 _cairo_quartz_surface_stroke (void *abstract_surface,
1831 cairo_operator_t op,
1832 cairo_pattern_t *source,
1833 cairo_path_fixed_t *path,
1834 cairo_stroke_style_t *style,
1835 cairo_matrix_t *ctm,
1836 cairo_matrix_t *ctm_inverse,
1837 double tolerance,
1838 cairo_antialias_t antialias)
1840 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1841 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1842 cairo_quartz_action_t action;
1843 quartz_stroke_t stroke;
1844 CGAffineTransform origCTM, strokeTransform;
1845 CGPathRef path_for_unbounded = NULL;
1847 ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
1849 if (IS_EMPTY(surface))
1850 return CAIRO_STATUS_SUCCESS;
1852 if (op == CAIRO_OPERATOR_DEST)
1853 return CAIRO_STATUS_SUCCESS;
1855 CGContextSaveGState (surface->cgContext);
1857 // Turning antialiasing off used to cause misrendering with
1858 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1859 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1860 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1861 CGContextSetLineWidth (surface->cgContext, style->line_width);
1862 CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1863 CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1864 CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1866 origCTM = CGContextGetCTM (surface->cgContext);
1868 _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1869 CGContextConcatCTM (surface->cgContext, strokeTransform);
1871 if (style->dash && style->num_dashes) {
1872 #define STATIC_DASH 32
1873 float sdash[STATIC_DASH];
1874 float *fdash = sdash;
1875 unsigned int max_dashes = style->num_dashes;
1876 unsigned int k;
1878 if (style->num_dashes%2)
1879 max_dashes *= 2;
1880 if (max_dashes > STATIC_DASH)
1881 fdash = _cairo_malloc_ab (max_dashes, sizeof (float));
1882 if (fdash == NULL)
1883 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1885 for (k = 0; k < max_dashes; k++)
1886 fdash[k] = (float) style->dash[k % style->num_dashes];
1888 CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
1889 if (fdash != sdash)
1890 free (fdash);
1893 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
1895 action = _cairo_quartz_setup_source (surface, source);
1897 CGContextBeginPath (surface->cgContext);
1899 stroke.cgContext = surface->cgContext;
1900 stroke.ctm_inverse = ctm_inverse;
1901 rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
1902 if (rv)
1903 goto BAIL;
1905 if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
1906 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1908 if (action == DO_SOLID || action == DO_PATTERN) {
1909 CGContextStrokePath (surface->cgContext);
1910 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
1911 CGContextReplacePathWithStrokedPath (surface->cgContext);
1912 CGContextClip (surface->cgContext);
1914 CGContextSetCTM (surface->cgContext, origCTM);
1916 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1917 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
1918 CGContextScaleCTM (surface->cgContext, 1, -1);
1920 if (action == DO_IMAGE)
1921 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1922 else
1923 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
1924 } else if (action == DO_SHADING) {
1925 CGContextReplacePathWithStrokedPath (surface->cgContext);
1926 CGContextClip (surface->cgContext);
1928 CGContextSetCTM (surface->cgContext, origCTM);
1930 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
1931 CGContextDrawShading (surface->cgContext, surface->sourceShading);
1932 } else if (action != DO_NOTHING) {
1933 rv = CAIRO_INT_STATUS_UNSUPPORTED;
1936 BAIL:
1937 _cairo_quartz_teardown_source (surface, source);
1939 CGContextRestoreGState (surface->cgContext);
1941 if (path_for_unbounded) {
1942 unbounded_op_data_t ub;
1944 CGContextBeginPath (surface->cgContext);
1946 /* recreate the stroke state, but without the CTM, as it's been already baked
1947 * into the path.
1949 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
1950 CGContextSetLineWidth (surface->cgContext, style->line_width);
1951 CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1952 CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1953 CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
1955 CGContextAddPath (surface->cgContext, path_for_unbounded);
1956 CGPathRelease (path_for_unbounded);
1958 CGContextReplacePathWithStrokedPath (surface->cgContext);
1959 path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
1961 ub.op = UNBOUNDED_STROKE_FILL;
1962 ub.u.stroke_fill.cgPath = path_for_unbounded;
1963 ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
1965 _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
1967 CGPathRelease (path_for_unbounded);
1970 ND((stderr, "-- stroke\n"));
1971 return rv;
1974 #if CAIRO_HAS_QUARTZ_FONT
1975 static cairo_int_status_t
1976 _cairo_quartz_surface_show_glyphs (void *abstract_surface,
1977 cairo_operator_t op,
1978 cairo_pattern_t *source,
1979 cairo_glyph_t *glyphs,
1980 int num_glyphs,
1981 cairo_scaled_font_t *scaled_font,
1982 int *remaining_glyphs)
1984 CGAffineTransform textTransform, ctm;
1985 #define STATIC_BUF_SIZE 64
1986 CGGlyph glyphs_static[STATIC_BUF_SIZE];
1987 CGSize cg_advances_static[STATIC_BUF_SIZE];
1988 CGGlyph *cg_glyphs = &glyphs_static[0];
1989 CGSize *cg_advances = &cg_advances_static[0];
1991 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1992 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1993 cairo_quartz_action_t action;
1994 float xprev, yprev;
1995 int i;
1996 CGFontRef cgfref = NULL;
1998 cairo_bool_t isClipping = FALSE;
1999 cairo_bool_t didForceFontSmoothing = FALSE;
2001 if (IS_EMPTY(surface))
2002 return CAIRO_STATUS_SUCCESS;
2004 if (num_glyphs <= 0)
2005 return CAIRO_STATUS_SUCCESS;
2007 if (op == CAIRO_OPERATOR_DEST)
2008 return CAIRO_STATUS_SUCCESS;
2010 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
2011 return CAIRO_INT_STATUS_UNSUPPORTED;
2013 CGContextSaveGState (surface->cgContext);
2015 action = _cairo_quartz_setup_source (surface, source);
2016 if (action == DO_SOLID || action == DO_PATTERN) {
2017 CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
2018 } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
2019 CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
2020 isClipping = TRUE;
2021 } else {
2022 if (action != DO_NOTHING)
2023 rv = CAIRO_INT_STATUS_UNSUPPORTED;
2024 goto BAIL;
2027 CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz (op));
2029 /* this doesn't addref */
2030 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
2031 CGContextSetFont (surface->cgContext, cgfref);
2032 CGContextSetFontSize (surface->cgContext, 1.0);
2034 switch (scaled_font->options.antialias) {
2035 case CAIRO_ANTIALIAS_SUBPIXEL:
2036 CGContextSetShouldAntialias (surface->cgContext, TRUE);
2037 CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
2038 if (CGContextSetAllowsFontSmoothingPtr &&
2039 !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
2041 didForceFontSmoothing = TRUE;
2042 CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
2044 break;
2045 case CAIRO_ANTIALIAS_NONE:
2046 CGContextSetShouldAntialias (surface->cgContext, FALSE);
2047 break;
2048 case CAIRO_ANTIALIAS_GRAY:
2049 CGContextSetShouldAntialias (surface->cgContext, TRUE);
2050 CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
2051 break;
2052 case CAIRO_ANTIALIAS_DEFAULT:
2053 /* Don't do anything */
2054 break;
2057 if (num_glyphs > STATIC_BUF_SIZE) {
2058 cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
2059 if (cg_glyphs == NULL) {
2060 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2061 goto BAIL;
2064 cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
2065 if (cg_advances == NULL) {
2066 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2067 goto BAIL;
2071 textTransform = CGAffineTransformMake (scaled_font->font_matrix.xx,
2072 scaled_font->font_matrix.yx,
2073 scaled_font->font_matrix.xy,
2074 scaled_font->font_matrix.yy,
2075 0., 0.);
2076 textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0);
2077 textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx,
2078 -scaled_font->ctm.yx,
2079 -scaled_font->ctm.xy,
2080 scaled_font->ctm.yy,
2081 0., 0.),
2082 textTransform);
2084 CGContextSetTextMatrix (surface->cgContext, textTransform);
2086 /* Convert our glyph positions to glyph advances. We need n-1 advances,
2087 * since the advance at index 0 is applied after glyph 0. */
2088 xprev = glyphs[0].x;
2089 yprev = glyphs[0].y;
2091 cg_glyphs[0] = glyphs[0].index;
2093 for (i = 1; i < num_glyphs; i++) {
2094 float xf = glyphs[i].x;
2095 float yf = glyphs[i].y;
2096 cg_glyphs[i] = glyphs[i].index;
2097 cg_advances[i-1].width = xf - xprev;
2098 cg_advances[i-1].height = yf - yprev;
2099 xprev = xf;
2100 yprev = yf;
2103 if (_cairo_quartz_osx_version >= 0x1050 && isClipping) {
2104 /* If we're clipping, OSX 10.5 (at least as of 10.5.2) has a
2105 * bug (apple bug ID #5834794) where the glyph
2106 * advances/positions are not transformed by the text matrix
2107 * if kCGTextClip is being used. So, we pre-transform here.
2108 * 10.4 does not have this problem (as of 10.4.11).
2110 for (i = 0; i < num_glyphs - 1; i++)
2111 cg_advances[i] = CGSizeApplyAffineTransform(cg_advances[i], textTransform);
2114 #if 0
2115 for (i = 0; i < num_glyphs; i++) {
2116 ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height));
2118 #endif
2120 /* Translate to the first glyph's position before drawing */
2121 ctm = CGContextGetCTM (surface->cgContext);
2122 CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
2124 CGContextShowGlyphsWithAdvances (surface->cgContext,
2125 cg_glyphs,
2126 cg_advances,
2127 num_glyphs);
2129 CGContextSetCTM (surface->cgContext, ctm);
2131 if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
2132 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
2133 CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
2134 CGContextScaleCTM (surface->cgContext, 1, -1);
2136 if (action == DO_IMAGE)
2137 CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
2138 else
2139 CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
2140 } else if (action == DO_SHADING) {
2141 CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
2142 CGContextDrawShading (surface->cgContext, surface->sourceShading);
2145 BAIL:
2146 _cairo_quartz_teardown_source (surface, source);
2148 if (didForceFontSmoothing)
2149 CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
2151 CGContextRestoreGState (surface->cgContext);
2153 if (rv == CAIRO_STATUS_SUCCESS &&
2154 cgfref &&
2155 !_cairo_operator_bounded_by_mask (op))
2157 unbounded_op_data_t ub;
2158 ub.op = UNBOUNDED_SHOW_GLYPHS;
2160 ub.u.show_glyphs.isClipping = isClipping;
2161 ub.u.show_glyphs.cg_glyphs = cg_glyphs;
2162 ub.u.show_glyphs.cg_advances = cg_advances;
2163 ub.u.show_glyphs.nglyphs = num_glyphs;
2164 ub.u.show_glyphs.textTransform = textTransform;
2165 ub.u.show_glyphs.font = cgfref;
2166 ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
2168 _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
2172 if (cg_advances != &cg_advances_static[0]) {
2173 free (cg_advances);
2176 if (cg_glyphs != &glyphs_static[0]) {
2177 free (cg_glyphs);
2180 return rv;
2182 #endif /* CAIRO_HAS_QUARTZ_FONT */
2184 static cairo_int_status_t
2185 _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
2186 cairo_operator_t op,
2187 cairo_pattern_t *source,
2188 cairo_surface_pattern_t *mask)
2190 cairo_rectangle_int_t extents;
2191 CGRect rect;
2192 CGImageRef img;
2193 cairo_surface_t *pat_surf = mask->surface;
2194 cairo_status_t status = CAIRO_STATUS_SUCCESS;
2195 CGAffineTransform ctm, mask_matrix;
2197 status = _cairo_surface_get_extents (pat_surf, &extents);
2198 if (status)
2199 return status;
2201 // everything would be masked out, so do nothing
2202 if (extents.width == 0 || extents.height == 0)
2203 return CAIRO_STATUS_SUCCESS;
2205 status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
2206 if (status == CAIRO_STATUS_SUCCESS && img == NULL)
2207 return CAIRO_STATUS_SUCCESS;
2208 if (status)
2209 return status;
2211 rect = CGRectMake (0.0f, 0.0f, extents.width, extents.height);
2213 CGContextSaveGState (surface->cgContext);
2215 /* ClipToMask is essentially drawing an image, so we need to flip the CTM
2216 * to get the image to appear oriented the right way */
2217 ctm = CGContextGetCTM (surface->cgContext);
2219 _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
2220 CGContextConcatCTM (surface->cgContext, CGAffineTransformInvert(mask_matrix));
2221 CGContextTranslateCTM (surface->cgContext, 0.0f, rect.size.height);
2222 CGContextScaleCTM (surface->cgContext, 1.0f, -1.0f);
2224 CGContextClipToMaskPtr (surface->cgContext, rect, img);
2226 CGContextSetCTM (surface->cgContext, ctm);
2228 status = _cairo_quartz_surface_paint (surface, op, source);
2230 CGContextRestoreGState (surface->cgContext);
2232 if (!_cairo_operator_bounded_by_mask (op)) {
2233 unbounded_op_data_t ub;
2234 ub.op = UNBOUNDED_MASK;
2235 ub.u.mask.mask = img;
2236 ub.u.mask.maskTransform = CGAffineTransformInvert(mask_matrix);
2237 _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
2240 CGImageRelease (img);
2242 return status;
2245 /* This is somewhat less than ideal, but it gets the job done;
2246 * it would be better to avoid calling back into cairo. This
2247 * creates a temporary surface to use as the mask.
2249 static cairo_int_status_t
2250 _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
2251 cairo_operator_t op,
2252 cairo_pattern_t *source,
2253 cairo_pattern_t *mask)
2255 int width = surface->extents.width - surface->extents.x;
2256 int height = surface->extents.height - surface->extents.y;
2258 cairo_surface_t *gradient_surf = NULL;
2259 cairo_t *gradient_surf_cr = NULL;
2261 cairo_surface_pattern_t surface_pattern;
2262 cairo_int_status_t status;
2264 /* Render the gradient to a surface */
2265 gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
2266 width,
2267 height);
2268 gradient_surf_cr = cairo_create(gradient_surf);
2269 cairo_set_source (gradient_surf_cr, mask);
2270 cairo_set_operator (gradient_surf_cr, CAIRO_OPERATOR_SOURCE);
2271 cairo_paint (gradient_surf_cr);
2272 status = cairo_status (gradient_surf_cr);
2273 cairo_destroy (gradient_surf_cr);
2275 if (status)
2276 goto BAIL;
2278 _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf);
2280 status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern);
2282 _cairo_pattern_fini (&surface_pattern.base);
2284 BAIL:
2285 if (gradient_surf)
2286 cairo_surface_destroy (gradient_surf);
2288 return status;
2291 static cairo_int_status_t
2292 _cairo_quartz_surface_mask (void *abstract_surface,
2293 cairo_operator_t op,
2294 cairo_pattern_t *source,
2295 cairo_pattern_t *mask)
2297 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
2298 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
2300 ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
2302 if (IS_EMPTY(surface))
2303 return CAIRO_STATUS_SUCCESS;
2305 if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
2306 /* This is easy; we just need to paint with the alpha. */
2307 cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
2309 CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
2310 rv = _cairo_quartz_surface_paint (surface, op, source);
2311 CGContextSetAlpha (surface->cgContext, 1.0);
2313 return rv;
2316 /* If we have CGContextClipToMask, we can do more complex masks */
2317 if (CGContextClipToMaskPtr) {
2318 /* For these, we can skip creating a temporary surface, since we already have one */
2319 if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
2320 return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask);
2322 return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask);
2325 /* So, CGContextClipToMask is not present in 10.3.9, so we're
2326 * doomed; if we have imageData, we can do fallback, otherwise
2327 * just pretend success.
2329 if (surface->imageData)
2330 return CAIRO_INT_STATUS_UNSUPPORTED;
2332 return CAIRO_STATUS_SUCCESS;
2335 static cairo_int_status_t
2336 _cairo_quartz_surface_intersect_clip_path (void *abstract_surface,
2337 cairo_path_fixed_t *path,
2338 cairo_fill_rule_t fill_rule,
2339 double tolerance,
2340 cairo_antialias_t antialias)
2342 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
2343 quartz_stroke_t stroke;
2344 cairo_status_t status;
2346 ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2348 if (IS_EMPTY(surface))
2349 return CAIRO_STATUS_SUCCESS;
2351 if (path == NULL) {
2352 /* If we're being asked to reset the clip, we can only do it
2353 * by restoring the gstate to our previous saved one, and
2354 * saving it again.
2356 * Note that this assumes that ALL quartz surface creation
2357 * functions will do a SaveGState first; we do this in create_internal.
2359 CGContextRestoreGState (surface->cgContext);
2360 CGContextSaveGState (surface->cgContext);
2361 } else {
2362 CGContextBeginPath (surface->cgContext);
2363 stroke.cgContext = surface->cgContext;
2364 stroke.ctm_inverse = NULL;
2366 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2368 /* path must not be empty. */
2369 CGContextMoveToPoint (surface->cgContext, 0, 0);
2370 status = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke);
2371 if (status)
2372 return status;
2374 if (fill_rule == CAIRO_FILL_RULE_WINDING)
2375 CGContextClip (surface->cgContext);
2376 else
2377 CGContextEOClip (surface->cgContext);
2380 ND((stderr, "-- intersect_clip_path\n"));
2382 return CAIRO_STATUS_SUCCESS;
2385 // XXXtodo implement show_page; need to figure out how to handle begin/end
2387 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2388 CAIRO_SURFACE_TYPE_QUARTZ,
2389 _cairo_quartz_surface_create_similar,
2390 _cairo_quartz_surface_finish,
2391 _cairo_quartz_surface_acquire_source_image,
2392 _cairo_quartz_surface_release_source_image,
2393 _cairo_quartz_surface_acquire_dest_image,
2394 _cairo_quartz_surface_release_dest_image,
2395 _cairo_quartz_surface_clone_similar,
2396 NULL, /* composite */
2397 NULL, /* fill_rectangles */
2398 NULL, /* composite_trapezoids */
2399 NULL, /* copy_page */
2400 NULL, /* show_page */
2401 NULL, /* set_clip_region */
2402 _cairo_quartz_surface_intersect_clip_path,
2403 _cairo_quartz_surface_get_extents,
2404 NULL, /* old_show_glyphs */
2405 NULL, /* get_font_options */
2406 NULL, /* flush */
2407 NULL, /* mark_dirty_rectangle */
2408 NULL, /* scaled_font_fini */
2409 NULL, /* scaled_glyph_fini */
2411 _cairo_quartz_surface_paint,
2412 _cairo_quartz_surface_mask,
2413 _cairo_quartz_surface_stroke,
2414 _cairo_quartz_surface_fill,
2415 #if CAIRO_HAS_QUARTZ_FONT
2416 _cairo_quartz_surface_show_glyphs,
2417 #else
2418 NULL, /* show_glyphs */
2419 #endif
2421 NULL, /* snapshot */
2422 NULL, /* is_similar */
2423 NULL, /* reset */
2424 NULL /* fill_stroke */
2427 cairo_quartz_surface_t *
2428 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2429 cairo_content_t content,
2430 unsigned int width,
2431 unsigned int height)
2433 cairo_quartz_surface_t *surface;
2435 quartz_ensure_symbols();
2437 /* Init the base surface */
2438 surface = malloc(sizeof(cairo_quartz_surface_t));
2439 if (surface == NULL)
2440 return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2442 memset(surface, 0, sizeof(cairo_quartz_surface_t));
2444 _cairo_surface_init(&surface->base, &cairo_quartz_surface_backend,
2445 content);
2447 /* Save our extents */
2448 surface->extents.x = surface->extents.y = 0;
2449 surface->extents.width = width;
2450 surface->extents.height = height;
2452 if (IS_EMPTY(surface)) {
2453 surface->cgContext = NULL;
2454 surface->cgContextBaseCTM = CGAffineTransformIdentity;
2455 surface->imageData = NULL;
2456 return surface;
2459 /* Save so we can always get back to a known-good CGContext -- this is
2460 * required for proper behaviour of intersect_clip_path(NULL)
2462 CGContextSaveGState (cgContext);
2464 surface->cgContext = cgContext;
2465 surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2467 surface->imageData = NULL;
2468 surface->imageSurfaceEquiv = NULL;
2470 return surface;
2474 * cairo_quartz_surface_create_for_cg_context
2475 * @cgContext: the existing CGContext for which to create the surface
2476 * @width: width of the surface, in pixels
2477 * @height: height of the surface, in pixels
2479 * Creates a Quartz surface that wraps the given CGContext. The
2480 * CGContext is assumed to be in the standard Cairo coordinate space
2481 * (that is, with the origin at the upper left and the Y axis
2482 * increasing downward). If the CGContext is in the Quartz coordinate
2483 * space (with the origin at the bottom left), then it should be
2484 * flipped before this function is called. The flip can be accomplished
2485 * using a translate and a scale; for example:
2487 * <informalexample><programlisting>
2488 * CGContextTranslateCTM (cgContext, 0.0, height);
2489 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2490 * </programlisting></informalexample>
2492 * All Cairo operations are implemented in terms of Quartz operations,
2493 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2495 * Return value: the newly created Cairo surface.
2497 * Since: 1.4
2500 cairo_surface_t *
2501 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2502 unsigned int width,
2503 unsigned int height)
2505 cairo_quartz_surface_t *surf;
2507 CGContextRetain (cgContext);
2509 surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2510 width, height);
2511 if (surf->base.status) {
2512 CGContextRelease (cgContext);
2513 // create_internal will have set an error
2514 return (cairo_surface_t*) surf;
2517 return (cairo_surface_t *) surf;
2521 * cairo_quartz_surface_create
2522 * @format: format of pixels in the surface to create
2523 * @width: width of the surface, in pixels
2524 * @height: height of the surface, in pixels
2526 * Creates a Quartz surface backed by a CGBitmap. The surface is
2527 * created using the Device RGB (or Device Gray, for A8) color space.
2528 * All Cairo operations, including those that require software
2529 * rendering, will succeed on this surface.
2531 * Return value: the newly created surface.
2533 * Since: 1.4
2535 cairo_surface_t *
2536 cairo_quartz_surface_create (cairo_format_t format,
2537 unsigned int width,
2538 unsigned int height)
2540 cairo_quartz_surface_t *surf;
2541 CGContextRef cgc;
2542 CGColorSpaceRef cgColorspace;
2543 CGBitmapInfo bitinfo;
2544 void *imageData;
2545 int stride;
2546 int bitsPerComponent;
2548 // verify width and height of surface
2549 if (!_cairo_quartz_verify_surface_size(width, height))
2550 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2552 if (width == 0 || height == 0) {
2553 return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2554 width, height);
2557 if (format == CAIRO_FORMAT_ARGB32 ||
2558 format == CAIRO_FORMAT_RGB24)
2560 cgColorspace = CGColorSpaceCreateDeviceRGB();
2561 bitinfo = kCGBitmapByteOrder32Host;
2562 if (format == CAIRO_FORMAT_ARGB32)
2563 bitinfo |= kCGImageAlphaPremultipliedFirst;
2564 else
2565 bitinfo |= kCGImageAlphaNoneSkipFirst;
2566 bitsPerComponent = 8;
2568 /* The Apple docs say that for best performance, the stride and the data
2569 * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2570 * so we don't have to anything special on allocation.
2572 stride = width * 4;
2573 stride += (16 - (stride & 15)) & 15;
2574 } else if (format == CAIRO_FORMAT_A8) {
2575 cgColorspace = CGColorSpaceCreateDeviceGray();
2576 if (width % 4 == 0)
2577 stride = width;
2578 else
2579 stride = (width & ~3) + 4;
2580 bitinfo = kCGImageAlphaNone;
2581 bitsPerComponent = 8;
2582 } else if (format == CAIRO_FORMAT_A1) {
2583 /* I don't think we can usefully support this, as defined by
2584 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2585 * quantities.
2587 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2588 } else {
2589 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2592 imageData = _cairo_malloc_ab (height, stride);
2593 if (!imageData) {
2594 CGColorSpaceRelease (cgColorspace);
2595 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2598 /* zero the memory to match the image surface behaviour */
2599 memset (imageData, 0, height * stride);
2601 cgc = CGBitmapContextCreate (imageData,
2602 width,
2603 height,
2604 bitsPerComponent,
2605 stride,
2606 cgColorspace,
2607 bitinfo);
2608 CGColorSpaceRelease (cgColorspace);
2610 if (!cgc) {
2611 free (imageData);
2612 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2615 /* flip the Y axis */
2616 CGContextTranslateCTM (cgc, 0.0, height);
2617 CGContextScaleCTM (cgc, 1.0, -1.0);
2619 surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2620 width, height);
2621 if (surf->base.status) {
2622 CGContextRelease (cgc);
2623 free (imageData);
2624 // create_internal will have set an error
2625 return (cairo_surface_t*) surf;
2628 surf->imageData = imageData;
2629 surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2631 return (cairo_surface_t *) surf;
2635 * cairo_quartz_surface_get_cg_context
2636 * @surface: the Cairo Quartz surface
2638 * Returns the CGContextRef that the given Quartz surface is backed
2639 * by.
2641 * Return value: the CGContextRef for the given surface.
2643 * Since: 1.4
2645 CGContextRef
2646 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2648 cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
2650 if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
2651 return NULL;
2653 return quartz->cgContext;
2657 /* Debug stuff */
2659 #ifdef QUARTZ_DEBUG
2661 #include <Movies.h>
2663 void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
2665 Handle dataRef = NULL;
2666 OSType dataRefType;
2667 CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII);
2669 GraphicsExportComponent grex = 0;
2670 unsigned long sizeWritten;
2672 ComponentResult result;
2674 // create the data reference
2675 result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
2676 0, &dataRef, &dataRefType);
2678 if (NULL != dataRef && noErr == result) {
2679 // get the PNG exporter
2680 result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG,
2681 &grex);
2683 if (grex) {
2684 // tell the exporter where to find its source image
2685 result = GraphicsExportSetInputCGImage(grex, inImageRef);
2687 if (noErr == result) {
2688 // tell the exporter where to save the exporter image
2689 result = GraphicsExportSetOutputDataReference(grex, dataRef,
2690 dataRefType);
2692 if (noErr == result) {
2693 // write the PNG file
2694 result = GraphicsExportDoExport(grex, &sizeWritten);
2698 // remember to close the component
2699 CloseComponent(grex);
2702 // remember to dispose of the data reference handle
2703 DisposeHandle(dataRef);
2707 void
2708 quartz_image_to_png (CGImageRef imgref, char *dest)
2710 static int sctr = 0;
2711 char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2713 if (dest == NULL) {
2714 fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2715 sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2716 sctr++;
2717 dest = sptr;
2720 ExportCGImageToPNGFile(imgref, dest);
2723 void
2724 quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2726 static int sctr = 0;
2727 char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2729 if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2730 fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2731 return;
2734 if (dest == NULL) {
2735 fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2736 sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2737 sctr++;
2738 dest = sptr;
2741 CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2742 if (imgref == NULL) {
2743 fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2744 return;
2747 ExportCGImageToPNGFile(imgref, dest);
2749 CGImageRelease(imgref);
2752 #endif /* QUARTZ_DEBUG */