Tizen 2.0 Release
[framework/graphics/cairo.git] / src / cairo-quartz-surface.c
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
3  *
4  * Copyright � 2006, 2007 Mozilla Corporation
5  *
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.
13  *
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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17  * You should have received a copy of the MPL along with this library
18  * in the file COPYING-MPL-1.1
19  *
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/
24  *
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.
28  *
29  * The Original Code is the cairo graphics library.
30  *
31  * The Initial Developer of the Original Code is Mozilla Foundation.
32  *
33  * Contributor(s):
34  *      Vladimir Vukicevic <vladimir@mozilla.com>
35  */
36
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
38 #include "cairoint.h"
39
40 #include "cairo-quartz-private.h"
41
42 #include "cairo-composite-rectangles-private.h"
43 #include "cairo-compositor-private.h"
44 #include "cairo-default-context-private.h"
45 #include "cairo-error-private.h"
46 #include "cairo-image-surface-inline.h"
47 #include "cairo-pattern-private.h"
48 #include "cairo-surface-backend-private.h"
49 #include "cairo-surface-clipper-private.h"
50 #include "cairo-recording-surface-private.h"
51
52 #include <dlfcn.h>
53
54 #ifndef RTLD_DEFAULT
55 #define RTLD_DEFAULT ((void *) 0)
56 #endif
57
58 #include <limits.h>
59
60 #undef QUARTZ_DEBUG
61
62 #ifdef QUARTZ_DEBUG
63 #define ND(_x)  fprintf _x
64 #else
65 #define ND(_x)  do {} while(0)
66 #endif
67
68 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
69
70 /**
71  * SECTION:cairo-quartz
72  * @Title: Quartz Surfaces
73  * @Short_Description: Rendering to Quartz surfaces
74  * @See_Also: #cairo_surface_t
75  *
76  * The Quartz surface is used to render cairo graphics targeting the
77  * Apple OS X Quartz rendering system.
78  **/
79
80 /**
81  * CAIRO_HAS_QUARTZ_SURFACE:
82  *
83  * Defined if the Quartz surface backend is available.
84  * This macro can be used to conditionally compile backend-specific code.
85  *
86  * Since: 1.6
87  **/
88
89 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
90 /* This method is private, but it exists.  Its params are are exposed
91  * as args to the NS* method, but not as CG.
92  */
93 enum PrivateCGCompositeMode {
94     kPrivateCGCompositeClear            = 0,
95     kPrivateCGCompositeCopy             = 1,
96     kPrivateCGCompositeSourceOver       = 2,
97     kPrivateCGCompositeSourceIn         = 3,
98     kPrivateCGCompositeSourceOut        = 4,
99     kPrivateCGCompositeSourceAtop       = 5,
100     kPrivateCGCompositeDestinationOver  = 6,
101     kPrivateCGCompositeDestinationIn    = 7,
102     kPrivateCGCompositeDestinationOut   = 8,
103     kPrivateCGCompositeDestinationAtop  = 9,
104     kPrivateCGCompositeXOR              = 10,
105     kPrivateCGCompositePlusDarker       = 11, // (max (0, (1-d) + (1-s)))
106     kPrivateCGCompositePlusLighter      = 12, // (min (1, s + d))
107 };
108 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
109 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
110 #endif
111
112 /* Some of these are present in earlier versions of the OS than where
113  * they are public; other are not public at all
114  */
115 /* public since 10.5 */
116 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
117
118 /* public since 10.6 */
119 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
120 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
121
122 /* not yet public */
123 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
124 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
125
126 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
127
128 /*
129  * Utility functions
130  */
131
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
136
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);
142
143 static cairo_bool_t
144 _cairo_surface_is_quartz (const cairo_surface_t *surface);
145
146 /* Load all extra symbols */
147 static void quartz_ensure_symbols (void)
148 {
149     if (likely (_cairo_quartz_symbol_lookup_done))
150         return;
151
152     CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
153     CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
154     CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
155     CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
156     CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
157
158     _cairo_quartz_symbol_lookup_done = TRUE;
159 }
160
161 CGImageRef
162 CairoQuartzCreateCGImage (cairo_format_t format,
163                           unsigned int width,
164                           unsigned int height,
165                           unsigned int stride,
166                           void *data,
167                           cairo_bool_t interpolate,
168                           CGColorSpaceRef colorSpaceOverride,
169                           CGDataProviderReleaseDataCallback releaseCallback,
170                           void *releaseInfo)
171 {
172     CGImageRef image = NULL;
173     CGDataProviderRef dataProvider = NULL;
174     CGColorSpaceRef colorSpace = colorSpaceOverride;
175     CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
176     int bitsPerComponent, bitsPerPixel;
177
178     switch (format) {
179         case CAIRO_FORMAT_ARGB32:
180             if (colorSpace == NULL)
181                 colorSpace = CGColorSpaceCreateDeviceRGB ();
182             bitinfo |= kCGImageAlphaPremultipliedFirst;
183             bitsPerComponent = 8;
184             bitsPerPixel = 32;
185             break;
186
187         case CAIRO_FORMAT_RGB24:
188             if (colorSpace == NULL)
189                 colorSpace = CGColorSpaceCreateDeviceRGB ();
190             bitinfo |= kCGImageAlphaNoneSkipFirst;
191             bitsPerComponent = 8;
192             bitsPerPixel = 32;
193             break;
194
195         case CAIRO_FORMAT_A8:
196             bitsPerComponent = 8;
197             bitsPerPixel = 8;
198             break;
199
200         case CAIRO_FORMAT_A1:
201 #ifdef WORDS_BIGENDIAN
202             bitsPerComponent = 1;
203             bitsPerPixel = 1;
204             break;
205 #endif
206
207         case CAIRO_FORMAT_RGB30:
208         case CAIRO_FORMAT_RGB16_565:
209         case CAIRO_FORMAT_INVALID:
210         default:
211             return NULL;
212     }
213
214     dataProvider = CGDataProviderCreateWithData (releaseInfo,
215                                                  data,
216                                                  height * stride,
217                                                  releaseCallback);
218
219     if (unlikely (!dataProvider)) {
220         // manually release
221         if (releaseCallback)
222             releaseCallback (releaseInfo, data, height * stride);
223         goto FINISH;
224     }
225
226     if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
227         cairo_quartz_float_t decode[] = {1.0, 0.0};
228         image = CGImageMaskCreate (width, height,
229                                    bitsPerComponent,
230                                    bitsPerPixel,
231                                    stride,
232                                    dataProvider,
233                                    decode,
234                                    interpolate);
235     } else
236         image = CGImageCreate (width, height,
237                                bitsPerComponent,
238                                bitsPerPixel,
239                                stride,
240                                colorSpace,
241                                bitinfo,
242                                dataProvider,
243                                NULL,
244                                interpolate,
245                                kCGRenderingIntentDefault);
246
247 FINISH:
248
249     CGDataProviderRelease (dataProvider);
250
251     if (colorSpace != colorSpaceOverride)
252         CGColorSpaceRelease (colorSpace);
253
254     return image;
255 }
256
257 static inline cairo_bool_t
258 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
259 {
260     if (unlikely (cgc == NULL))
261         return FALSE;
262
263     if (likely (CGContextGetTypePtr)) {
264         /* 4 is the type value of a bitmap context */
265         return CGContextGetTypePtr (cgc) == 4;
266     }
267
268     /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
269     return CGBitmapContextGetBitsPerPixel (cgc) != 0;
270 }
271
272 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
273
274 #define CG_MAX_HEIGHT   SHRT_MAX
275 #define CG_MAX_WIDTH    USHRT_MAX
276
277 /* is the desired size of the surface within bounds? */
278 cairo_bool_t
279 _cairo_quartz_verify_surface_size (int width, int height)
280 {
281     /* hmmm, allow width, height == 0 ? */
282     if (width < 0 || height < 0)
283         return FALSE;
284
285     if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
286         return FALSE;
287
288     return TRUE;
289 }
290
291 /*
292  * Cairo path -> Quartz path conversion helpers
293  */
294
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)
299 {
300     //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
301     double x = _cairo_fixed_to_double (point->x);
302     double y = _cairo_fixed_to_double (point->y);
303
304     CGContextMoveToPoint (closure, x, y);
305     return CAIRO_STATUS_SUCCESS;
306 }
307
308 static cairo_status_t
309 _cairo_path_to_quartz_context_line_to (void *closure,
310                                        const cairo_point_t *point)
311 {
312     //ND ((stderr, "lineto: %f %f\n",  _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
313     double x = _cairo_fixed_to_double (point->x);
314     double y = _cairo_fixed_to_double (point->y);
315
316     CGContextAddLineToPoint (closure, x, y);
317     return CAIRO_STATUS_SUCCESS;
318 }
319
320 static cairo_status_t
321 _cairo_path_to_quartz_context_curve_to (void *closure,
322                                         const cairo_point_t *p0,
323                                         const cairo_point_t *p1,
324                                         const cairo_point_t *p2)
325 {
326     //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
327     //             _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
328     //             _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
329     //             _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
330     double x0 = _cairo_fixed_to_double (p0->x);
331     double y0 = _cairo_fixed_to_double (p0->y);
332     double x1 = _cairo_fixed_to_double (p1->x);
333     double y1 = _cairo_fixed_to_double (p1->y);
334     double x2 = _cairo_fixed_to_double (p2->x);
335     double y2 = _cairo_fixed_to_double (p2->y);
336
337     CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
338     return CAIRO_STATUS_SUCCESS;
339 }
340
341 static cairo_status_t
342 _cairo_path_to_quartz_context_close_path (void *closure)
343 {
344     //ND ((stderr, "closepath\n"));
345     CGContextClosePath (closure);
346     return CAIRO_STATUS_SUCCESS;
347 }
348
349 static void
350 _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
351                                             CGContextRef closure)
352 {
353     cairo_status_t status;
354
355     CGContextBeginPath (closure);
356     status = _cairo_path_fixed_interpret (path,
357                                           _cairo_path_to_quartz_context_move_to,
358                                           _cairo_path_to_quartz_context_line_to,
359                                           _cairo_path_to_quartz_context_curve_to,
360                                           _cairo_path_to_quartz_context_close_path,
361                                           closure);
362
363     assert (status == CAIRO_STATUS_SUCCESS);
364 }
365
366 /*
367  * Misc helpers/callbacks
368  */
369
370 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
371 static PrivateCGCompositeMode
372 _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
373 {
374     switch (op) {
375         case CAIRO_OPERATOR_CLEAR:
376             return kPrivateCGCompositeClear;
377         case CAIRO_OPERATOR_SOURCE:
378             return kPrivateCGCompositeCopy;
379         case CAIRO_OPERATOR_OVER:
380             return kPrivateCGCompositeSourceOver;
381         case CAIRO_OPERATOR_IN:
382             return kPrivateCGCompositeSourceIn;
383         case CAIRO_OPERATOR_OUT:
384             return kPrivateCGCompositeSourceOut;
385         case CAIRO_OPERATOR_ATOP:
386             return kPrivateCGCompositeSourceAtop;
387         case CAIRO_OPERATOR_DEST_OVER:
388             return kPrivateCGCompositeDestinationOver;
389         case CAIRO_OPERATOR_DEST_IN:
390             return kPrivateCGCompositeDestinationIn;
391         case CAIRO_OPERATOR_DEST_OUT:
392             return kPrivateCGCompositeDestinationOut;
393         case CAIRO_OPERATOR_DEST_ATOP:
394             return kPrivateCGCompositeDestinationAtop;
395         case CAIRO_OPERATOR_XOR:
396             return kPrivateCGCompositeXOR;
397         case CAIRO_OPERATOR_ADD:
398             return kPrivateCGCompositePlusLighter;
399
400         case CAIRO_OPERATOR_DEST:
401         case CAIRO_OPERATOR_SATURATE:
402         case CAIRO_OPERATOR_MULTIPLY:
403         case CAIRO_OPERATOR_SCREEN:
404         case CAIRO_OPERATOR_OVERLAY:
405         case CAIRO_OPERATOR_DARKEN:
406         case CAIRO_OPERATOR_LIGHTEN:
407         case CAIRO_OPERATOR_COLOR_DODGE:
408         case CAIRO_OPERATOR_COLOR_BURN:
409         case CAIRO_OPERATOR_HARD_LIGHT:
410         case CAIRO_OPERATOR_SOFT_LIGHT:
411         case CAIRO_OPERATOR_DIFFERENCE:
412         case CAIRO_OPERATOR_EXCLUSION:
413         case CAIRO_OPERATOR_HSL_HUE:
414         case CAIRO_OPERATOR_HSL_SATURATION:
415         case CAIRO_OPERATOR_HSL_COLOR:
416         case CAIRO_OPERATOR_HSL_LUMINOSITY:
417         default:
418             ASSERT_NOT_REACHED;
419     }
420 }
421 #endif
422
423 static CGBlendMode
424 _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
425 {
426     switch (op) {
427         case CAIRO_OPERATOR_MULTIPLY:
428             return kCGBlendModeMultiply;
429         case CAIRO_OPERATOR_SCREEN:
430             return kCGBlendModeScreen;
431         case CAIRO_OPERATOR_OVERLAY:
432             return kCGBlendModeOverlay;
433         case CAIRO_OPERATOR_DARKEN:
434             return kCGBlendModeDarken;
435         case CAIRO_OPERATOR_LIGHTEN:
436             return kCGBlendModeLighten;
437         case CAIRO_OPERATOR_COLOR_DODGE:
438             return kCGBlendModeColorDodge;
439         case CAIRO_OPERATOR_COLOR_BURN:
440             return kCGBlendModeColorBurn;
441         case CAIRO_OPERATOR_HARD_LIGHT:
442             return kCGBlendModeHardLight;
443         case CAIRO_OPERATOR_SOFT_LIGHT:
444             return kCGBlendModeSoftLight;
445         case CAIRO_OPERATOR_DIFFERENCE:
446             return kCGBlendModeDifference;
447         case CAIRO_OPERATOR_EXCLUSION:
448             return kCGBlendModeExclusion;
449         case CAIRO_OPERATOR_HSL_HUE:
450             return kCGBlendModeHue;
451         case CAIRO_OPERATOR_HSL_SATURATION:
452             return kCGBlendModeSaturation;
453         case CAIRO_OPERATOR_HSL_COLOR:
454             return kCGBlendModeColor;
455         case CAIRO_OPERATOR_HSL_LUMINOSITY:
456             return kCGBlendModeLuminosity;
457
458 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
459         case CAIRO_OPERATOR_CLEAR:
460             return kCGBlendModeClear;
461         case CAIRO_OPERATOR_SOURCE:
462             return kCGBlendModeCopy;
463         case CAIRO_OPERATOR_OVER:
464             return kCGBlendModeNormal;
465         case CAIRO_OPERATOR_IN:
466             return kCGBlendModeSourceIn;
467         case CAIRO_OPERATOR_OUT:
468             return kCGBlendModeSourceOut;
469         case CAIRO_OPERATOR_ATOP:
470             return kCGBlendModeSourceAtop;
471         case CAIRO_OPERATOR_DEST_OVER:
472             return kCGBlendModeDestinationOver;
473         case CAIRO_OPERATOR_DEST_IN:
474             return kCGBlendModeDestinationIn;
475         case CAIRO_OPERATOR_DEST_OUT:
476             return kCGBlendModeDestinationOut;
477         case CAIRO_OPERATOR_DEST_ATOP:
478             return kCGBlendModeDestinationAtop;
479         case CAIRO_OPERATOR_XOR:
480             return kCGBlendModeXOR;
481         case CAIRO_OPERATOR_ADD:
482             return kCGBlendModePlusLighter;
483 #else
484         case CAIRO_OPERATOR_CLEAR:
485         case CAIRO_OPERATOR_SOURCE:
486         case CAIRO_OPERATOR_OVER:
487         case CAIRO_OPERATOR_IN:
488         case CAIRO_OPERATOR_OUT:
489         case CAIRO_OPERATOR_ATOP:
490         case CAIRO_OPERATOR_DEST_OVER:
491         case CAIRO_OPERATOR_DEST_IN:
492         case CAIRO_OPERATOR_DEST_OUT:
493         case CAIRO_OPERATOR_DEST_ATOP:
494         case CAIRO_OPERATOR_XOR:
495         case CAIRO_OPERATOR_ADD:
496 #endif
497
498         case CAIRO_OPERATOR_DEST:
499         case CAIRO_OPERATOR_SATURATE:
500         default:
501             ASSERT_NOT_REACHED;
502     }
503 }
504
505 static cairo_int_status_t
506 _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
507 {
508     CGBlendMode blendmode;
509
510     assert (op != CAIRO_OPERATOR_DEST);
511
512     /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
513      * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
514      * uses the definition from the Adobe Supplement.
515      */
516     if (op == CAIRO_OPERATOR_SATURATE ||
517         op == CAIRO_OPERATOR_COLOR_DODGE ||
518         op == CAIRO_OPERATOR_COLOR_BURN)
519     {
520         return CAIRO_INT_STATUS_UNSUPPORTED;
521     }
522
523 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
524     if (op <= CAIRO_OPERATOR_ADD) {
525         PrivateCGCompositeMode compmode;
526
527         compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
528         CGContextSetCompositeOperation (context, compmode);
529         return CAIRO_STATUS_SUCCESS;
530     }
531 #endif
532
533     blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
534     CGContextSetBlendMode (context, blendmode);
535     return CAIRO_STATUS_SUCCESS;
536 }
537
538 static cairo_int_status_t
539 _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
540 {
541     ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
542
543     /* When the destination has no color components, we can avoid some
544      * fallbacks, but we have to workaround operators which behave
545      * differently in Quartz. */
546     if (surface->base.content == CAIRO_CONTENT_ALPHA) {
547         assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
548
549         if (op == CAIRO_OPERATOR_SOURCE ||
550             op == CAIRO_OPERATOR_IN ||
551             op == CAIRO_OPERATOR_OUT ||
552             op == CAIRO_OPERATOR_DEST_IN ||
553             op == CAIRO_OPERATOR_DEST_ATOP ||
554             op == CAIRO_OPERATOR_XOR)
555         {
556             return CAIRO_INT_STATUS_UNSUPPORTED;
557         }
558
559         if (op == CAIRO_OPERATOR_DEST_OVER)
560             op = CAIRO_OPERATOR_OVER;
561         else if (op == CAIRO_OPERATOR_SATURATE)
562             op = CAIRO_OPERATOR_ADD;
563         else if (op == CAIRO_OPERATOR_COLOR_DODGE)
564             op = CAIRO_OPERATOR_OVER;
565         else if (op == CAIRO_OPERATOR_COLOR_BURN)
566             op = CAIRO_OPERATOR_OVER;
567     }
568
569     return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
570 }
571
572 static inline CGLineCap
573 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
574 {
575     switch (ccap) {
576     default:
577         ASSERT_NOT_REACHED;
578
579     case CAIRO_LINE_CAP_BUTT:
580         return kCGLineCapButt;
581
582     case CAIRO_LINE_CAP_ROUND:
583         return kCGLineCapRound;
584
585     case CAIRO_LINE_CAP_SQUARE:
586         return kCGLineCapSquare;
587     }
588 }
589
590 static inline CGLineJoin
591 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
592 {
593     switch (cjoin) {
594     default:
595         ASSERT_NOT_REACHED;
596
597     case CAIRO_LINE_JOIN_MITER:
598         return kCGLineJoinMiter;
599
600     case CAIRO_LINE_JOIN_ROUND:
601         return kCGLineJoinRound;
602
603     case CAIRO_LINE_JOIN_BEVEL:
604         return kCGLineJoinBevel;
605     }
606 }
607
608 static inline CGInterpolationQuality
609 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
610 {
611     switch (filter) {
612     case CAIRO_FILTER_NEAREST:
613     case CAIRO_FILTER_FAST:
614         return kCGInterpolationNone;
615
616     case CAIRO_FILTER_BEST:
617     case CAIRO_FILTER_GOOD:
618     case CAIRO_FILTER_BILINEAR:
619     case CAIRO_FILTER_GAUSSIAN:
620         return kCGInterpolationDefault;
621
622     default:
623         ASSERT_NOT_REACHED;
624         return kCGInterpolationDefault;
625     }
626 }
627
628 static inline void
629 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
630                                       CGAffineTransform *dst)
631 {
632     dst->a = src->xx;
633     dst->b = src->yx;
634     dst->c = src->xy;
635     dst->d = src->yy;
636     dst->tx = src->x0;
637     dst->ty = src->y0;
638 }
639
640
641 /*
642  * Source -> Quartz setup and finish functions
643  */
644
645 static void
646 ComputeGradientValue (void *info,
647                       const cairo_quartz_float_t *in,
648                       cairo_quartz_float_t *out)
649 {
650     double fdist = *in;
651     const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
652     unsigned int i;
653
654     /* Put fdist back in the 0.0..1.0 range if we're doing
655      * REPEAT/REFLECT
656      */
657     if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
658         fdist = fdist - floor (fdist);
659     } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
660         fdist = fmod (fabs (fdist), 2.0);
661         if (fdist > 1.0)
662             fdist = 2.0 - fdist;
663     }
664
665     for (i = 0; i < grad->n_stops; i++)
666         if (grad->stops[i].offset > fdist)
667             break;
668
669     if (i == 0 || i == grad->n_stops) {
670         if (i == grad->n_stops)
671             --i;
672         out[0] = grad->stops[i].color.red;
673         out[1] = grad->stops[i].color.green;
674         out[2] = grad->stops[i].color.blue;
675         out[3] = grad->stops[i].color.alpha;
676     } else {
677         cairo_quartz_float_t ax = grad->stops[i-1].offset;
678         cairo_quartz_float_t bx = grad->stops[i].offset - ax;
679         cairo_quartz_float_t bp = (fdist - ax)/bx;
680         cairo_quartz_float_t ap = 1.0 - bp;
681
682         out[0] =
683             grad->stops[i-1].color.red * ap +
684             grad->stops[i].color.red * bp;
685         out[1] =
686             grad->stops[i-1].color.green * ap +
687             grad->stops[i].color.green * bp;
688         out[2] =
689             grad->stops[i-1].color.blue * ap +
690             grad->stops[i].color.blue * bp;
691         out[3] =
692             grad->stops[i-1].color.alpha * ap +
693             grad->stops[i].color.alpha * bp;
694     }
695 }
696
697 static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
698     0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
699 };
700 static const CGFunctionCallbacks gradient_callbacks = {
701     0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
702 };
703
704 /* Quartz computes a small number of samples of the gradient color
705  * function. On MacOS X 10.5 it apparently computes only 1024
706  * samples. */
707 #define MAX_GRADIENT_RANGE 1024
708
709 static CGFunctionRef
710 CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
711                                    const cairo_rectangle_int_t    *extents,
712                                    cairo_circle_double_t          *start,
713                                    cairo_circle_double_t          *end)
714 {
715     cairo_pattern_t *pat;
716     cairo_quartz_float_t input_value_range[2];
717
718     if (gradient->base.extend != CAIRO_EXTEND_NONE) {
719         double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
720         double t[2], tolerance;
721
722         tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
723         tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
724
725         bounds_x1 = extents->x;
726         bounds_y1 = extents->y;
727         bounds_x2 = extents->x + extents->width;
728         bounds_y2 = extents->y + extents->height;
729         _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
730                                               &bounds_x1, &bounds_y1,
731                                               &bounds_x2, &bounds_y2,
732                                               NULL);
733
734         _cairo_gradient_pattern_box_to_parameter (gradient,
735                                                   bounds_x1, bounds_y1,
736                                                   bounds_x2, bounds_y2,
737                                                   tolerance,
738                                                   t);
739
740         if (gradient->base.extend == CAIRO_EXTEND_PAD) {
741             t[0] = MAX (t[0], -0.5);
742             t[1] = MIN (t[1],  1.5);
743         } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
744             return NULL;
745
746         /* set the input range for the function -- the function knows how
747            to map values outside of 0.0 .. 1.0 to the correct color */
748         input_value_range[0] = t[0];
749         input_value_range[1] = t[1];
750     } else {
751         input_value_range[0] = 0;
752         input_value_range[1] = 1;
753     }
754
755     _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
756     _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
757
758     if (_cairo_pattern_create_copy (&pat, &gradient->base))
759         return NULL;
760
761     return CGFunctionCreate (pat,
762                              1,
763                              input_value_range,
764                              4,
765                              gradient_output_value_ranges,
766                              &gradient_callbacks);
767 }
768
769 /* Obtain a CGImageRef from a #cairo_surface_t * */
770
771 typedef struct {
772     cairo_surface_t *surface;
773     cairo_image_surface_t *image_out;
774     void *image_extra;
775 } quartz_source_image_t;
776
777 static void
778 DataProviderReleaseCallback (void *info, const void *data, size_t size)
779 {
780     quartz_source_image_t *source_img = info;
781     _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
782     free (source_img);
783 }
784
785 static cairo_status_t
786 _cairo_surface_to_cgimage (cairo_surface_t       *source,
787                            cairo_rectangle_int_t *extents,
788                            cairo_format_t         format,
789                            cairo_matrix_t        *matrix,
790                            const cairo_clip_t    *clip,
791                            CGImageRef            *image_out)
792 {
793     cairo_status_t status;
794     quartz_source_image_t *source_img;
795     cairo_image_surface_t *image_surface;
796
797     if (source->backend && source->backend->type == 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;
801     }
802
803     if (_cairo_surface_is_quartz (source)) {
804         cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
805         if (IS_EMPTY (surface)) {
806             *image_out = NULL;
807             return CAIRO_INT_STATUS_NOTHING_TO_DO;
808         }
809
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;
814         }
815     }
816
817     source_img = malloc (sizeof (quartz_source_image_t));
818     if (unlikely (source_img == NULL))
819         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
820
821     source_img->surface = source;
822
823     if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
824         image_surface = (cairo_image_surface_t *)
825             cairo_image_surface_create (format, extents->width, extents->height);
826         if (unlikely (image_surface->base.status)) {
827             status = image_surface->base.status;
828             cairo_surface_destroy (&image_surface->base);
829             free (source_img);
830             return status;
831         }
832
833         status = _cairo_recording_surface_replay_with_clip (source,
834                                                             matrix,
835                                                             &image_surface->base,
836                                                             NULL);
837         if (unlikely (status)) {
838             cairo_surface_destroy (&image_surface->base);
839             free (source_img);
840             return status;
841         }
842
843         source_img->image_out = image_surface;
844         source_img->image_extra = NULL;
845
846         cairo_matrix_init_identity (matrix);
847     }
848     else {
849         status = _cairo_surface_acquire_source_image (source_img->surface,
850                                                       &source_img->image_out,
851                                                       &source_img->image_extra);
852         if (unlikely (status)) {
853             free (source_img);
854             return status;
855         }
856     }
857
858     if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
859         *image_out = NULL;
860         DataProviderReleaseCallback (source_img,
861                                      source_img->image_out->data,
862                                      source_img->image_out->height * source_img->image_out->stride);
863     } else {
864         *image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
865                                                source_img->image_out->width,
866                                                source_img->image_out->height,
867                                                source_img->image_out->stride,
868                                                source_img->image_out->data,
869                                                TRUE,
870                                                NULL,
871                                                DataProviderReleaseCallback,
872                                                source_img);
873
874         /* TODO: differentiate memory error and unsupported surface type */
875         if (unlikely (*image_out == NULL))
876             status = CAIRO_INT_STATUS_UNSUPPORTED;
877     }
878
879     return status;
880 }
881
882 /* Generic #cairo_pattern_t -> CGPattern function */
883
884 typedef struct {
885     CGImageRef image;
886     CGRect imageBounds;
887     cairo_bool_t do_reflect;
888 } SurfacePatternDrawInfo;
889
890 static void
891 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
892 {
893     SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
894
895     CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
896     CGContextScaleCTM (context, 1, -1);
897
898     CGContextDrawImage (context, info->imageBounds, info->image);
899     if (info->do_reflect) {
900         /* draw 3 more copies of the image, flipped.
901          * DrawImage draws the image according to the current Y-direction into the rectangle given
902          * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
903          * of the base image position, and the Y axis is extending upwards.
904          */
905
906         /* Make the y axis extend downwards, and draw a flipped image below */
907         CGContextScaleCTM (context, 1, -1);
908         CGContextDrawImage (context, info->imageBounds, info->image);
909
910         /* Shift over to the right, and flip vertically (translation is 2x,
911          * since we'll be flipping and thus rendering the rectangle "backwards"
912          */
913         CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
914         CGContextScaleCTM (context, -1, 1);
915         CGContextDrawImage (context, info->imageBounds, info->image);
916
917         /* Then unflip the Y-axis again, and draw the image above the point. */
918         CGContextScaleCTM (context, 1, -1);
919         CGContextDrawImage (context, info->imageBounds, info->image);
920     }
921 }
922
923 static void
924 SurfacePatternReleaseInfoFunc (void *ainfo)
925 {
926     SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
927
928     CGImageRelease (info->image);
929     free (info);
930 }
931
932 static cairo_int_status_t
933 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
934                                                          const cairo_pattern_t *apattern,
935                                                          const cairo_clip_t *clip,
936                                                          CGPatternRef *cgpat)
937 {
938     cairo_surface_pattern_t *spattern;
939     cairo_surface_t *pat_surf;
940     cairo_rectangle_int_t extents;
941     cairo_format_t format = _cairo_format_from_content (dest->base.content);
942
943     CGImageRef image;
944     CGRect pbounds;
945     CGAffineTransform ptransform, stransform;
946     CGPatternCallbacks cb = { 0,
947                               SurfacePatternDrawFunc,
948                               SurfacePatternReleaseInfoFunc };
949     SurfacePatternDrawInfo *info;
950     cairo_quartz_float_t rw, rh;
951     cairo_status_t status;
952     cairo_bool_t is_bounded;
953
954     cairo_matrix_t m;
955
956     /* SURFACE is the only type we'll handle here */
957     assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
958
959     spattern = (cairo_surface_pattern_t *) apattern;
960     pat_surf = spattern->surface;
961
962     if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
963         is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
964         assert (is_bounded);
965     }
966     else
967         _cairo_surface_get_extents (&dest->base, &extents);
968
969     m = spattern->base.matrix;
970     status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
971                                         &m, clip, &image);
972     if (unlikely (status))
973         return status;
974
975     info = malloc (sizeof (SurfacePatternDrawInfo));
976     if (unlikely (!info))
977         return CAIRO_STATUS_NO_MEMORY;
978
979     /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
980      * that the data will stick around for this image when the printer gets to it.
981      * Otherwise, the underlying data store may disappear from under us!
982      *
983      * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
984      * since the Quartz surfaces have a higher chance of sticking around.  If the
985      * source is a quartz image surface, then it's set up to retain a ref to the
986      * image surface that it's backed by.
987      */
988     info->image = image;
989     info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
990     info->do_reflect = FALSE;
991
992     pbounds.origin.x = 0;
993     pbounds.origin.y = 0;
994
995     if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
996         pbounds.size.width = 2.0 * extents.width;
997         pbounds.size.height = 2.0 * extents.height;
998         info->do_reflect = TRUE;
999     } else {
1000         pbounds.size.width = extents.width;
1001         pbounds.size.height = extents.height;
1002     }
1003     rw = pbounds.size.width;
1004     rh = pbounds.size.height;
1005
1006     cairo_matrix_invert (&m);
1007     _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
1008
1009     /* The pattern matrix is relative to the bottom left, again; the
1010      * incoming cairo pattern matrix is relative to the upper left.
1011      * So we take the pattern matrix and the original context matrix,
1012      * which gives us the correct base translation/y flip.
1013      */
1014     ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
1015
1016 #ifdef QUARTZ_DEBUG
1017     ND ((stderr, "  pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
1018     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));
1019     CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
1020     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));
1021 #endif
1022
1023     *cgpat = CGPatternCreate (info,
1024                               pbounds,
1025                               ptransform,
1026                               rw, rh,
1027                               kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
1028                               TRUE,
1029                               &cb);
1030
1031     return CAIRO_STATUS_SUCCESS;
1032 }
1033
1034 /* State used during a drawing operation. */
1035 typedef struct {
1036     /* The destination of the mask */
1037     CGContextRef cgMaskContext;
1038
1039     /* The destination of the drawing of the source */
1040     CGContextRef cgDrawContext;
1041
1042     /* The filter to be used when drawing the source */
1043     CGInterpolationQuality filter;
1044
1045     /* Action type */
1046     cairo_quartz_action_t action;
1047
1048     /* Destination rect */
1049     CGRect rect;
1050
1051     /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1052     CGAffineTransform transform;
1053
1054     /* Used with DO_IMAGE and DO_TILED_IMAGE */
1055     CGImageRef image;
1056
1057     /* Used with DO_SHADING */
1058     CGShadingRef shading;
1059
1060     /* Temporary destination for unbounded operations */
1061     CGLayerRef layer;
1062     CGRect clipRect;
1063 } cairo_quartz_drawing_state_t;
1064
1065 /*
1066 Quartz does not support repeating radients. We handle repeating gradients
1067 by manually extending the gradient and repeating color stops. We need to
1068 minimize the number of repetitions since Quartz seems to sample our color
1069 function across the entire range, even if part of that range is not needed
1070 for the visible area of the gradient, and it samples with some fixed resolution,
1071 so if the gradient range is too large it samples with very low resolution and
1072 the gradient is very coarse. _cairo_quartz_create_gradient_function computes
1073 the number of repetitions needed based on the extents.
1074 */
1075 static cairo_int_status_t
1076 _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
1077                                      const cairo_gradient_pattern_t *gradient,
1078                                      const cairo_rectangle_int_t *extents)
1079 {
1080     cairo_matrix_t mat;
1081     cairo_circle_double_t start, end;
1082     CGFunctionRef gradFunc;
1083     CGColorSpaceRef rgb;
1084     bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
1085
1086     assert (gradient->n_stops > 0);
1087
1088     mat = gradient->base.matrix;
1089     cairo_matrix_invert (&mat);
1090     _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
1091
1092     gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
1093                                                   &start, &end);
1094
1095     if (unlikely (gradFunc == NULL))
1096         return CAIRO_INT_STATUS_UNSUPPORTED;
1097
1098     rgb = CGColorSpaceCreateDeviceRGB ();
1099
1100     if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
1101         state->shading = CGShadingCreateAxial (rgb,
1102                                                CGPointMake (start.center.x,
1103                                                             start.center.y),
1104                                                CGPointMake (end.center.x,
1105                                                             end.center.y),
1106                                                gradFunc,
1107                                                extend, extend);
1108     } else {
1109         state->shading = CGShadingCreateRadial (rgb,
1110                                                 CGPointMake (start.center.x,
1111                                                              start.center.y),
1112                                                 MAX (start.radius, 0),
1113                                                 CGPointMake (end.center.x,
1114                                                              end.center.y),
1115                                                 MAX (end.radius, 0),
1116                                                 gradFunc,
1117                                                 extend, extend);
1118     }
1119
1120     CGColorSpaceRelease (rgb);
1121     CGFunctionRelease (gradFunc);
1122
1123     state->action = DO_SHADING;
1124     return CAIRO_STATUS_SUCCESS;
1125 }
1126
1127 static cairo_int_status_t
1128 _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
1129                            cairo_composite_rectangles_t *composite)
1130 {
1131     cairo_quartz_surface_t       *surface = (cairo_quartz_surface_t *) composite->surface;
1132     cairo_operator_t              op = composite->op;
1133     const cairo_pattern_t        *source = &composite->source_pattern.base;
1134     const cairo_clip_t           *clip = composite->clip;
1135     cairo_bool_t needs_temp;
1136     cairo_status_t status;
1137     cairo_format_t format = _cairo_format_from_content (composite->surface->content);
1138
1139     state->layer = NULL;
1140     state->image = NULL;
1141     state->shading = NULL;
1142     state->cgDrawContext = NULL;
1143     state->cgMaskContext = NULL;
1144
1145     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1146     if (unlikely (status))
1147         return status;
1148
1149     status = _cairo_quartz_surface_set_cairo_operator (surface, op);
1150     if (unlikely (status))
1151         return status;
1152
1153     /* Save before we change the pattern, colorspace, etc. so that
1154      * we can restore and make sure that quartz releases our
1155      * pattern (which may be stack allocated)
1156      */
1157
1158     CGContextSaveGState (surface->cgContext);
1159     state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
1160     state->clipRect = CGRectIntegral (state->clipRect);
1161     state->rect = state->clipRect;
1162
1163     state->cgMaskContext = surface->cgContext;
1164     state->cgDrawContext = state->cgMaskContext;
1165
1166     state->filter = _cairo_quartz_filter_to_quartz (source->filter);
1167
1168     if (op == CAIRO_OPERATOR_CLEAR) {
1169         CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1170
1171         state->action = DO_DIRECT;
1172         return CAIRO_STATUS_SUCCESS;
1173     }
1174
1175     /*
1176      * To implement mask unbounded operations Quartz needs a temporary
1177      * surface which will be composited entirely (ignoring the mask).
1178      * To implement source unbounded operations Quartz needs a
1179      * temporary surface which allows extending the source to a size
1180      * covering the whole mask, but there are some optimization
1181      * opportunities:
1182      *
1183      * - CLEAR completely ignores the source, thus we can just use a
1184      *   solid color fill.
1185      *
1186      * - SOURCE can be implemented by drawing the source and clearing
1187      *   outside of the source as long as the two regions have no
1188      *   intersection. This happens when the source is a pixel-aligned
1189      *   rectangle. If the source is at least as big as the
1190      *   intersection between the clip rectangle and the mask
1191      *   rectangle, no clear operation is needed.
1192      */
1193     needs_temp = ! _cairo_operator_bounded_by_mask (op);
1194
1195     if (needs_temp) {
1196         state->layer = CGLayerCreateWithContext (surface->cgContext,
1197                                                  state->clipRect.size,
1198                                                  NULL);
1199         state->cgDrawContext = CGLayerGetContext (state->layer);
1200         state->cgMaskContext = state->cgDrawContext;
1201         CGContextTranslateCTM (state->cgDrawContext,
1202                                -state->clipRect.origin.x,
1203                                -state->clipRect.origin.y);
1204     }
1205
1206     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1207         cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1208
1209         CGContextSetRGBStrokeColor (state->cgDrawContext,
1210                                     solid->color.red,
1211                                     solid->color.green,
1212                                     solid->color.blue,
1213                                     solid->color.alpha);
1214         CGContextSetRGBFillColor (state->cgDrawContext,
1215                                   solid->color.red,
1216                                   solid->color.green,
1217                                   solid->color.blue,
1218                                   solid->color.alpha);
1219
1220         state->action = DO_DIRECT;
1221         return CAIRO_STATUS_SUCCESS;
1222     }
1223
1224     if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
1225         source->type == CAIRO_PATTERN_TYPE_RADIAL)
1226     {
1227         const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
1228         cairo_rectangle_int_t extents;
1229
1230         extents = surface->virtual_extents;
1231         extents.x -= surface->base.device_transform.x0;
1232         extents.y -= surface->base.device_transform.y0;
1233         _cairo_rectangle_union (&extents, &surface->extents);
1234
1235         return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
1236     }
1237
1238     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1239         (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1240     {
1241         const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1242         cairo_surface_t *pat_surf = spat->surface;
1243         CGImageRef img;
1244         cairo_matrix_t m = spat->base.matrix;
1245         cairo_rectangle_int_t extents;
1246         CGAffineTransform xform;
1247         CGRect srcRect;
1248         cairo_fixed_t fw, fh;
1249         cairo_bool_t is_bounded;
1250
1251         _cairo_surface_get_extents (composite->surface, &extents);
1252         status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
1253                                             &m, clip, &img);
1254         if (unlikely (status))
1255             return status;
1256
1257         state->image = img;
1258
1259         if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
1260             m.x0 = -ceil (m.x0 - 0.5);
1261             m.y0 = -ceil (m.y0 - 0.5);
1262         } else {
1263             cairo_matrix_invert (&m);
1264         }
1265
1266         _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
1267
1268         if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
1269             is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
1270             assert (is_bounded);
1271         }
1272
1273         srcRect = CGRectMake (0, 0, extents.width, extents.height);
1274
1275         if (source->extend == CAIRO_EXTEND_NONE) {
1276             int x, y;
1277             if (op == CAIRO_OPERATOR_SOURCE &&
1278                 (pat_surf->content == CAIRO_CONTENT_ALPHA ||
1279                  ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
1280             {
1281                 state->layer = CGLayerCreateWithContext (surface->cgContext,
1282                                                          state->clipRect.size,
1283                                                          NULL);
1284                 state->cgDrawContext = CGLayerGetContext (state->layer);
1285                 CGContextTranslateCTM (state->cgDrawContext,
1286                                        -state->clipRect.origin.x,
1287                                        -state->clipRect.origin.y);
1288             }
1289
1290             CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1291
1292             state->rect = srcRect;
1293             state->action = DO_IMAGE;
1294             return CAIRO_STATUS_SUCCESS;
1295         }
1296
1297         CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1298
1299         /* Quartz seems to tile images at pixel-aligned regions only -- this
1300          * leads to seams if the image doesn't end up scaling to fill the
1301          * space exactly.  The CGPattern tiling approach doesn't have this
1302          * problem.  Check if we're going to fill up the space (within some
1303          * epsilon), and if not, fall back to the CGPattern type.
1304          */
1305
1306         xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
1307                                          state->transform);
1308
1309         srcRect = CGRectApplyAffineTransform (srcRect, xform);
1310
1311         fw = _cairo_fixed_from_double (srcRect.size.width);
1312         fh = _cairo_fixed_from_double (srcRect.size.height);
1313
1314         if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1315             (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1316         {
1317             /* We're good to use DrawTiledImage, but ensure that
1318              * the math works out */
1319
1320             srcRect.size.width = round (srcRect.size.width);
1321             srcRect.size.height = round (srcRect.size.height);
1322
1323             xform = CGAffineTransformInvert (xform);
1324
1325             srcRect = CGRectApplyAffineTransform (srcRect, xform);
1326
1327             state->rect = srcRect;
1328             state->action = DO_TILED_IMAGE;
1329             return CAIRO_STATUS_SUCCESS;
1330         }
1331
1332         /* Fall through to generic SURFACE case */
1333     }
1334
1335     if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1336         cairo_quartz_float_t patternAlpha = 1.0f;
1337         CGColorSpaceRef patternSpace;
1338         CGPatternRef pattern = NULL;
1339         cairo_int_status_t status;
1340
1341         status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
1342         if (unlikely (status))
1343             return status;
1344
1345         patternSpace = CGColorSpaceCreatePattern (NULL);
1346         CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
1347         CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
1348         CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
1349         CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
1350         CGColorSpaceRelease (patternSpace);
1351
1352         /* Quartz likes to munge the pattern phase (as yet unexplained
1353          * why); force it to 0,0 as we've already baked in the correct
1354          * pattern translation into the pattern matrix
1355          */
1356         CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
1357
1358         CGPatternRelease (pattern);
1359
1360         state->action = DO_DIRECT;
1361         return CAIRO_STATUS_SUCCESS;
1362     }
1363
1364     return CAIRO_INT_STATUS_UNSUPPORTED;
1365 }
1366
1367 static void
1368 _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
1369                               cairo_composite_rectangles_t *extents)
1370 {
1371     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1372
1373     if (state->layer) {
1374         CGContextDrawLayerInRect (surface->cgContext,
1375                                   state->clipRect,
1376                                   state->layer);
1377         CGContextRelease (state->cgDrawContext);
1378         CGLayerRelease (state->layer);
1379     }
1380
1381     if (state->cgMaskContext)
1382         CGContextRestoreGState (surface->cgContext);
1383
1384     if (state->image)
1385         CGImageRelease (state->image);
1386
1387     if (state->shading)
1388         CGShadingRelease (state->shading);
1389 }
1390
1391 static void
1392 _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
1393                            cairo_operator_t              op)
1394 {
1395     CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
1396     CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
1397
1398     if (state->action == DO_DIRECT) {
1399         CGContextFillRect (state->cgDrawContext, state->rect);
1400         return;
1401     }
1402
1403     CGContextConcatCTM (state->cgDrawContext, state->transform);
1404
1405     if (state->action == DO_SHADING) {
1406         CGContextDrawShading (state->cgDrawContext, state->shading);
1407         return;
1408     }
1409
1410     CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1411     CGContextScaleCTM (state->cgDrawContext, 1, -1);
1412
1413     if (state->action == DO_IMAGE) {
1414         CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
1415         if (op == CAIRO_OPERATOR_SOURCE &&
1416             state->cgDrawContext == state->cgMaskContext)
1417         {
1418             CGContextBeginPath (state->cgDrawContext);
1419             CGContextAddRect (state->cgDrawContext, state->rect);
1420
1421             CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1422             CGContextScaleCTM (state->cgDrawContext, 1, -1);
1423             CGContextConcatCTM (state->cgDrawContext,
1424                                 CGAffineTransformInvert (state->transform));
1425
1426             CGContextAddRect (state->cgDrawContext, state->clipRect);
1427
1428             CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
1429             CGContextEOFillPath (state->cgDrawContext);
1430         }
1431     } else {
1432         CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
1433     }
1434 }
1435
1436 static cairo_image_surface_t *
1437 _cairo_quartz_surface_map_to_image (void *abstract_surface,
1438                                     const cairo_rectangle_int_t *extents)
1439 {
1440     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1441     unsigned int stride, bitinfo, bpp, color_comps;
1442     CGColorSpaceRef colorspace;
1443     void *imageData;
1444     cairo_format_t format;
1445
1446     if (surface->imageSurfaceEquiv)
1447         return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
1448
1449     if (IS_EMPTY (surface))
1450         return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1451
1452     if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
1453         return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1454
1455     bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1456     bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1457
1458     // let's hope they don't add YUV under us
1459     colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1460     color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
1461
1462     /* XXX TODO: We can handle many more data formats by
1463      * converting to pixman_format_t */
1464
1465     if (bpp == 32 && color_comps == 3 &&
1466         (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1467         (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1468     {
1469         format = CAIRO_FORMAT_ARGB32;
1470     }
1471     else if (bpp == 32 && color_comps == 3 &&
1472              (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1473              (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1474     {
1475         format = CAIRO_FORMAT_RGB24;
1476     }
1477     else if (bpp == 8 && color_comps == 1)
1478     {
1479         format = CAIRO_FORMAT_A1;
1480     }
1481     else
1482     {
1483         return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1484     }
1485
1486     imageData = CGBitmapContextGetData (surface->cgContext);
1487     stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1488
1489     return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
1490                                                                           format,
1491                                                                           extents->width,
1492                                                                           extents->height,
1493                                                                           stride);
1494 }
1495
1496 static cairo_int_status_t
1497 _cairo_quartz_surface_unmap_image (void *abstract_surface,
1498                                    cairo_image_surface_t *image)
1499 {
1500     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1501
1502     if (surface->imageSurfaceEquiv)
1503         return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
1504
1505     cairo_surface_finish (&image->base);
1506     cairo_surface_destroy (&image->base);
1507
1508     return CAIRO_STATUS_SUCCESS;
1509 }
1510
1511
1512 /*
1513  * Cairo surface backend implementations
1514  */
1515
1516 static cairo_status_t
1517 _cairo_quartz_surface_finish (void *abstract_surface)
1518 {
1519     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1520
1521     ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1522
1523     if (IS_EMPTY (surface))
1524         return CAIRO_STATUS_SUCCESS;
1525
1526     /* Restore our saved gstate that we use to reset clipping */
1527     CGContextRestoreGState (surface->cgContext);
1528     _cairo_surface_clipper_reset (&surface->clipper);
1529
1530     CGContextRelease (surface->cgContext);
1531
1532     surface->cgContext = NULL;
1533
1534     if (surface->imageSurfaceEquiv) {
1535         cairo_surface_destroy (surface->imageSurfaceEquiv);
1536         surface->imageSurfaceEquiv = NULL;
1537     }
1538
1539     free (surface->imageData);
1540     surface->imageData = NULL;
1541
1542     return CAIRO_STATUS_SUCCESS;
1543 }
1544
1545 static cairo_status_t
1546 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1547                                              cairo_image_surface_t **image_out,
1548                                              void **image_extra)
1549 {
1550     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1551
1552     //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1553
1554     *image_extra = NULL;
1555
1556     *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
1557     if (unlikely (cairo_surface_status(&(*image_out)->base))) {
1558         cairo_surface_destroy (&(*image_out)->base);
1559         *image_out = NULL;
1560         return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1561     }
1562
1563     return CAIRO_STATUS_SUCCESS;
1564 }
1565
1566 static void
1567 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1568                                             cairo_image_surface_t *image,
1569                                             void *image_extra)
1570 {
1571     _cairo_quartz_surface_unmap_image (abstract_surface, image);
1572 }
1573
1574 static cairo_surface_t *
1575 _cairo_quartz_surface_create_similar (void *abstract_surface,
1576                                       cairo_content_t content,
1577                                       int width,
1578                                       int height)
1579 {
1580     cairo_quartz_surface_t *surface, *similar_quartz;
1581     cairo_surface_t *similar;
1582     cairo_format_t format;
1583
1584     if (content == CAIRO_CONTENT_COLOR_ALPHA)
1585         format = CAIRO_FORMAT_ARGB32;
1586     else if (content == CAIRO_CONTENT_COLOR)
1587         format = CAIRO_FORMAT_RGB24;
1588     else if (content == CAIRO_CONTENT_ALPHA)
1589         format = CAIRO_FORMAT_A8;
1590     else
1591         return NULL;
1592
1593     // verify width and height of surface
1594     if (!_cairo_quartz_verify_surface_size (width, height)) {
1595         return _cairo_surface_create_in_error (_cairo_error
1596                                                (CAIRO_STATUS_INVALID_SIZE));
1597     }
1598
1599     similar = cairo_quartz_surface_create (format, width, height);
1600     if (unlikely (similar->status))
1601         return similar;
1602
1603     surface = (cairo_quartz_surface_t *) abstract_surface;
1604     similar_quartz = (cairo_quartz_surface_t *) similar;
1605     similar_quartz->virtual_extents = surface->virtual_extents;
1606
1607     return similar;
1608 }
1609
1610 static cairo_bool_t
1611 _cairo_quartz_surface_get_extents (void *abstract_surface,
1612                                    cairo_rectangle_int_t *extents)
1613 {
1614     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1615
1616     *extents = surface->extents;
1617     return TRUE;
1618 }
1619
1620 static cairo_int_status_t
1621 _cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
1622                         cairo_composite_rectangles_t *extents)
1623 {
1624     cairo_quartz_drawing_state_t state;
1625     cairo_int_status_t rv;
1626
1627     ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
1628          extents->surface, extents->op, extents->source_pattern.base.type));
1629
1630     rv = _cairo_quartz_setup_state (&state, extents);
1631     if (unlikely (rv))
1632         goto BAIL;
1633
1634     _cairo_quartz_draw_source (&state, extents->op);
1635
1636 BAIL:
1637     _cairo_quartz_teardown_state (&state, extents);
1638
1639     ND ((stderr, "-- paint\n"));
1640     return rv;
1641 }
1642
1643 static cairo_int_status_t
1644 _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
1645                                     cairo_surface_t              *mask_surf,
1646                                     const cairo_matrix_t         *mask_mat,
1647                                     CGInterpolationQuality        filter)
1648 {
1649     CGRect rect;
1650     CGImageRef img;
1651     cairo_status_t status;
1652     CGAffineTransform mask_matrix;
1653     cairo_quartz_drawing_state_t state;
1654     cairo_format_t format = _cairo_format_from_content (extents->surface->content);
1655     cairo_rectangle_int_t dest_extents;
1656     cairo_matrix_t m = *mask_mat;
1657
1658     _cairo_surface_get_extents (extents->surface, &dest_extents);
1659     status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
1660                                         &m, extents->clip, &img);
1661     if (unlikely (status))
1662         return status;
1663
1664     status = _cairo_quartz_setup_state (&state, extents);
1665     if (unlikely (status))
1666         goto BAIL;
1667
1668     rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
1669     _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
1670
1671     /* ClipToMask is essentially drawing an image, so we need to flip the CTM
1672      * to get the image to appear oriented the right way */
1673     CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
1674     CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
1675     CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1676
1677     state.filter = filter;
1678
1679     CGContextSetInterpolationQuality (state.cgMaskContext, filter);
1680     CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
1681
1682     CGContextClipToMask (state.cgMaskContext, rect, img);
1683
1684     CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1685     CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
1686     CGContextConcatCTM (state.cgMaskContext, mask_matrix);
1687
1688     _cairo_quartz_draw_source (&state, extents->op);
1689
1690 BAIL:
1691     _cairo_quartz_teardown_state (&state, extents);
1692
1693     CGImageRelease (img);
1694
1695     return status;
1696 }
1697
1698 static cairo_int_status_t
1699 _cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
1700                                   cairo_composite_rectangles_t *extents)
1701 {
1702     cairo_quartz_drawing_state_t state;
1703     double alpha = extents->mask_pattern.solid.color.alpha;
1704     cairo_status_t status;
1705
1706     status = _cairo_quartz_setup_state (&state, extents);
1707     if (unlikely (status))
1708         return status;
1709
1710     CGContextSetAlpha (surface->cgContext, alpha);
1711     _cairo_quartz_draw_source (&state, extents->op);
1712
1713     _cairo_quartz_teardown_state (&state, extents);
1714
1715     return CAIRO_STATUS_SUCCESS;
1716 }
1717
1718 static cairo_int_status_t
1719 _cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
1720                        cairo_composite_rectangles_t *extents)
1721 {
1722     cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
1723     const cairo_pattern_t *source = &extents->source_pattern.base;
1724     const cairo_pattern_t *mask = &extents->mask_pattern.base;
1725     cairo_surface_t *mask_surf;
1726     cairo_matrix_t matrix;
1727     cairo_status_t status;
1728     cairo_bool_t need_temp;
1729     CGInterpolationQuality filter;
1730
1731     ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
1732          extents->surface, extents->op, extents->source_pattern.base.type,
1733          extents->mask_pattern.base.type));
1734
1735     if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
1736         return _cairo_quartz_cg_mask_with_solid (surface, extents);
1737
1738     need_temp = (mask->type   != CAIRO_PATTERN_TYPE_SURFACE ||
1739                  mask->extend != CAIRO_EXTEND_NONE);
1740
1741     filter = _cairo_quartz_filter_to_quartz (source->filter);
1742
1743     if (! need_temp) {
1744         mask_surf = extents->mask_pattern.surface.surface;
1745
1746         /* When an opaque surface used as a mask in Quartz, its
1747          * luminosity is used as the alpha value, so we con only use
1748          * surfaces with alpha without creating a temporary mask. */
1749         need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
1750     }
1751
1752     if (! need_temp) {
1753         CGInterpolationQuality mask_filter;
1754         cairo_bool_t simple_transform;
1755
1756         matrix = mask->matrix;
1757
1758         mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
1759         if (mask_filter == kCGInterpolationNone) {
1760             simple_transform = _cairo_matrix_is_translation (&matrix);
1761             if (simple_transform) {
1762                 matrix.x0 = ceil (matrix.x0 - 0.5);
1763                 matrix.y0 = ceil (matrix.y0 - 0.5);
1764             }
1765         } else {
1766             simple_transform = _cairo_matrix_is_integer_translation (&matrix,
1767                                                                      NULL,
1768                                                                      NULL);
1769         }
1770
1771         /* Quartz only allows one interpolation to be set for mask and
1772          * source, so we can skip the temp surface only if the source
1773          * filtering makes the mask look correct. */
1774         if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
1775             need_temp = ! (simple_transform || filter == mask_filter);
1776         else
1777             filter = mask_filter;
1778     }
1779
1780     if (need_temp) {
1781         /* Render the mask to a surface */
1782         mask_surf = _cairo_quartz_surface_create_similar (surface,
1783                                                           CAIRO_CONTENT_ALPHA,
1784                                                           surface->extents.width,
1785                                                           surface->extents.height);
1786         status = mask_surf->status;
1787         if (unlikely (status))
1788             goto BAIL;
1789
1790         /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
1791          * temporary layer or fallback to cairo-image. */
1792         status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
1793         if (unlikely (status))
1794             goto BAIL;
1795
1796         cairo_matrix_init_identity (&matrix);
1797     }
1798
1799     status = _cairo_quartz_cg_mask_with_surface (extents,
1800                                                  mask_surf, &matrix, filter);
1801
1802 BAIL:
1803
1804     if (need_temp)
1805         cairo_surface_destroy (mask_surf);
1806
1807     return status;
1808 }
1809
1810 static cairo_int_status_t
1811 _cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
1812                        cairo_composite_rectangles_t *extents,
1813                        const cairo_path_fixed_t *path,
1814                        cairo_fill_rule_t fill_rule,
1815                        double tolerance,
1816                        cairo_antialias_t antialias)
1817 {
1818     cairo_quartz_drawing_state_t state;
1819     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1820
1821     ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
1822          extents->surface, extents->op, extents->source_pattern.base.type));
1823
1824     rv = _cairo_quartz_setup_state (&state, extents);
1825     if (unlikely (rv))
1826         goto BAIL;
1827
1828     CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1829
1830     _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1831
1832     if (state.action == DO_DIRECT) {
1833         assert (state.cgDrawContext == state.cgMaskContext);
1834         if (fill_rule == CAIRO_FILL_RULE_WINDING)
1835             CGContextFillPath (state.cgMaskContext);
1836         else
1837             CGContextEOFillPath (state.cgMaskContext);
1838     } else {
1839         if (fill_rule == CAIRO_FILL_RULE_WINDING)
1840             CGContextClip (state.cgMaskContext);
1841         else
1842             CGContextEOClip (state.cgMaskContext);
1843
1844         _cairo_quartz_draw_source (&state, extents->op);
1845     }
1846
1847 BAIL:
1848     _cairo_quartz_teardown_state (&state, extents);
1849
1850     ND ((stderr, "-- fill\n"));
1851     return rv;
1852 }
1853
1854 static cairo_int_status_t
1855 _cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
1856                          cairo_composite_rectangles_t *extents,
1857                          const cairo_path_fixed_t *path,
1858                          const cairo_stroke_style_t *style,
1859                          const cairo_matrix_t *ctm,
1860                          const cairo_matrix_t *ctm_inverse,
1861                          double tolerance,
1862                          cairo_antialias_t antialias)
1863 {
1864     cairo_quartz_drawing_state_t state;
1865     cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1866     CGAffineTransform strokeTransform, invStrokeTransform;
1867
1868     ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
1869          extents->surface, extents->op, extents->source_pattern.base.type));
1870
1871     rv = _cairo_quartz_setup_state (&state, extents);
1872     if (unlikely (rv))
1873         goto BAIL;
1874
1875     // Turning antialiasing off used to cause misrendering with
1876     // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1877     // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1878     CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1879     CGContextSetLineWidth (state.cgMaskContext, style->line_width);
1880     CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1881     CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1882     CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
1883
1884     if (style->dash && style->num_dashes) {
1885         cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
1886         cairo_quartz_float_t *fdash = sdash;
1887         unsigned int max_dashes = style->num_dashes;
1888         unsigned int k;
1889
1890         if (style->num_dashes%2)
1891             max_dashes *= 2;
1892         if (max_dashes > ARRAY_LENGTH (sdash))
1893             fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
1894         if (unlikely (fdash == NULL)) {
1895             rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1896             goto BAIL;
1897         }
1898
1899         for (k = 0; k < max_dashes; k++)
1900             fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
1901
1902         CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
1903         if (fdash != sdash)
1904             free (fdash);
1905     } else
1906         CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
1907
1908     _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1909
1910     _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1911     CGContextConcatCTM (state.cgMaskContext, strokeTransform);
1912
1913     if (state.action == DO_DIRECT) {
1914         assert (state.cgDrawContext == state.cgMaskContext);
1915         CGContextStrokePath (state.cgMaskContext);
1916     } else {
1917         CGContextReplacePathWithStrokedPath (state.cgMaskContext);
1918         CGContextClip (state.cgMaskContext);
1919
1920         _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
1921         CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
1922
1923         _cairo_quartz_draw_source (&state, extents->op);
1924     }
1925
1926 BAIL:
1927     _cairo_quartz_teardown_state (&state, extents);
1928
1929     ND ((stderr, "-- stroke\n"));
1930     return rv;
1931 }
1932
1933 #if CAIRO_HAS_QUARTZ_FONT
1934 static cairo_int_status_t
1935 _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
1936                          cairo_composite_rectangles_t *extents,
1937                          cairo_scaled_font_t *scaled_font,
1938                          cairo_glyph_t *glyphs,
1939                          int num_glyphs,
1940                          cairo_bool_t overlap)
1941 {
1942     CGAffineTransform textTransform, invTextTransform;
1943     CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1944     CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1945     CGGlyph *cg_glyphs = &glyphs_static[0];
1946     CGSize *cg_advances = &cg_advances_static[0];
1947     COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
1948
1949     cairo_quartz_drawing_state_t state;
1950     cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
1951     cairo_quartz_float_t xprev, yprev;
1952     int i;
1953     CGFontRef cgfref = NULL;
1954
1955     cairo_bool_t didForceFontSmoothing = FALSE;
1956
1957     if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
1958         return CAIRO_INT_STATUS_UNSUPPORTED;
1959
1960     rv = _cairo_quartz_setup_state (&state, extents);
1961     if (unlikely (rv))
1962         goto BAIL;
1963
1964     if (state.action == DO_DIRECT) {
1965         assert (state.cgDrawContext == state.cgMaskContext);
1966         CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
1967     } else {
1968         CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
1969     }
1970
1971     /* this doesn't addref */
1972     cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1973     CGContextSetFont (state.cgMaskContext, cgfref);
1974     CGContextSetFontSize (state.cgMaskContext, 1.0);
1975
1976     switch (scaled_font->options.antialias) {
1977         case CAIRO_ANTIALIAS_SUBPIXEL:
1978         case CAIRO_ANTIALIAS_BEST:
1979             CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1980             CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
1981             if (CGContextSetAllowsFontSmoothingPtr &&
1982                 !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
1983             {
1984                 didForceFontSmoothing = TRUE;
1985                 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
1986             }
1987             break;
1988         case CAIRO_ANTIALIAS_NONE:
1989             CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
1990             break;
1991         case CAIRO_ANTIALIAS_GRAY:
1992         case CAIRO_ANTIALIAS_GOOD:
1993         case CAIRO_ANTIALIAS_FAST:
1994             CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1995             CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
1996             break;
1997         case CAIRO_ANTIALIAS_DEFAULT:
1998             /* Don't do anything */
1999             break;
2000     }
2001
2002     if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
2003         cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
2004         if (unlikely (cg_glyphs == NULL)) {
2005             rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2006             goto BAIL;
2007         }
2008
2009         cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
2010     }
2011
2012     /* scale(1,-1) * scaled_font->scale */
2013     textTransform = CGAffineTransformMake (scaled_font->scale.xx,
2014                                            scaled_font->scale.yx,
2015                                            -scaled_font->scale.xy,
2016                                            -scaled_font->scale.yy,
2017                                            0.0, 0.0);
2018
2019     /* scaled_font->scale_inverse * scale(1,-1) */
2020     invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
2021                                               -scaled_font->scale_inverse.yx,
2022                                               scaled_font->scale_inverse.xy,
2023                                               -scaled_font->scale_inverse.yy,
2024                                               0.0, 0.0);
2025
2026     CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
2027     CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
2028
2029     /* Convert our glyph positions to glyph advances.  We need n-1 advances,
2030      * since the advance at index 0 is applied after glyph 0. */
2031     xprev = glyphs[0].x;
2032     yprev = glyphs[0].y;
2033
2034     cg_glyphs[0] = glyphs[0].index;
2035
2036     for (i = 1; i < num_glyphs; i++) {
2037         cairo_quartz_float_t xf = glyphs[i].x;
2038         cairo_quartz_float_t yf = glyphs[i].y;
2039         cg_glyphs[i] = glyphs[i].index;
2040         cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
2041         xprev = xf;
2042         yprev = yf;
2043     }
2044
2045     /* Translate to the first glyph's position before drawing */
2046     CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
2047     CGContextConcatCTM (state.cgMaskContext, textTransform);
2048
2049     CGContextShowGlyphsWithAdvances (state.cgMaskContext,
2050                                      cg_glyphs,
2051                                      cg_advances,
2052                                      num_glyphs);
2053
2054     CGContextConcatCTM (state.cgMaskContext, invTextTransform);
2055     CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
2056
2057     if (state.action != DO_DIRECT)
2058         _cairo_quartz_draw_source (&state, extents->op);
2059
2060 BAIL:
2061     if (didForceFontSmoothing)
2062         CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
2063
2064     _cairo_quartz_teardown_state (&state, extents);
2065
2066     if (cg_glyphs != glyphs_static)
2067         free (cg_glyphs);
2068
2069     return rv;
2070 }
2071 #endif /* CAIRO_HAS_QUARTZ_FONT */
2072
2073 static const cairo_compositor_t _cairo_quartz_cg_compositor = {
2074     &_cairo_fallback_compositor,
2075
2076     _cairo_quartz_cg_paint,
2077     _cairo_quartz_cg_mask,
2078     _cairo_quartz_cg_stroke,
2079     _cairo_quartz_cg_fill,
2080 #if CAIRO_HAS_QUARTZ_FONT
2081     _cairo_quartz_cg_glyphs,
2082 #else
2083     NULL,
2084 #endif
2085 };
2086
2087 static cairo_int_status_t
2088 _cairo_quartz_surface_paint (void *surface,
2089                              cairo_operator_t op,
2090                              const cairo_pattern_t *source,
2091                              const cairo_clip_t *clip)
2092 {
2093     return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
2094                                     surface, op, source, clip);
2095 }
2096
2097 static cairo_int_status_t
2098 _cairo_quartz_surface_mask (void *surface,
2099                             cairo_operator_t op,
2100                             const cairo_pattern_t *source,
2101                             const cairo_pattern_t *mask,
2102                             const cairo_clip_t *clip)
2103 {
2104     return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
2105                                    surface, op, source, mask,
2106                                    clip);
2107 }
2108
2109 static cairo_int_status_t
2110 _cairo_quartz_surface_fill (void *surface,
2111                             cairo_operator_t op,
2112                             const cairo_pattern_t *source,
2113                             const cairo_path_fixed_t *path,
2114                             cairo_fill_rule_t fill_rule,
2115                             double tolerance,
2116                             cairo_antialias_t antialias,
2117                             const cairo_clip_t *clip)
2118 {
2119     return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
2120                                    surface, op, source, path,
2121                                    fill_rule, tolerance, antialias,
2122                                    clip);
2123 }
2124
2125 static cairo_int_status_t
2126 _cairo_quartz_surface_stroke (void *surface,
2127                               cairo_operator_t op,
2128                               const cairo_pattern_t *source,
2129                               const cairo_path_fixed_t *path,
2130                               const cairo_stroke_style_t *style,
2131                               const cairo_matrix_t *ctm,
2132                               const cairo_matrix_t *ctm_inverse,
2133                               double tolerance,
2134                               cairo_antialias_t antialias,
2135                               const cairo_clip_t *clip)
2136 {
2137     return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
2138                                      surface, op, source, path,
2139                                      style, ctm,ctm_inverse,
2140                                      tolerance, antialias, clip);
2141 }
2142
2143 static cairo_int_status_t
2144 _cairo_quartz_surface_glyphs (void *surface,
2145                               cairo_operator_t op,
2146                               const cairo_pattern_t *source,
2147                               cairo_glyph_t *glyphs,
2148                               int num_glyphs,
2149                               cairo_scaled_font_t *scaled_font,
2150                               const cairo_clip_t *clip)
2151 {
2152     return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
2153                                      surface, op, source,
2154                                      glyphs, num_glyphs, scaled_font,
2155                                      clip);
2156 }
2157
2158 static cairo_status_t
2159 _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
2160                                                    cairo_path_fixed_t *path,
2161                                                    cairo_fill_rule_t fill_rule,
2162                                                    double tolerance,
2163                                                    cairo_antialias_t antialias)
2164 {
2165     cairo_quartz_surface_t *surface =
2166         cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
2167
2168     ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2169
2170     if (IS_EMPTY (surface))
2171         return CAIRO_STATUS_SUCCESS;
2172
2173     if (path == NULL) {
2174         /* If we're being asked to reset the clip, we can only do it
2175          * by restoring the gstate to our previous saved one, and
2176          * saving it again.
2177          *
2178          * Note that this assumes that ALL quartz surface creation
2179          * functions will do a SaveGState first; we do this in create_internal.
2180          */
2181         CGContextRestoreGState (surface->cgContext);
2182         CGContextSaveGState (surface->cgContext);
2183     } else {
2184         CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2185
2186         _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
2187
2188         if (fill_rule == CAIRO_FILL_RULE_WINDING)
2189             CGContextClip (surface->cgContext);
2190         else
2191             CGContextEOClip (surface->cgContext);
2192     }
2193
2194     ND ((stderr, "-- intersect_clip_path\n"));
2195
2196     return CAIRO_STATUS_SUCCESS;
2197 }
2198
2199 // XXXtodo implement show_page; need to figure out how to handle begin/end
2200
2201 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2202     CAIRO_SURFACE_TYPE_QUARTZ,
2203     _cairo_quartz_surface_finish,
2204
2205     _cairo_default_context_create,
2206
2207     _cairo_quartz_surface_create_similar,
2208     NULL, /* similar image */
2209     _cairo_quartz_surface_map_to_image,
2210     _cairo_quartz_surface_unmap_image,
2211
2212     _cairo_surface_default_source,
2213     _cairo_quartz_surface_acquire_source_image,
2214     _cairo_quartz_surface_release_source_image,
2215     NULL, /* snapshot */
2216
2217     NULL, /* copy_page */
2218     NULL, /* show_page */
2219
2220     _cairo_quartz_surface_get_extents,
2221     NULL, /* get_font_options */
2222
2223     NULL, /* flush */
2224     NULL, /* mark_dirty_rectangle */
2225
2226     _cairo_quartz_surface_paint,
2227     _cairo_quartz_surface_mask,
2228     _cairo_quartz_surface_stroke,
2229     _cairo_quartz_surface_fill,
2230     NULL,  /* fill-stroke */
2231     _cairo_quartz_surface_glyphs,
2232 };
2233
2234 cairo_quartz_surface_t *
2235 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2236                                        cairo_content_t content,
2237                                        unsigned int width,
2238                                        unsigned int height)
2239 {
2240     cairo_quartz_surface_t *surface;
2241
2242     quartz_ensure_symbols ();
2243
2244     /* Init the base surface */
2245     surface = malloc (sizeof (cairo_quartz_surface_t));
2246     if (unlikely (surface == NULL))
2247         return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2248
2249     memset (surface, 0, sizeof (cairo_quartz_surface_t));
2250
2251     _cairo_surface_init (&surface->base,
2252                          &cairo_quartz_surface_backend,
2253                          NULL, /* device */
2254                          content);
2255
2256     _cairo_surface_clipper_init (&surface->clipper,
2257                                  _cairo_quartz_surface_clipper_intersect_clip_path);
2258
2259     /* Save our extents */
2260     surface->extents.x = surface->extents.y = 0;
2261     surface->extents.width = width;
2262     surface->extents.height = height;
2263     surface->virtual_extents = surface->extents;
2264
2265     if (IS_EMPTY (surface)) {
2266         surface->cgContext = NULL;
2267         surface->cgContextBaseCTM = CGAffineTransformIdentity;
2268         surface->imageData = NULL;
2269         surface->base.is_clear = TRUE;
2270         return surface;
2271     }
2272
2273     /* Save so we can always get back to a known-good CGContext -- this is
2274      * required for proper behaviour of intersect_clip_path(NULL)
2275      */
2276     CGContextSaveGState (cgContext);
2277
2278     surface->cgContext = cgContext;
2279     surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2280
2281     surface->imageData = NULL;
2282     surface->imageSurfaceEquiv = NULL;
2283
2284     return surface;
2285 }
2286
2287 /**
2288  * cairo_quartz_surface_create_for_cg_context:
2289  * @cgContext: the existing CGContext for which to create the surface
2290  * @width: width of the surface, in pixels
2291  * @height: height of the surface, in pixels
2292  *
2293  * Creates a Quartz surface that wraps the given CGContext.  The
2294  * CGContext is assumed to be in the standard Cairo coordinate space
2295  * (that is, with the origin at the upper left and the Y axis
2296  * increasing downward).  If the CGContext is in the Quartz coordinate
2297  * space (with the origin at the bottom left), then it should be
2298  * flipped before this function is called.  The flip can be accomplished
2299  * using a translate and a scale; for example:
2300  *
2301  * <informalexample><programlisting>
2302  * CGContextTranslateCTM (cgContext, 0.0, height);
2303  * CGContextScaleCTM (cgContext, 1.0, -1.0);
2304  * </programlisting></informalexample>
2305  *
2306  * All Cairo operations are implemented in terms of Quartz operations,
2307  * as long as Quartz-compatible elements are used (such as Quartz fonts).
2308  *
2309  * Return value: the newly created Cairo surface.
2310  *
2311  * Since: 1.6
2312  **/
2313
2314 cairo_surface_t *
2315 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2316                                             unsigned int width,
2317                                             unsigned int height)
2318 {
2319     cairo_quartz_surface_t *surf;
2320
2321     surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2322                                                   width, height);
2323     if (likely (!surf->base.status))
2324         CGContextRetain (cgContext);
2325
2326     return &surf->base;
2327 }
2328
2329 /**
2330  * cairo_quartz_surface_create:
2331  * @format: format of pixels in the surface to create
2332  * @width: width of the surface, in pixels
2333  * @height: height of the surface, in pixels
2334  *
2335  * Creates a Quartz surface backed by a CGBitmap.  The surface is
2336  * created using the Device RGB (or Device Gray, for A8) color space.
2337  * All Cairo operations, including those that require software
2338  * rendering, will succeed on this surface.
2339  *
2340  * Return value: the newly created surface.
2341  *
2342  * Since: 1.6
2343  **/
2344 cairo_surface_t *
2345 cairo_quartz_surface_create (cairo_format_t format,
2346                              unsigned int width,
2347                              unsigned int height)
2348 {
2349     cairo_quartz_surface_t *surf;
2350     CGContextRef cgc;
2351     CGColorSpaceRef cgColorspace;
2352     CGBitmapInfo bitinfo;
2353     void *imageData;
2354     int stride;
2355     int bitsPerComponent;
2356
2357     if (!_cairo_quartz_verify_surface_size (width, height))
2358         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2359
2360     if (width == 0 || height == 0) {
2361         return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2362                                                        width, height)->base;
2363     }
2364
2365     if (format == CAIRO_FORMAT_ARGB32 ||
2366         format == CAIRO_FORMAT_RGB24)
2367     {
2368         cgColorspace = CGColorSpaceCreateDeviceRGB ();
2369         bitinfo = kCGBitmapByteOrder32Host;
2370         if (format == CAIRO_FORMAT_ARGB32)
2371             bitinfo |= kCGImageAlphaPremultipliedFirst;
2372         else
2373             bitinfo |= kCGImageAlphaNoneSkipFirst;
2374         bitsPerComponent = 8;
2375         stride = width * 4;
2376     } else if (format == CAIRO_FORMAT_A8) {
2377         cgColorspace = NULL;
2378         stride = width;
2379         bitinfo = kCGImageAlphaOnly;
2380         bitsPerComponent = 8;
2381     } else if (format == CAIRO_FORMAT_A1) {
2382         /* I don't think we can usefully support this, as defined by
2383          * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2384          * quantities.
2385          */
2386         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2387     } else {
2388         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2389     }
2390
2391     /* The Apple docs say that for best performance, the stride and the data
2392      * pointer should be 16-byte aligned.  malloc already aligns to 16-bytes,
2393      * so we don't have to anything special on allocation.
2394      */
2395     stride = (stride + 15) & ~15;
2396
2397     imageData = _cairo_malloc_ab (height, stride);
2398     if (unlikely (!imageData)) {
2399         CGColorSpaceRelease (cgColorspace);
2400         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2401     }
2402
2403     /* zero the memory to match the image surface behaviour */
2404     memset (imageData, 0, height * stride);
2405
2406     cgc = CGBitmapContextCreate (imageData,
2407                                  width,
2408                                  height,
2409                                  bitsPerComponent,
2410                                  stride,
2411                                  cgColorspace,
2412                                  bitinfo);
2413     CGColorSpaceRelease (cgColorspace);
2414
2415     if (!cgc) {
2416         free (imageData);
2417         return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2418     }
2419
2420     /* flip the Y axis */
2421     CGContextTranslateCTM (cgc, 0.0, height);
2422     CGContextScaleCTM (cgc, 1.0, -1.0);
2423
2424     surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2425                                                   width, height);
2426     if (surf->base.status) {
2427         CGContextRelease (cgc);
2428         free (imageData);
2429         // create_internal will have set an error
2430         return &surf->base;
2431     }
2432
2433     surf->base.is_clear = TRUE;
2434
2435     surf->imageData = imageData;
2436     surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2437
2438     return &surf->base;
2439 }
2440
2441 /**
2442  * cairo_quartz_surface_get_cg_context:
2443  * @surface: the Cairo Quartz surface
2444  *
2445  * Returns the CGContextRef that the given Quartz surface is backed
2446  * by.
2447  *
2448  * A call to cairo_surface_flush() is required before using the
2449  * CGContextRef to ensure that all pending drawing operations are
2450  * finished and to restore any temporary modification cairo has made
2451  * to its state. A call to cairo_surface_mark_dirty() is required
2452  * after the state or the content of the CGContextRef has been
2453  * modified.
2454  *
2455  * Return value: the CGContextRef for the given surface.
2456  *
2457  * Since: 1.6
2458  **/
2459 CGContextRef
2460 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2461 {
2462     if (surface && _cairo_surface_is_quartz (surface)) {
2463         cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
2464         return quartz->cgContext;
2465     } else
2466         return NULL;
2467 }
2468
2469 static cairo_bool_t
2470 _cairo_surface_is_quartz (const cairo_surface_t *surface)
2471 {
2472     return surface->backend == &cairo_quartz_surface_backend;
2473 }
2474
2475 /* Debug stuff */
2476
2477 #ifdef QUARTZ_DEBUG
2478
2479 #include <Movies.h>
2480
2481 void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
2482 {
2483     Handle  dataRef = NULL;
2484     OSType  dataRefType;
2485     CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
2486
2487     GraphicsExportComponent grex = 0;
2488     unsigned long sizeWritten;
2489
2490     ComponentResult result;
2491
2492     // create the data reference
2493     result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
2494                                                      0, &dataRef, &dataRefType);
2495
2496     if (NULL != dataRef && noErr == result) {
2497         // get the PNG exporter
2498         result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
2499                                         &grex);
2500
2501         if (grex) {
2502             // tell the exporter where to find its source image
2503             result = GraphicsExportSetInputCGImage (grex, inImageRef);
2504
2505             if (noErr == result) {
2506                 // tell the exporter where to save the exporter image
2507                 result = GraphicsExportSetOutputDataReference (grex, dataRef,
2508                                                                dataRefType);
2509
2510                 if (noErr == result) {
2511                     // write the PNG file
2512                     result = GraphicsExportDoExport (grex, &sizeWritten);
2513                 }
2514             }
2515
2516             // remember to close the component
2517             CloseComponent (grex);
2518         }
2519
2520         // remember to dispose of the data reference handle
2521         DisposeHandle (dataRef);
2522     }
2523 }
2524
2525 void
2526 quartz_image_to_png (CGImageRef imgref, char *dest)
2527 {
2528     static int sctr = 0;
2529     char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2530
2531     if (dest == NULL) {
2532         fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2533         sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2534         sctr++;
2535         dest = sptr;
2536     }
2537
2538     ExportCGImageToPNGFile (imgref, dest);
2539 }
2540
2541 void
2542 quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2543 {
2544     static int sctr = 0;
2545     char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2546
2547     if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2548         fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2549         return;
2550     }
2551
2552     if (dest == NULL) {
2553         fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2554         sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2555         sctr++;
2556         dest = sptr;
2557     }
2558
2559     CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2560     if (imgref == NULL) {
2561         fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2562         return;
2563     }
2564
2565     ExportCGImageToPNGFile (imgref, dest);
2566
2567     CGImageRelease (imgref);
2568 }
2569
2570 #endif /* QUARTZ_DEBUG */