beta-0.89.2
[luatex.git] / source / libs / cairo / cairo-src / src / cairo-quartz-surface.c
blob1116ff94c1ec39de8aea8729c9ffcf15f4b212aa
1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright � 2006, 2007 Mozilla Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 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
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is Mozilla Foundation.
33 * Contributor(s):
34 * Vladimir Vukicevic <vladimir@mozilla.com>
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
38 #include "cairoint.h"
40 #include "cairo-quartz-private.h"
42 #include "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"
52 #include <dlfcn.h>
54 #ifndef RTLD_DEFAULT
55 #define RTLD_DEFAULT ((void *) 0)
56 #endif
58 #include <limits.h>
60 #undef QUARTZ_DEBUG
62 #ifdef QUARTZ_DEBUG
63 #define ND(_x) fprintf _x
64 #else
65 #define ND(_x) do {} while(0)
66 #endif
68 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
70 /**
71 * SECTION:cairo-quartz
72 * @Title: Quartz Surfaces
73 * @Short_Description: Rendering to Quartz surfaces
74 * @See_Also: #cairo_surface_t
76 * The Quartz surface is used to render cairo graphics targeting the
77 * Apple OS X Quartz rendering system.
78 **/
80 /**
81 * CAIRO_HAS_QUARTZ_SURFACE:
83 * Defined if the Quartz surface backend is available.
84 * This macro can be used to conditionally compile backend-specific code.
86 * Since: 1.6
87 **/
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.
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))
108 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
109 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
110 #endif
112 /* Some of these are present in earlier versions of the OS than where
113 * they are public; other are not public at all
115 /* public since 10.5 */
116 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
118 /* public since 10.6 */
119 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
120 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
122 /* not yet public */
123 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
124 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
126 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
129 * Utility functions
132 #ifdef QUARTZ_DEBUG
133 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
134 static void quartz_image_to_png (CGImageRef, char *dest);
135 #endif
137 static cairo_quartz_surface_t *
138 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
139 cairo_content_t content,
140 unsigned int width,
141 unsigned int height);
143 /* Load all extra symbols */
144 static void quartz_ensure_symbols (void)
146 if (likely (_cairo_quartz_symbol_lookup_done))
147 return;
149 CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
150 CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
151 CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
152 CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
153 CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
155 _cairo_quartz_symbol_lookup_done = TRUE;
158 CGImageRef
159 CairoQuartzCreateCGImage (cairo_format_t format,
160 unsigned int width,
161 unsigned int height,
162 unsigned int stride,
163 void *data,
164 cairo_bool_t interpolate,
165 CGColorSpaceRef colorSpaceOverride,
166 CGDataProviderReleaseDataCallback releaseCallback,
167 void *releaseInfo)
169 CGImageRef image = NULL;
170 CGDataProviderRef dataProvider = NULL;
171 CGColorSpaceRef colorSpace = colorSpaceOverride;
172 CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
173 int bitsPerComponent, bitsPerPixel;
175 switch (format) {
176 case CAIRO_FORMAT_ARGB32:
177 if (colorSpace == NULL)
178 colorSpace = CGColorSpaceCreateDeviceRGB ();
179 bitinfo |= kCGImageAlphaPremultipliedFirst;
180 bitsPerComponent = 8;
181 bitsPerPixel = 32;
182 break;
184 case CAIRO_FORMAT_RGB24:
185 if (colorSpace == NULL)
186 colorSpace = CGColorSpaceCreateDeviceRGB ();
187 bitinfo |= kCGImageAlphaNoneSkipFirst;
188 bitsPerComponent = 8;
189 bitsPerPixel = 32;
190 break;
192 case CAIRO_FORMAT_A8:
193 bitsPerComponent = 8;
194 bitsPerPixel = 8;
195 break;
197 case CAIRO_FORMAT_A1:
198 #ifdef WORDS_BIGENDIAN
199 bitsPerComponent = 1;
200 bitsPerPixel = 1;
201 break;
202 #endif
204 case CAIRO_FORMAT_RGB30:
205 case CAIRO_FORMAT_RGB16_565:
206 case CAIRO_FORMAT_INVALID:
207 default:
208 return NULL;
211 dataProvider = CGDataProviderCreateWithData (releaseInfo,
212 data,
213 height * stride,
214 releaseCallback);
216 if (unlikely (!dataProvider)) {
217 // manually release
218 if (releaseCallback)
219 releaseCallback (releaseInfo, data, height * stride);
220 goto FINISH;
223 if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
224 cairo_quartz_float_t decode[] = {1.0, 0.0};
225 image = CGImageMaskCreate (width, height,
226 bitsPerComponent,
227 bitsPerPixel,
228 stride,
229 dataProvider,
230 decode,
231 interpolate);
232 } else
233 image = CGImageCreate (width, height,
234 bitsPerComponent,
235 bitsPerPixel,
236 stride,
237 colorSpace,
238 bitinfo,
239 dataProvider,
240 NULL,
241 interpolate,
242 kCGRenderingIntentDefault);
244 FINISH:
246 CGDataProviderRelease (dataProvider);
248 if (colorSpace != colorSpaceOverride)
249 CGColorSpaceRelease (colorSpace);
251 return image;
254 static inline cairo_bool_t
255 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
257 if (unlikely (cgc == NULL))
258 return FALSE;
260 if (likely (CGContextGetTypePtr)) {
261 /* 4 is the type value of a bitmap context */
262 return CGContextGetTypePtr (cgc) == 4;
265 /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
266 return CGBitmapContextGetBitsPerPixel (cgc) != 0;
269 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
271 #define CG_MAX_HEIGHT SHRT_MAX
272 #define CG_MAX_WIDTH USHRT_MAX
274 /* is the desired size of the surface within bounds? */
275 cairo_bool_t
276 _cairo_quartz_verify_surface_size (int width, int height)
278 /* hmmm, allow width, height == 0 ? */
279 if (width < 0 || height < 0)
280 return FALSE;
282 if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
283 return FALSE;
285 return TRUE;
289 * Cairo path -> Quartz path conversion helpers
292 /* cairo path -> execute in context */
293 static cairo_status_t
294 _cairo_path_to_quartz_context_move_to (void *closure,
295 const cairo_point_t *point)
297 //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
298 double x = _cairo_fixed_to_double (point->x);
299 double y = _cairo_fixed_to_double (point->y);
301 CGContextMoveToPoint (closure, x, y);
302 return CAIRO_STATUS_SUCCESS;
305 static cairo_status_t
306 _cairo_path_to_quartz_context_line_to (void *closure,
307 const cairo_point_t *point)
309 //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
310 double x = _cairo_fixed_to_double (point->x);
311 double y = _cairo_fixed_to_double (point->y);
313 CGContextAddLineToPoint (closure, x, y);
314 return CAIRO_STATUS_SUCCESS;
317 static cairo_status_t
318 _cairo_path_to_quartz_context_curve_to (void *closure,
319 const cairo_point_t *p0,
320 const cairo_point_t *p1,
321 const cairo_point_t *p2)
323 //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
324 // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
325 // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
326 // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
327 double x0 = _cairo_fixed_to_double (p0->x);
328 double y0 = _cairo_fixed_to_double (p0->y);
329 double x1 = _cairo_fixed_to_double (p1->x);
330 double y1 = _cairo_fixed_to_double (p1->y);
331 double x2 = _cairo_fixed_to_double (p2->x);
332 double y2 = _cairo_fixed_to_double (p2->y);
334 CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
335 return CAIRO_STATUS_SUCCESS;
338 static cairo_status_t
339 _cairo_path_to_quartz_context_close_path (void *closure)
341 //ND ((stderr, "closepath\n"));
342 CGContextClosePath (closure);
343 return CAIRO_STATUS_SUCCESS;
346 static void
347 _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
348 CGContextRef closure)
350 cairo_status_t status;
352 CGContextBeginPath (closure);
353 status = _cairo_path_fixed_interpret (path,
354 _cairo_path_to_quartz_context_move_to,
355 _cairo_path_to_quartz_context_line_to,
356 _cairo_path_to_quartz_context_curve_to,
357 _cairo_path_to_quartz_context_close_path,
358 closure);
360 assert (status == CAIRO_STATUS_SUCCESS);
364 * Misc helpers/callbacks
367 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
368 static PrivateCGCompositeMode
369 _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
371 switch (op) {
372 case CAIRO_OPERATOR_CLEAR:
373 return kPrivateCGCompositeClear;
374 case CAIRO_OPERATOR_SOURCE:
375 return kPrivateCGCompositeCopy;
376 case CAIRO_OPERATOR_OVER:
377 return kPrivateCGCompositeSourceOver;
378 case CAIRO_OPERATOR_IN:
379 return kPrivateCGCompositeSourceIn;
380 case CAIRO_OPERATOR_OUT:
381 return kPrivateCGCompositeSourceOut;
382 case CAIRO_OPERATOR_ATOP:
383 return kPrivateCGCompositeSourceAtop;
384 case CAIRO_OPERATOR_DEST_OVER:
385 return kPrivateCGCompositeDestinationOver;
386 case CAIRO_OPERATOR_DEST_IN:
387 return kPrivateCGCompositeDestinationIn;
388 case CAIRO_OPERATOR_DEST_OUT:
389 return kPrivateCGCompositeDestinationOut;
390 case CAIRO_OPERATOR_DEST_ATOP:
391 return kPrivateCGCompositeDestinationAtop;
392 case CAIRO_OPERATOR_XOR:
393 return kPrivateCGCompositeXOR;
394 case CAIRO_OPERATOR_ADD:
395 return kPrivateCGCompositePlusLighter;
397 case CAIRO_OPERATOR_DEST:
398 case CAIRO_OPERATOR_SATURATE:
399 case CAIRO_OPERATOR_MULTIPLY:
400 case CAIRO_OPERATOR_SCREEN:
401 case CAIRO_OPERATOR_OVERLAY:
402 case CAIRO_OPERATOR_DARKEN:
403 case CAIRO_OPERATOR_LIGHTEN:
404 case CAIRO_OPERATOR_COLOR_DODGE:
405 case CAIRO_OPERATOR_COLOR_BURN:
406 case CAIRO_OPERATOR_HARD_LIGHT:
407 case CAIRO_OPERATOR_SOFT_LIGHT:
408 case CAIRO_OPERATOR_DIFFERENCE:
409 case CAIRO_OPERATOR_EXCLUSION:
410 case CAIRO_OPERATOR_HSL_HUE:
411 case CAIRO_OPERATOR_HSL_SATURATION:
412 case CAIRO_OPERATOR_HSL_COLOR:
413 case CAIRO_OPERATOR_HSL_LUMINOSITY:
414 default:
415 ASSERT_NOT_REACHED;
418 #endif
420 static CGBlendMode
421 _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
423 switch (op) {
424 case CAIRO_OPERATOR_MULTIPLY:
425 return kCGBlendModeMultiply;
426 case CAIRO_OPERATOR_SCREEN:
427 return kCGBlendModeScreen;
428 case CAIRO_OPERATOR_OVERLAY:
429 return kCGBlendModeOverlay;
430 case CAIRO_OPERATOR_DARKEN:
431 return kCGBlendModeDarken;
432 case CAIRO_OPERATOR_LIGHTEN:
433 return kCGBlendModeLighten;
434 case CAIRO_OPERATOR_COLOR_DODGE:
435 return kCGBlendModeColorDodge;
436 case CAIRO_OPERATOR_COLOR_BURN:
437 return kCGBlendModeColorBurn;
438 case CAIRO_OPERATOR_HARD_LIGHT:
439 return kCGBlendModeHardLight;
440 case CAIRO_OPERATOR_SOFT_LIGHT:
441 return kCGBlendModeSoftLight;
442 case CAIRO_OPERATOR_DIFFERENCE:
443 return kCGBlendModeDifference;
444 case CAIRO_OPERATOR_EXCLUSION:
445 return kCGBlendModeExclusion;
446 case CAIRO_OPERATOR_HSL_HUE:
447 return kCGBlendModeHue;
448 case CAIRO_OPERATOR_HSL_SATURATION:
449 return kCGBlendModeSaturation;
450 case CAIRO_OPERATOR_HSL_COLOR:
451 return kCGBlendModeColor;
452 case CAIRO_OPERATOR_HSL_LUMINOSITY:
453 return kCGBlendModeLuminosity;
455 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
456 case CAIRO_OPERATOR_CLEAR:
457 return kCGBlendModeClear;
458 case CAIRO_OPERATOR_SOURCE:
459 return kCGBlendModeCopy;
460 case CAIRO_OPERATOR_OVER:
461 return kCGBlendModeNormal;
462 case CAIRO_OPERATOR_IN:
463 return kCGBlendModeSourceIn;
464 case CAIRO_OPERATOR_OUT:
465 return kCGBlendModeSourceOut;
466 case CAIRO_OPERATOR_ATOP:
467 return kCGBlendModeSourceAtop;
468 case CAIRO_OPERATOR_DEST_OVER:
469 return kCGBlendModeDestinationOver;
470 case CAIRO_OPERATOR_DEST_IN:
471 return kCGBlendModeDestinationIn;
472 case CAIRO_OPERATOR_DEST_OUT:
473 return kCGBlendModeDestinationOut;
474 case CAIRO_OPERATOR_DEST_ATOP:
475 return kCGBlendModeDestinationAtop;
476 case CAIRO_OPERATOR_XOR:
477 return kCGBlendModeXOR;
478 case CAIRO_OPERATOR_ADD:
479 return kCGBlendModePlusLighter;
480 #else
481 case CAIRO_OPERATOR_CLEAR:
482 case CAIRO_OPERATOR_SOURCE:
483 case CAIRO_OPERATOR_OVER:
484 case CAIRO_OPERATOR_IN:
485 case CAIRO_OPERATOR_OUT:
486 case CAIRO_OPERATOR_ATOP:
487 case CAIRO_OPERATOR_DEST_OVER:
488 case CAIRO_OPERATOR_DEST_IN:
489 case CAIRO_OPERATOR_DEST_OUT:
490 case CAIRO_OPERATOR_DEST_ATOP:
491 case CAIRO_OPERATOR_XOR:
492 case CAIRO_OPERATOR_ADD:
493 #endif
495 case CAIRO_OPERATOR_DEST:
496 case CAIRO_OPERATOR_SATURATE:
497 default:
498 ASSERT_NOT_REACHED;
502 static cairo_int_status_t
503 _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
505 CGBlendMode blendmode;
507 assert (op != CAIRO_OPERATOR_DEST);
509 /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
510 * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
511 * uses the definition from the Adobe Supplement.
513 if (op == CAIRO_OPERATOR_SATURATE ||
514 op == CAIRO_OPERATOR_COLOR_DODGE ||
515 op == CAIRO_OPERATOR_COLOR_BURN)
517 return CAIRO_INT_STATUS_UNSUPPORTED;
520 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
521 if (op <= CAIRO_OPERATOR_ADD) {
522 PrivateCGCompositeMode compmode;
524 compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
525 CGContextSetCompositeOperation (context, compmode);
526 return CAIRO_STATUS_SUCCESS;
528 #endif
530 blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
531 CGContextSetBlendMode (context, blendmode);
532 return CAIRO_STATUS_SUCCESS;
535 static cairo_int_status_t
536 _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
538 ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
540 /* When the destination has no color components, we can avoid some
541 * fallbacks, but we have to workaround operators which behave
542 * differently in Quartz. */
543 if (surface->base.content == CAIRO_CONTENT_ALPHA) {
544 assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
546 if (op == CAIRO_OPERATOR_SOURCE ||
547 op == CAIRO_OPERATOR_IN ||
548 op == CAIRO_OPERATOR_OUT ||
549 op == CAIRO_OPERATOR_DEST_IN ||
550 op == CAIRO_OPERATOR_DEST_ATOP ||
551 op == CAIRO_OPERATOR_XOR)
553 return CAIRO_INT_STATUS_UNSUPPORTED;
556 if (op == CAIRO_OPERATOR_DEST_OVER)
557 op = CAIRO_OPERATOR_OVER;
558 else if (op == CAIRO_OPERATOR_SATURATE)
559 op = CAIRO_OPERATOR_ADD;
560 else if (op == CAIRO_OPERATOR_COLOR_DODGE)
561 op = CAIRO_OPERATOR_OVER;
562 else if (op == CAIRO_OPERATOR_COLOR_BURN)
563 op = CAIRO_OPERATOR_OVER;
566 return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
569 static inline CGLineCap
570 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
572 switch (ccap) {
573 default:
574 ASSERT_NOT_REACHED;
576 case CAIRO_LINE_CAP_BUTT:
577 return kCGLineCapButt;
579 case CAIRO_LINE_CAP_ROUND:
580 return kCGLineCapRound;
582 case CAIRO_LINE_CAP_SQUARE:
583 return kCGLineCapSquare;
587 static inline CGLineJoin
588 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
590 switch (cjoin) {
591 default:
592 ASSERT_NOT_REACHED;
594 case CAIRO_LINE_JOIN_MITER:
595 return kCGLineJoinMiter;
597 case CAIRO_LINE_JOIN_ROUND:
598 return kCGLineJoinRound;
600 case CAIRO_LINE_JOIN_BEVEL:
601 return kCGLineJoinBevel;
605 static inline CGInterpolationQuality
606 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
608 switch (filter) {
609 case CAIRO_FILTER_NEAREST:
610 case CAIRO_FILTER_FAST:
611 return kCGInterpolationNone;
613 case CAIRO_FILTER_BEST:
614 case CAIRO_FILTER_GOOD:
615 case CAIRO_FILTER_BILINEAR:
616 case CAIRO_FILTER_GAUSSIAN:
617 return kCGInterpolationDefault;
619 default:
620 ASSERT_NOT_REACHED;
621 return kCGInterpolationDefault;
625 static inline void
626 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
627 CGAffineTransform *dst)
629 dst->a = src->xx;
630 dst->b = src->yx;
631 dst->c = src->xy;
632 dst->d = src->yy;
633 dst->tx = src->x0;
634 dst->ty = src->y0;
639 * Source -> Quartz setup and finish functions
642 static void
643 ComputeGradientValue (void *info,
644 const cairo_quartz_float_t *in,
645 cairo_quartz_float_t *out)
647 double fdist = *in;
648 const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
649 unsigned int i;
651 /* Put fdist back in the 0.0..1.0 range if we're doing
652 * REPEAT/REFLECT
654 if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
655 fdist = fdist - floor (fdist);
656 } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
657 fdist = fmod (fabs (fdist), 2.0);
658 if (fdist > 1.0)
659 fdist = 2.0 - fdist;
662 for (i = 0; i < grad->n_stops; i++)
663 if (grad->stops[i].offset > fdist)
664 break;
666 if (i == 0 || i == grad->n_stops) {
667 if (i == grad->n_stops)
668 --i;
669 out[0] = grad->stops[i].color.red;
670 out[1] = grad->stops[i].color.green;
671 out[2] = grad->stops[i].color.blue;
672 out[3] = grad->stops[i].color.alpha;
673 } else {
674 cairo_quartz_float_t ax = grad->stops[i-1].offset;
675 cairo_quartz_float_t bx = grad->stops[i].offset - ax;
676 cairo_quartz_float_t bp = (fdist - ax)/bx;
677 cairo_quartz_float_t ap = 1.0 - bp;
679 out[0] =
680 grad->stops[i-1].color.red * ap +
681 grad->stops[i].color.red * bp;
682 out[1] =
683 grad->stops[i-1].color.green * ap +
684 grad->stops[i].color.green * bp;
685 out[2] =
686 grad->stops[i-1].color.blue * ap +
687 grad->stops[i].color.blue * bp;
688 out[3] =
689 grad->stops[i-1].color.alpha * ap +
690 grad->stops[i].color.alpha * bp;
694 static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
695 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
697 static const CGFunctionCallbacks gradient_callbacks = {
698 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
701 /* Quartz computes a small number of samples of the gradient color
702 * function. On MacOS X 10.5 it apparently computes only 1024
703 * samples. */
704 #define MAX_GRADIENT_RANGE 1024
706 static CGFunctionRef
707 CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
708 const cairo_rectangle_int_t *extents,
709 cairo_circle_double_t *start,
710 cairo_circle_double_t *end)
712 cairo_pattern_t *pat;
713 cairo_quartz_float_t input_value_range[2];
715 if (gradient->base.extend != CAIRO_EXTEND_NONE) {
716 double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
717 double t[2], tolerance;
719 tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
720 tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
722 bounds_x1 = extents->x;
723 bounds_y1 = extents->y;
724 bounds_x2 = extents->x + extents->width;
725 bounds_y2 = extents->y + extents->height;
726 _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
727 &bounds_x1, &bounds_y1,
728 &bounds_x2, &bounds_y2,
729 NULL);
731 _cairo_gradient_pattern_box_to_parameter (gradient,
732 bounds_x1, bounds_y1,
733 bounds_x2, bounds_y2,
734 tolerance,
737 if (gradient->base.extend == CAIRO_EXTEND_PAD) {
738 t[0] = MAX (t[0], -0.5);
739 t[1] = MIN (t[1], 1.5);
740 } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
741 return NULL;
743 /* set the input range for the function -- the function knows how
744 to map values outside of 0.0 .. 1.0 to the correct color */
745 input_value_range[0] = t[0];
746 input_value_range[1] = t[1];
747 } else {
748 input_value_range[0] = 0;
749 input_value_range[1] = 1;
752 _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
753 _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
755 if (_cairo_pattern_create_copy (&pat, &gradient->base))
756 return NULL;
758 return CGFunctionCreate (pat,
760 input_value_range,
762 gradient_output_value_ranges,
763 &gradient_callbacks);
766 /* Obtain a CGImageRef from a #cairo_surface_t * */
768 typedef struct {
769 cairo_surface_t *surface;
770 cairo_image_surface_t *image_out;
771 void *image_extra;
772 } quartz_source_image_t;
774 static void
775 DataProviderReleaseCallback (void *info, const void *data, size_t size)
777 quartz_source_image_t *source_img = info;
778 _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
779 free (source_img);
782 static cairo_status_t
783 _cairo_surface_to_cgimage (cairo_surface_t *source,
784 cairo_rectangle_int_t *extents,
785 cairo_format_t format,
786 cairo_matrix_t *matrix,
787 const cairo_clip_t *clip,
788 CGImageRef *image_out)
790 cairo_status_t status;
791 quartz_source_image_t *source_img;
792 cairo_image_surface_t *image_surface;
794 if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
795 cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
796 *image_out = CGImageRetain (surface->image);
797 return CAIRO_STATUS_SUCCESS;
800 if (_cairo_surface_is_quartz (source)) {
801 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
802 if (IS_EMPTY (surface)) {
803 *image_out = NULL;
804 return CAIRO_INT_STATUS_NOTHING_TO_DO;
807 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
808 *image_out = CGBitmapContextCreateImage (surface->cgContext);
809 if (*image_out)
810 return CAIRO_STATUS_SUCCESS;
814 source_img = malloc (sizeof (quartz_source_image_t));
815 if (unlikely (source_img == NULL))
816 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
818 source_img->surface = source;
820 if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
821 image_surface = (cairo_image_surface_t *)
822 cairo_image_surface_create (format, extents->width, extents->height);
823 if (unlikely (image_surface->base.status)) {
824 status = image_surface->base.status;
825 cairo_surface_destroy (&image_surface->base);
826 free (source_img);
827 return status;
830 status = _cairo_recording_surface_replay_with_clip (source,
831 matrix,
832 &image_surface->base,
833 NULL);
834 if (unlikely (status)) {
835 cairo_surface_destroy (&image_surface->base);
836 free (source_img);
837 return status;
840 source_img->image_out = image_surface;
841 source_img->image_extra = NULL;
843 cairo_matrix_init_identity (matrix);
845 else {
846 status = _cairo_surface_acquire_source_image (source_img->surface,
847 &source_img->image_out,
848 &source_img->image_extra);
849 if (unlikely (status)) {
850 free (source_img);
851 return status;
855 if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
856 *image_out = NULL;
857 DataProviderReleaseCallback (source_img,
858 source_img->image_out->data,
859 source_img->image_out->height * source_img->image_out->stride);
860 } else {
861 *image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
862 source_img->image_out->width,
863 source_img->image_out->height,
864 source_img->image_out->stride,
865 source_img->image_out->data,
866 TRUE,
867 NULL,
868 DataProviderReleaseCallback,
869 source_img);
871 /* TODO: differentiate memory error and unsupported surface type */
872 if (unlikely (*image_out == NULL))
873 status = CAIRO_INT_STATUS_UNSUPPORTED;
876 return status;
879 /* Generic #cairo_pattern_t -> CGPattern function */
881 typedef struct {
882 CGImageRef image;
883 CGRect imageBounds;
884 cairo_bool_t do_reflect;
885 } SurfacePatternDrawInfo;
887 static void
888 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
890 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
892 CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
893 CGContextScaleCTM (context, 1, -1);
895 CGContextDrawImage (context, info->imageBounds, info->image);
896 if (info->do_reflect) {
897 /* draw 3 more copies of the image, flipped.
898 * DrawImage draws the image according to the current Y-direction into the rectangle given
899 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
900 * of the base image position, and the Y axis is extending upwards.
903 /* Make the y axis extend downwards, and draw a flipped image below */
904 CGContextScaleCTM (context, 1, -1);
905 CGContextDrawImage (context, info->imageBounds, info->image);
907 /* Shift over to the right, and flip vertically (translation is 2x,
908 * since we'll be flipping and thus rendering the rectangle "backwards"
910 CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
911 CGContextScaleCTM (context, -1, 1);
912 CGContextDrawImage (context, info->imageBounds, info->image);
914 /* Then unflip the Y-axis again, and draw the image above the point. */
915 CGContextScaleCTM (context, 1, -1);
916 CGContextDrawImage (context, info->imageBounds, info->image);
920 static void
921 SurfacePatternReleaseInfoFunc (void *ainfo)
923 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
925 CGImageRelease (info->image);
926 free (info);
929 static cairo_int_status_t
930 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
931 const cairo_pattern_t *apattern,
932 const cairo_clip_t *clip,
933 CGPatternRef *cgpat)
935 cairo_surface_pattern_t *spattern;
936 cairo_surface_t *pat_surf;
937 cairo_rectangle_int_t extents;
938 cairo_format_t format = _cairo_format_from_content (dest->base.content);
940 CGImageRef image;
941 CGRect pbounds;
942 CGAffineTransform ptransform, stransform;
943 CGPatternCallbacks cb = { 0,
944 SurfacePatternDrawFunc,
945 SurfacePatternReleaseInfoFunc };
946 SurfacePatternDrawInfo *info;
947 cairo_quartz_float_t rw, rh;
948 cairo_status_t status;
949 cairo_bool_t is_bounded;
951 cairo_matrix_t m;
953 /* SURFACE is the only type we'll handle here */
954 assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
956 spattern = (cairo_surface_pattern_t *) apattern;
957 pat_surf = spattern->surface;
959 if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
960 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
961 assert (is_bounded);
963 else
964 _cairo_surface_get_extents (&dest->base, &extents);
966 m = spattern->base.matrix;
967 status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
968 &m, clip, &image);
969 if (unlikely (status))
970 return status;
972 info = malloc (sizeof (SurfacePatternDrawInfo));
973 if (unlikely (!info))
974 return CAIRO_STATUS_NO_MEMORY;
976 /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
977 * that the data will stick around for this image when the printer gets to it.
978 * Otherwise, the underlying data store may disappear from under us!
980 * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
981 * since the Quartz surfaces have a higher chance of sticking around. If the
982 * source is a quartz image surface, then it's set up to retain a ref to the
983 * image surface that it's backed by.
985 info->image = image;
986 info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
987 info->do_reflect = FALSE;
989 pbounds.origin.x = 0;
990 pbounds.origin.y = 0;
992 if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
993 pbounds.size.width = 2.0 * extents.width;
994 pbounds.size.height = 2.0 * extents.height;
995 info->do_reflect = TRUE;
996 } else {
997 pbounds.size.width = extents.width;
998 pbounds.size.height = extents.height;
1000 rw = pbounds.size.width;
1001 rh = pbounds.size.height;
1003 cairo_matrix_invert (&m);
1004 _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
1006 /* The pattern matrix is relative to the bottom left, again; the
1007 * incoming cairo pattern matrix is relative to the upper left.
1008 * So we take the pattern matrix and the original context matrix,
1009 * which gives us the correct base translation/y flip.
1011 ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
1013 #ifdef QUARTZ_DEBUG
1014 ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
1015 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));
1016 CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
1017 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));
1018 #endif
1020 *cgpat = CGPatternCreate (info,
1021 pbounds,
1022 ptransform,
1023 rw, rh,
1024 kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
1025 TRUE,
1026 &cb);
1028 return CAIRO_STATUS_SUCCESS;
1031 /* State used during a drawing operation. */
1032 typedef struct {
1033 /* The destination of the mask */
1034 CGContextRef cgMaskContext;
1036 /* The destination of the drawing of the source */
1037 CGContextRef cgDrawContext;
1039 /* The filter to be used when drawing the source */
1040 CGInterpolationQuality filter;
1042 /* Action type */
1043 cairo_quartz_action_t action;
1045 /* Destination rect */
1046 CGRect rect;
1048 /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1049 CGAffineTransform transform;
1051 /* Used with DO_IMAGE and DO_TILED_IMAGE */
1052 CGImageRef image;
1054 /* Used with DO_SHADING */
1055 CGShadingRef shading;
1057 /* Temporary destination for unbounded operations */
1058 CGLayerRef layer;
1059 CGRect clipRect;
1060 } cairo_quartz_drawing_state_t;
1063 Quartz does not support repeating radients. We handle repeating gradients
1064 by manually extending the gradient and repeating color stops. We need to
1065 minimize the number of repetitions since Quartz seems to sample our color
1066 function across the entire range, even if part of that range is not needed
1067 for the visible area of the gradient, and it samples with some fixed resolution,
1068 so if the gradient range is too large it samples with very low resolution and
1069 the gradient is very coarse. _cairo_quartz_create_gradient_function computes
1070 the number of repetitions needed based on the extents.
1072 static cairo_int_status_t
1073 _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
1074 const cairo_gradient_pattern_t *gradient,
1075 const cairo_rectangle_int_t *extents)
1077 cairo_matrix_t mat;
1078 cairo_circle_double_t start, end;
1079 CGFunctionRef gradFunc;
1080 CGColorSpaceRef rgb;
1081 bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
1083 assert (gradient->n_stops > 0);
1085 mat = gradient->base.matrix;
1086 cairo_matrix_invert (&mat);
1087 _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
1089 gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
1090 &start, &end);
1092 if (unlikely (gradFunc == NULL))
1093 return CAIRO_INT_STATUS_UNSUPPORTED;
1095 rgb = CGColorSpaceCreateDeviceRGB ();
1097 if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
1098 state->shading = CGShadingCreateAxial (rgb,
1099 CGPointMake (start.center.x,
1100 start.center.y),
1101 CGPointMake (end.center.x,
1102 end.center.y),
1103 gradFunc,
1104 extend, extend);
1105 } else {
1106 state->shading = CGShadingCreateRadial (rgb,
1107 CGPointMake (start.center.x,
1108 start.center.y),
1109 MAX (start.radius, 0),
1110 CGPointMake (end.center.x,
1111 end.center.y),
1112 MAX (end.radius, 0),
1113 gradFunc,
1114 extend, extend);
1117 CGColorSpaceRelease (rgb);
1118 CGFunctionRelease (gradFunc);
1120 state->action = DO_SHADING;
1121 return CAIRO_STATUS_SUCCESS;
1124 static cairo_int_status_t
1125 _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
1126 cairo_composite_rectangles_t *composite)
1128 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface;
1129 cairo_operator_t op = composite->op;
1130 const cairo_pattern_t *source = &composite->source_pattern.base;
1131 const cairo_clip_t *clip = composite->clip;
1132 cairo_bool_t needs_temp;
1133 cairo_status_t status;
1134 cairo_format_t format = _cairo_format_from_content (composite->surface->content);
1136 state->layer = NULL;
1137 state->image = NULL;
1138 state->shading = NULL;
1139 state->cgDrawContext = NULL;
1140 state->cgMaskContext = NULL;
1142 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1143 if (unlikely (status))
1144 return status;
1146 status = _cairo_quartz_surface_set_cairo_operator (surface, op);
1147 if (unlikely (status))
1148 return status;
1150 /* Save before we change the pattern, colorspace, etc. so that
1151 * we can restore and make sure that quartz releases our
1152 * pattern (which may be stack allocated)
1155 CGContextSaveGState (surface->cgContext);
1156 state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
1157 state->clipRect = CGRectIntegral (state->clipRect);
1158 state->rect = state->clipRect;
1160 state->cgMaskContext = surface->cgContext;
1161 state->cgDrawContext = state->cgMaskContext;
1163 state->filter = _cairo_quartz_filter_to_quartz (source->filter);
1165 if (op == CAIRO_OPERATOR_CLEAR) {
1166 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1168 state->action = DO_DIRECT;
1169 return CAIRO_STATUS_SUCCESS;
1173 * To implement mask unbounded operations Quartz needs a temporary
1174 * surface which will be composited entirely (ignoring the mask).
1175 * To implement source unbounded operations Quartz needs a
1176 * temporary surface which allows extending the source to a size
1177 * covering the whole mask, but there are some optimization
1178 * opportunities:
1180 * - CLEAR completely ignores the source, thus we can just use a
1181 * solid color fill.
1183 * - SOURCE can be implemented by drawing the source and clearing
1184 * outside of the source as long as the two regions have no
1185 * intersection. This happens when the source is a pixel-aligned
1186 * rectangle. If the source is at least as big as the
1187 * intersection between the clip rectangle and the mask
1188 * rectangle, no clear operation is needed.
1190 needs_temp = ! _cairo_operator_bounded_by_mask (op);
1192 if (needs_temp) {
1193 state->layer = CGLayerCreateWithContext (surface->cgContext,
1194 state->clipRect.size,
1195 NULL);
1196 state->cgDrawContext = CGLayerGetContext (state->layer);
1197 state->cgMaskContext = state->cgDrawContext;
1198 CGContextTranslateCTM (state->cgDrawContext,
1199 -state->clipRect.origin.x,
1200 -state->clipRect.origin.y);
1203 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1204 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1206 CGContextSetRGBStrokeColor (state->cgDrawContext,
1207 solid->color.red,
1208 solid->color.green,
1209 solid->color.blue,
1210 solid->color.alpha);
1211 CGContextSetRGBFillColor (state->cgDrawContext,
1212 solid->color.red,
1213 solid->color.green,
1214 solid->color.blue,
1215 solid->color.alpha);
1217 state->action = DO_DIRECT;
1218 return CAIRO_STATUS_SUCCESS;
1221 if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
1222 source->type == CAIRO_PATTERN_TYPE_RADIAL)
1224 const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
1225 cairo_rectangle_int_t extents;
1227 extents = surface->virtual_extents;
1228 extents.x -= surface->base.device_transform.x0;
1229 extents.y -= surface->base.device_transform.y0;
1230 _cairo_rectangle_union (&extents, &surface->extents);
1232 return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
1235 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1236 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1238 const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1239 cairo_surface_t *pat_surf = spat->surface;
1240 CGImageRef img;
1241 cairo_matrix_t m = spat->base.matrix;
1242 cairo_rectangle_int_t extents;
1243 CGAffineTransform xform;
1244 CGRect srcRect;
1245 cairo_fixed_t fw, fh;
1246 cairo_bool_t is_bounded;
1248 _cairo_surface_get_extents (composite->surface, &extents);
1249 status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
1250 &m, clip, &img);
1251 if (unlikely (status))
1252 return status;
1254 state->image = img;
1256 if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
1257 m.x0 = -ceil (m.x0 - 0.5);
1258 m.y0 = -ceil (m.y0 - 0.5);
1259 } else {
1260 cairo_matrix_invert (&m);
1263 _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
1265 if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
1266 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
1267 assert (is_bounded);
1270 srcRect = CGRectMake (0, 0, extents.width, extents.height);
1272 if (source->extend == CAIRO_EXTEND_NONE) {
1273 int x, y;
1274 if (op == CAIRO_OPERATOR_SOURCE &&
1275 (pat_surf->content == CAIRO_CONTENT_ALPHA ||
1276 ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
1278 state->layer = CGLayerCreateWithContext (surface->cgContext,
1279 state->clipRect.size,
1280 NULL);
1281 state->cgDrawContext = CGLayerGetContext (state->layer);
1282 CGContextTranslateCTM (state->cgDrawContext,
1283 -state->clipRect.origin.x,
1284 -state->clipRect.origin.y);
1287 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1289 state->rect = srcRect;
1290 state->action = DO_IMAGE;
1291 return CAIRO_STATUS_SUCCESS;
1294 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1296 /* Quartz seems to tile images at pixel-aligned regions only -- this
1297 * leads to seams if the image doesn't end up scaling to fill the
1298 * space exactly. The CGPattern tiling approach doesn't have this
1299 * problem. Check if we're going to fill up the space (within some
1300 * epsilon), and if not, fall back to the CGPattern type.
1303 xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
1304 state->transform);
1306 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1308 fw = _cairo_fixed_from_double (srcRect.size.width);
1309 fh = _cairo_fixed_from_double (srcRect.size.height);
1311 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1312 (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1314 /* We're good to use DrawTiledImage, but ensure that
1315 * the math works out */
1317 srcRect.size.width = round (srcRect.size.width);
1318 srcRect.size.height = round (srcRect.size.height);
1320 xform = CGAffineTransformInvert (xform);
1322 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1324 state->rect = srcRect;
1325 state->action = DO_TILED_IMAGE;
1326 return CAIRO_STATUS_SUCCESS;
1329 /* Fall through to generic SURFACE case */
1332 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1333 cairo_quartz_float_t patternAlpha = 1.0f;
1334 CGColorSpaceRef patternSpace;
1335 CGPatternRef pattern = NULL;
1336 cairo_int_status_t status;
1338 status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
1339 if (unlikely (status))
1340 return status;
1342 patternSpace = CGColorSpaceCreatePattern (NULL);
1343 CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
1344 CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
1345 CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
1346 CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
1347 CGColorSpaceRelease (patternSpace);
1349 /* Quartz likes to munge the pattern phase (as yet unexplained
1350 * why); force it to 0,0 as we've already baked in the correct
1351 * pattern translation into the pattern matrix
1353 CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
1355 CGPatternRelease (pattern);
1357 state->action = DO_DIRECT;
1358 return CAIRO_STATUS_SUCCESS;
1361 return CAIRO_INT_STATUS_UNSUPPORTED;
1364 static void
1365 _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
1366 cairo_composite_rectangles_t *extents)
1368 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1370 if (state->layer) {
1371 CGContextDrawLayerInRect (surface->cgContext,
1372 state->clipRect,
1373 state->layer);
1374 CGLayerRelease (state->layer);
1377 if (state->cgMaskContext)
1378 CGContextRestoreGState (surface->cgContext);
1380 if (state->image)
1381 CGImageRelease (state->image);
1383 if (state->shading)
1384 CGShadingRelease (state->shading);
1387 static void
1388 _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
1389 cairo_operator_t op)
1391 CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
1392 CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
1394 if (state->action == DO_DIRECT) {
1395 CGContextFillRect (state->cgDrawContext, state->rect);
1396 return;
1399 CGContextConcatCTM (state->cgDrawContext, state->transform);
1401 if (state->action == DO_SHADING) {
1402 CGContextDrawShading (state->cgDrawContext, state->shading);
1403 return;
1406 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1407 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1409 if (state->action == DO_IMAGE) {
1410 CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
1411 if (op == CAIRO_OPERATOR_SOURCE &&
1412 state->cgDrawContext == state->cgMaskContext)
1414 CGContextBeginPath (state->cgDrawContext);
1415 CGContextAddRect (state->cgDrawContext, state->rect);
1417 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1418 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1419 CGContextConcatCTM (state->cgDrawContext,
1420 CGAffineTransformInvert (state->transform));
1422 CGContextAddRect (state->cgDrawContext, state->clipRect);
1424 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
1425 CGContextEOFillPath (state->cgDrawContext);
1427 } else {
1428 CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
1432 static cairo_image_surface_t *
1433 _cairo_quartz_surface_map_to_image (void *abstract_surface,
1434 const cairo_rectangle_int_t *extents)
1436 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1437 unsigned int stride, bitinfo, bpp, color_comps;
1438 CGColorSpaceRef colorspace;
1439 void *imageData;
1440 cairo_format_t format;
1442 if (surface->imageSurfaceEquiv)
1443 return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
1445 if (IS_EMPTY (surface))
1446 return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1448 if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
1449 return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1451 bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1452 bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1454 // let's hope they don't add YUV under us
1455 colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1456 color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
1458 /* XXX TODO: We can handle many more data formats by
1459 * converting to pixman_format_t */
1461 if (bpp == 32 && color_comps == 3 &&
1462 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1463 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1465 format = CAIRO_FORMAT_ARGB32;
1467 else if (bpp == 32 && color_comps == 3 &&
1468 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1469 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1471 format = CAIRO_FORMAT_RGB24;
1473 else if (bpp == 8 && color_comps == 1)
1475 format = CAIRO_FORMAT_A1;
1477 else
1479 return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1482 imageData = CGBitmapContextGetData (surface->cgContext);
1483 stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1485 return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
1486 format,
1487 extents->width,
1488 extents->height,
1489 stride);
1492 static cairo_int_status_t
1493 _cairo_quartz_surface_unmap_image (void *abstract_surface,
1494 cairo_image_surface_t *image)
1496 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1498 if (surface->imageSurfaceEquiv)
1499 return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
1501 cairo_surface_finish (&image->base);
1502 cairo_surface_destroy (&image->base);
1504 return CAIRO_STATUS_SUCCESS;
1509 * Cairo surface backend implementations
1512 static cairo_status_t
1513 _cairo_quartz_surface_finish (void *abstract_surface)
1515 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1517 ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1519 if (IS_EMPTY (surface))
1520 return CAIRO_STATUS_SUCCESS;
1522 /* Restore our saved gstate that we use to reset clipping */
1523 CGContextRestoreGState (surface->cgContext);
1524 _cairo_surface_clipper_reset (&surface->clipper);
1526 CGContextRelease (surface->cgContext);
1528 surface->cgContext = NULL;
1530 if (surface->imageSurfaceEquiv) {
1531 cairo_surface_destroy (surface->imageSurfaceEquiv);
1532 surface->imageSurfaceEquiv = NULL;
1535 free (surface->imageData);
1536 surface->imageData = NULL;
1538 return CAIRO_STATUS_SUCCESS;
1541 static cairo_status_t
1542 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1543 cairo_image_surface_t **image_out,
1544 void **image_extra)
1546 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1548 //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1550 *image_extra = NULL;
1552 *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
1553 if (unlikely (cairo_surface_status(&(*image_out)->base))) {
1554 cairo_surface_destroy (&(*image_out)->base);
1555 *image_out = NULL;
1556 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1559 return CAIRO_STATUS_SUCCESS;
1562 static void
1563 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1564 cairo_image_surface_t *image,
1565 void *image_extra)
1567 _cairo_quartz_surface_unmap_image (abstract_surface, image);
1570 static cairo_surface_t *
1571 _cairo_quartz_surface_create_similar (void *abstract_surface,
1572 cairo_content_t content,
1573 int width,
1574 int height)
1576 cairo_quartz_surface_t *surface, *similar_quartz;
1577 cairo_surface_t *similar;
1578 cairo_format_t format;
1580 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1581 format = CAIRO_FORMAT_ARGB32;
1582 else if (content == CAIRO_CONTENT_COLOR)
1583 format = CAIRO_FORMAT_RGB24;
1584 else if (content == CAIRO_CONTENT_ALPHA)
1585 format = CAIRO_FORMAT_A8;
1586 else
1587 return NULL;
1589 // verify width and height of surface
1590 if (!_cairo_quartz_verify_surface_size (width, height)) {
1591 return _cairo_surface_create_in_error (_cairo_error
1592 (CAIRO_STATUS_INVALID_SIZE));
1595 similar = cairo_quartz_surface_create (format, width, height);
1596 if (unlikely (similar->status))
1597 return similar;
1599 surface = (cairo_quartz_surface_t *) abstract_surface;
1600 similar_quartz = (cairo_quartz_surface_t *) similar;
1601 similar_quartz->virtual_extents = surface->virtual_extents;
1603 return similar;
1606 static cairo_bool_t
1607 _cairo_quartz_surface_get_extents (void *abstract_surface,
1608 cairo_rectangle_int_t *extents)
1610 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1612 *extents = surface->extents;
1613 return TRUE;
1616 static cairo_int_status_t
1617 _cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
1618 cairo_composite_rectangles_t *extents)
1620 cairo_quartz_drawing_state_t state;
1621 cairo_int_status_t rv;
1623 ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
1624 extents->surface, extents->op, extents->source_pattern.base.type));
1626 rv = _cairo_quartz_setup_state (&state, extents);
1627 if (unlikely (rv))
1628 goto BAIL;
1630 _cairo_quartz_draw_source (&state, extents->op);
1632 BAIL:
1633 _cairo_quartz_teardown_state (&state, extents);
1635 ND ((stderr, "-- paint\n"));
1636 return rv;
1639 static cairo_int_status_t
1640 _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
1641 cairo_surface_t *mask_surf,
1642 const cairo_matrix_t *mask_mat,
1643 CGInterpolationQuality filter)
1645 CGRect rect;
1646 CGImageRef img;
1647 cairo_status_t status;
1648 CGAffineTransform mask_matrix;
1649 cairo_quartz_drawing_state_t state;
1650 cairo_format_t format = _cairo_format_from_content (extents->surface->content);
1651 cairo_rectangle_int_t dest_extents;
1652 cairo_matrix_t m = *mask_mat;
1654 _cairo_surface_get_extents (extents->surface, &dest_extents);
1655 status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
1656 &m, extents->clip, &img);
1657 if (unlikely (status))
1658 return status;
1660 status = _cairo_quartz_setup_state (&state, extents);
1661 if (unlikely (status))
1662 goto BAIL;
1664 rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
1665 _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
1667 /* ClipToMask is essentially drawing an image, so we need to flip the CTM
1668 * to get the image to appear oriented the right way */
1669 CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
1670 CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
1671 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1673 state.filter = filter;
1675 CGContextSetInterpolationQuality (state.cgMaskContext, filter);
1676 CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
1678 CGContextClipToMask (state.cgMaskContext, rect, img);
1680 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1681 CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
1682 CGContextConcatCTM (state.cgMaskContext, mask_matrix);
1684 _cairo_quartz_draw_source (&state, extents->op);
1686 BAIL:
1687 _cairo_quartz_teardown_state (&state, extents);
1689 CGImageRelease (img);
1691 return status;
1694 static cairo_int_status_t
1695 _cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
1696 cairo_composite_rectangles_t *extents)
1698 cairo_quartz_drawing_state_t state;
1699 double alpha = extents->mask_pattern.solid.color.alpha;
1700 cairo_status_t status;
1702 status = _cairo_quartz_setup_state (&state, extents);
1703 if (unlikely (status))
1704 return status;
1706 CGContextSetAlpha (surface->cgContext, alpha);
1707 _cairo_quartz_draw_source (&state, extents->op);
1709 _cairo_quartz_teardown_state (&state, extents);
1711 return CAIRO_STATUS_SUCCESS;
1714 static cairo_int_status_t
1715 _cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
1716 cairo_composite_rectangles_t *extents)
1718 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
1719 const cairo_pattern_t *source = &extents->source_pattern.base;
1720 const cairo_pattern_t *mask = &extents->mask_pattern.base;
1721 cairo_surface_t *mask_surf;
1722 cairo_matrix_t matrix;
1723 cairo_status_t status;
1724 cairo_bool_t need_temp;
1725 CGInterpolationQuality filter;
1727 ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
1728 extents->surface, extents->op, extents->source_pattern.base.type,
1729 extents->mask_pattern.base.type));
1731 if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
1732 return _cairo_quartz_cg_mask_with_solid (surface, extents);
1734 need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
1735 mask->extend != CAIRO_EXTEND_NONE);
1737 filter = _cairo_quartz_filter_to_quartz (source->filter);
1739 if (! need_temp) {
1740 mask_surf = extents->mask_pattern.surface.surface;
1742 /* When an opaque surface used as a mask in Quartz, its
1743 * luminosity is used as the alpha value, so we con only use
1744 * surfaces with alpha without creating a temporary mask. */
1745 need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
1748 if (! need_temp) {
1749 CGInterpolationQuality mask_filter;
1750 cairo_bool_t simple_transform;
1752 matrix = mask->matrix;
1754 mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
1755 if (mask_filter == kCGInterpolationNone) {
1756 simple_transform = _cairo_matrix_is_translation (&matrix);
1757 if (simple_transform) {
1758 matrix.x0 = ceil (matrix.x0 - 0.5);
1759 matrix.y0 = ceil (matrix.y0 - 0.5);
1761 } else {
1762 simple_transform = _cairo_matrix_is_integer_translation (&matrix,
1763 NULL,
1764 NULL);
1767 /* Quartz only allows one interpolation to be set for mask and
1768 * source, so we can skip the temp surface only if the source
1769 * filtering makes the mask look correct. */
1770 if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
1771 need_temp = ! (simple_transform || filter == mask_filter);
1772 else
1773 filter = mask_filter;
1776 if (need_temp) {
1777 /* Render the mask to a surface */
1778 mask_surf = _cairo_quartz_surface_create_similar (surface,
1779 CAIRO_CONTENT_ALPHA,
1780 surface->extents.width,
1781 surface->extents.height);
1782 status = mask_surf->status;
1783 if (unlikely (status))
1784 goto BAIL;
1786 /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
1787 * temporary layer or fallback to cairo-image. */
1788 status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
1789 if (unlikely (status))
1790 goto BAIL;
1792 cairo_matrix_init_identity (&matrix);
1795 status = _cairo_quartz_cg_mask_with_surface (extents,
1796 mask_surf, &matrix, filter);
1798 BAIL:
1800 if (need_temp)
1801 cairo_surface_destroy (mask_surf);
1803 return status;
1806 static cairo_int_status_t
1807 _cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
1808 cairo_composite_rectangles_t *extents,
1809 const cairo_path_fixed_t *path,
1810 cairo_fill_rule_t fill_rule,
1811 double tolerance,
1812 cairo_antialias_t antialias)
1814 cairo_quartz_drawing_state_t state;
1815 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1817 ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
1818 extents->surface, extents->op, extents->source_pattern.base.type));
1820 rv = _cairo_quartz_setup_state (&state, extents);
1821 if (unlikely (rv))
1822 goto BAIL;
1824 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1826 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1828 if (state.action == DO_DIRECT) {
1829 assert (state.cgDrawContext == state.cgMaskContext);
1830 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1831 CGContextFillPath (state.cgMaskContext);
1832 else
1833 CGContextEOFillPath (state.cgMaskContext);
1834 } else {
1835 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1836 CGContextClip (state.cgMaskContext);
1837 else
1838 CGContextEOClip (state.cgMaskContext);
1840 _cairo_quartz_draw_source (&state, extents->op);
1843 BAIL:
1844 _cairo_quartz_teardown_state (&state, extents);
1846 ND ((stderr, "-- fill\n"));
1847 return rv;
1850 static cairo_int_status_t
1851 _cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
1852 cairo_composite_rectangles_t *extents,
1853 const cairo_path_fixed_t *path,
1854 const cairo_stroke_style_t *style,
1855 const cairo_matrix_t *ctm,
1856 const cairo_matrix_t *ctm_inverse,
1857 double tolerance,
1858 cairo_antialias_t antialias)
1860 cairo_quartz_drawing_state_t state;
1861 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1862 CGAffineTransform strokeTransform, invStrokeTransform;
1864 ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
1865 extents->surface, extents->op, extents->source_pattern.base.type));
1867 rv = _cairo_quartz_setup_state (&state, extents);
1868 if (unlikely (rv))
1869 goto BAIL;
1871 // Turning antialiasing off used to cause misrendering with
1872 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1873 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1874 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1875 CGContextSetLineWidth (state.cgMaskContext, style->line_width);
1876 CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1877 CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1878 CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
1880 if (style->dash && style->num_dashes) {
1881 cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
1882 cairo_quartz_float_t *fdash = sdash;
1883 unsigned int max_dashes = style->num_dashes;
1884 unsigned int k;
1886 if (style->num_dashes%2)
1887 max_dashes *= 2;
1888 if (max_dashes > ARRAY_LENGTH (sdash))
1889 fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
1890 if (unlikely (fdash == NULL)) {
1891 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1892 goto BAIL;
1895 for (k = 0; k < max_dashes; k++)
1896 fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
1898 CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
1899 if (fdash != sdash)
1900 free (fdash);
1901 } else
1902 CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
1904 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1906 _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1907 CGContextConcatCTM (state.cgMaskContext, strokeTransform);
1909 if (state.action == DO_DIRECT) {
1910 assert (state.cgDrawContext == state.cgMaskContext);
1911 CGContextStrokePath (state.cgMaskContext);
1912 } else {
1913 CGContextReplacePathWithStrokedPath (state.cgMaskContext);
1914 CGContextClip (state.cgMaskContext);
1916 _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
1917 CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
1919 _cairo_quartz_draw_source (&state, extents->op);
1922 BAIL:
1923 _cairo_quartz_teardown_state (&state, extents);
1925 ND ((stderr, "-- stroke\n"));
1926 return rv;
1929 #if CAIRO_HAS_QUARTZ_FONT
1930 static cairo_int_status_t
1931 _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
1932 cairo_composite_rectangles_t *extents,
1933 cairo_scaled_font_t *scaled_font,
1934 cairo_glyph_t *glyphs,
1935 int num_glyphs,
1936 cairo_bool_t overlap)
1938 CGAffineTransform textTransform, invTextTransform;
1939 CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1940 CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1941 CGGlyph *cg_glyphs = &glyphs_static[0];
1942 CGSize *cg_advances = &cg_advances_static[0];
1943 COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
1945 cairo_quartz_drawing_state_t state;
1946 cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
1947 cairo_quartz_float_t xprev, yprev;
1948 int i;
1949 CGFontRef cgfref = NULL;
1951 cairo_bool_t didForceFontSmoothing = FALSE;
1953 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
1954 return CAIRO_INT_STATUS_UNSUPPORTED;
1956 rv = _cairo_quartz_setup_state (&state, extents);
1957 if (unlikely (rv))
1958 goto BAIL;
1960 if (state.action == DO_DIRECT) {
1961 assert (state.cgDrawContext == state.cgMaskContext);
1962 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
1963 } else {
1964 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
1967 /* this doesn't addref */
1968 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1969 CGContextSetFont (state.cgMaskContext, cgfref);
1970 CGContextSetFontSize (state.cgMaskContext, 1.0);
1972 switch (scaled_font->options.antialias) {
1973 case CAIRO_ANTIALIAS_SUBPIXEL:
1974 case CAIRO_ANTIALIAS_BEST:
1975 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1976 CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
1977 if (CGContextSetAllowsFontSmoothingPtr &&
1978 !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
1980 didForceFontSmoothing = TRUE;
1981 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
1983 break;
1984 case CAIRO_ANTIALIAS_NONE:
1985 CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
1986 break;
1987 case CAIRO_ANTIALIAS_GRAY:
1988 case CAIRO_ANTIALIAS_GOOD:
1989 case CAIRO_ANTIALIAS_FAST:
1990 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1991 CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
1992 break;
1993 case CAIRO_ANTIALIAS_DEFAULT:
1994 /* Don't do anything */
1995 break;
1998 if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
1999 cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
2000 if (unlikely (cg_glyphs == NULL)) {
2001 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2002 goto BAIL;
2005 cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
2008 /* scale(1,-1) * scaled_font->scale */
2009 textTransform = CGAffineTransformMake (scaled_font->scale.xx,
2010 scaled_font->scale.yx,
2011 -scaled_font->scale.xy,
2012 -scaled_font->scale.yy,
2013 0.0, 0.0);
2015 /* scaled_font->scale_inverse * scale(1,-1) */
2016 invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
2017 -scaled_font->scale_inverse.yx,
2018 scaled_font->scale_inverse.xy,
2019 -scaled_font->scale_inverse.yy,
2020 0.0, 0.0);
2022 CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
2023 CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
2025 /* Convert our glyph positions to glyph advances. We need n-1 advances,
2026 * since the advance at index 0 is applied after glyph 0. */
2027 xprev = glyphs[0].x;
2028 yprev = glyphs[0].y;
2030 cg_glyphs[0] = glyphs[0].index;
2032 for (i = 1; i < num_glyphs; i++) {
2033 cairo_quartz_float_t xf = glyphs[i].x;
2034 cairo_quartz_float_t yf = glyphs[i].y;
2035 cg_glyphs[i] = glyphs[i].index;
2036 cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
2037 xprev = xf;
2038 yprev = yf;
2041 /* Translate to the first glyph's position before drawing */
2042 CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
2043 CGContextConcatCTM (state.cgMaskContext, textTransform);
2045 CGContextShowGlyphsWithAdvances (state.cgMaskContext,
2046 cg_glyphs,
2047 cg_advances,
2048 num_glyphs);
2050 CGContextConcatCTM (state.cgMaskContext, invTextTransform);
2051 CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
2053 if (state.action != DO_DIRECT)
2054 _cairo_quartz_draw_source (&state, extents->op);
2056 BAIL:
2057 if (didForceFontSmoothing)
2058 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
2060 _cairo_quartz_teardown_state (&state, extents);
2062 if (cg_glyphs != glyphs_static)
2063 free (cg_glyphs);
2065 return rv;
2067 #endif /* CAIRO_HAS_QUARTZ_FONT */
2069 static const cairo_compositor_t _cairo_quartz_cg_compositor = {
2070 &_cairo_fallback_compositor,
2072 _cairo_quartz_cg_paint,
2073 _cairo_quartz_cg_mask,
2074 _cairo_quartz_cg_stroke,
2075 _cairo_quartz_cg_fill,
2076 #if CAIRO_HAS_QUARTZ_FONT
2077 _cairo_quartz_cg_glyphs,
2078 #else
2079 NULL,
2080 #endif
2083 static cairo_int_status_t
2084 _cairo_quartz_surface_paint (void *surface,
2085 cairo_operator_t op,
2086 const cairo_pattern_t *source,
2087 const cairo_clip_t *clip)
2089 return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
2090 surface, op, source, clip);
2093 static cairo_int_status_t
2094 _cairo_quartz_surface_mask (void *surface,
2095 cairo_operator_t op,
2096 const cairo_pattern_t *source,
2097 const cairo_pattern_t *mask,
2098 const cairo_clip_t *clip)
2100 return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
2101 surface, op, source, mask,
2102 clip);
2105 static cairo_int_status_t
2106 _cairo_quartz_surface_fill (void *surface,
2107 cairo_operator_t op,
2108 const cairo_pattern_t *source,
2109 const cairo_path_fixed_t *path,
2110 cairo_fill_rule_t fill_rule,
2111 double tolerance,
2112 cairo_antialias_t antialias,
2113 const cairo_clip_t *clip)
2115 return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
2116 surface, op, source, path,
2117 fill_rule, tolerance, antialias,
2118 clip);
2121 static cairo_int_status_t
2122 _cairo_quartz_surface_stroke (void *surface,
2123 cairo_operator_t op,
2124 const cairo_pattern_t *source,
2125 const cairo_path_fixed_t *path,
2126 const cairo_stroke_style_t *style,
2127 const cairo_matrix_t *ctm,
2128 const cairo_matrix_t *ctm_inverse,
2129 double tolerance,
2130 cairo_antialias_t antialias,
2131 const cairo_clip_t *clip)
2133 return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
2134 surface, op, source, path,
2135 style, ctm,ctm_inverse,
2136 tolerance, antialias, clip);
2139 static cairo_int_status_t
2140 _cairo_quartz_surface_glyphs (void *surface,
2141 cairo_operator_t op,
2142 const cairo_pattern_t *source,
2143 cairo_glyph_t *glyphs,
2144 int num_glyphs,
2145 cairo_scaled_font_t *scaled_font,
2146 const cairo_clip_t *clip)
2148 return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
2149 surface, op, source,
2150 glyphs, num_glyphs, scaled_font,
2151 clip);
2154 static cairo_status_t
2155 _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
2156 cairo_path_fixed_t *path,
2157 cairo_fill_rule_t fill_rule,
2158 double tolerance,
2159 cairo_antialias_t antialias)
2161 cairo_quartz_surface_t *surface =
2162 cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
2164 ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2166 if (IS_EMPTY (surface))
2167 return CAIRO_STATUS_SUCCESS;
2169 if (path == NULL) {
2170 /* If we're being asked to reset the clip, we can only do it
2171 * by restoring the gstate to our previous saved one, and
2172 * saving it again.
2174 * Note that this assumes that ALL quartz surface creation
2175 * functions will do a SaveGState first; we do this in create_internal.
2177 CGContextRestoreGState (surface->cgContext);
2178 CGContextSaveGState (surface->cgContext);
2179 } else {
2180 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2182 _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
2184 if (fill_rule == CAIRO_FILL_RULE_WINDING)
2185 CGContextClip (surface->cgContext);
2186 else
2187 CGContextEOClip (surface->cgContext);
2190 ND ((stderr, "-- intersect_clip_path\n"));
2192 return CAIRO_STATUS_SUCCESS;
2195 // XXXtodo implement show_page; need to figure out how to handle begin/end
2197 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2198 CAIRO_SURFACE_TYPE_QUARTZ,
2199 _cairo_quartz_surface_finish,
2201 _cairo_default_context_create,
2203 _cairo_quartz_surface_create_similar,
2204 NULL, /* similar image */
2205 _cairo_quartz_surface_map_to_image,
2206 _cairo_quartz_surface_unmap_image,
2208 _cairo_surface_default_source,
2209 _cairo_quartz_surface_acquire_source_image,
2210 _cairo_quartz_surface_release_source_image,
2211 NULL, /* snapshot */
2213 NULL, /* copy_page */
2214 NULL, /* show_page */
2216 _cairo_quartz_surface_get_extents,
2217 NULL, /* get_font_options */
2219 NULL, /* flush */
2220 NULL, /* mark_dirty_rectangle */
2222 _cairo_quartz_surface_paint,
2223 _cairo_quartz_surface_mask,
2224 _cairo_quartz_surface_stroke,
2225 _cairo_quartz_surface_fill,
2226 NULL, /* fill-stroke */
2227 _cairo_quartz_surface_glyphs,
2230 cairo_quartz_surface_t *
2231 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2232 cairo_content_t content,
2233 unsigned int width,
2234 unsigned int height)
2236 cairo_quartz_surface_t *surface;
2238 quartz_ensure_symbols ();
2240 /* Init the base surface */
2241 surface = malloc (sizeof (cairo_quartz_surface_t));
2242 if (unlikely (surface == NULL))
2243 return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2245 memset (surface, 0, sizeof (cairo_quartz_surface_t));
2247 _cairo_surface_init (&surface->base,
2248 &cairo_quartz_surface_backend,
2249 NULL, /* device */
2250 content);
2252 _cairo_surface_clipper_init (&surface->clipper,
2253 _cairo_quartz_surface_clipper_intersect_clip_path);
2255 /* Save our extents */
2256 surface->extents.x = surface->extents.y = 0;
2257 surface->extents.width = width;
2258 surface->extents.height = height;
2259 surface->virtual_extents = surface->extents;
2261 if (IS_EMPTY (surface)) {
2262 surface->cgContext = NULL;
2263 surface->cgContextBaseCTM = CGAffineTransformIdentity;
2264 surface->imageData = NULL;
2265 surface->base.is_clear = TRUE;
2266 return surface;
2269 /* Save so we can always get back to a known-good CGContext -- this is
2270 * required for proper behaviour of intersect_clip_path(NULL)
2272 CGContextSaveGState (cgContext);
2274 surface->cgContext = cgContext;
2275 surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2277 surface->imageData = NULL;
2278 surface->imageSurfaceEquiv = NULL;
2280 return surface;
2284 * cairo_quartz_surface_create_for_cg_context:
2285 * @cgContext: the existing CGContext for which to create the surface
2286 * @width: width of the surface, in pixels
2287 * @height: height of the surface, in pixels
2289 * Creates a Quartz surface that wraps the given CGContext. The
2290 * CGContext is assumed to be in the standard Cairo coordinate space
2291 * (that is, with the origin at the upper left and the Y axis
2292 * increasing downward). If the CGContext is in the Quartz coordinate
2293 * space (with the origin at the bottom left), then it should be
2294 * flipped before this function is called. The flip can be accomplished
2295 * using a translate and a scale; for example:
2297 * <informalexample><programlisting>
2298 * CGContextTranslateCTM (cgContext, 0.0, height);
2299 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2300 * </programlisting></informalexample>
2302 * All Cairo operations are implemented in terms of Quartz operations,
2303 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2305 * Return value: the newly created Cairo surface.
2307 * Since: 1.6
2310 cairo_surface_t *
2311 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2312 unsigned int width,
2313 unsigned int height)
2315 cairo_quartz_surface_t *surf;
2317 surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2318 width, height);
2319 if (likely (!surf->base.status))
2320 CGContextRetain (cgContext);
2322 return &surf->base;
2326 * cairo_quartz_surface_create:
2327 * @format: format of pixels in the surface to create
2328 * @width: width of the surface, in pixels
2329 * @height: height of the surface, in pixels
2331 * Creates a Quartz surface backed by a CGBitmap. The surface is
2332 * created using the Device RGB (or Device Gray, for A8) color space.
2333 * All Cairo operations, including those that require software
2334 * rendering, will succeed on this surface.
2336 * Return value: the newly created surface.
2338 * Since: 1.6
2340 cairo_surface_t *
2341 cairo_quartz_surface_create (cairo_format_t format,
2342 unsigned int width,
2343 unsigned int height)
2345 cairo_quartz_surface_t *surf;
2346 CGContextRef cgc;
2347 CGColorSpaceRef cgColorspace;
2348 CGBitmapInfo bitinfo;
2349 void *imageData;
2350 int stride;
2351 int bitsPerComponent;
2353 if (!_cairo_quartz_verify_surface_size (width, height))
2354 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2356 if (width == 0 || height == 0) {
2357 return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2358 width, height)->base;
2361 if (format == CAIRO_FORMAT_ARGB32 ||
2362 format == CAIRO_FORMAT_RGB24)
2364 cgColorspace = CGColorSpaceCreateDeviceRGB ();
2365 bitinfo = kCGBitmapByteOrder32Host;
2366 if (format == CAIRO_FORMAT_ARGB32)
2367 bitinfo |= kCGImageAlphaPremultipliedFirst;
2368 else
2369 bitinfo |= kCGImageAlphaNoneSkipFirst;
2370 bitsPerComponent = 8;
2371 stride = width * 4;
2372 } else if (format == CAIRO_FORMAT_A8) {
2373 cgColorspace = NULL;
2374 stride = width;
2375 bitinfo = kCGImageAlphaOnly;
2376 bitsPerComponent = 8;
2377 } else if (format == CAIRO_FORMAT_A1) {
2378 /* I don't think we can usefully support this, as defined by
2379 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2380 * quantities.
2382 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2383 } else {
2384 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2387 /* The Apple docs say that for best performance, the stride and the data
2388 * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2389 * so we don't have to anything special on allocation.
2391 stride = (stride + 15) & ~15;
2393 imageData = _cairo_malloc_ab (height, stride);
2394 if (unlikely (!imageData)) {
2395 CGColorSpaceRelease (cgColorspace);
2396 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2399 /* zero the memory to match the image surface behaviour */
2400 memset (imageData, 0, height * stride);
2402 cgc = CGBitmapContextCreate (imageData,
2403 width,
2404 height,
2405 bitsPerComponent,
2406 stride,
2407 cgColorspace,
2408 bitinfo);
2409 CGColorSpaceRelease (cgColorspace);
2411 if (!cgc) {
2412 free (imageData);
2413 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2416 /* flip the Y axis */
2417 CGContextTranslateCTM (cgc, 0.0, height);
2418 CGContextScaleCTM (cgc, 1.0, -1.0);
2420 surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2421 width, height);
2422 if (surf->base.status) {
2423 CGContextRelease (cgc);
2424 free (imageData);
2425 // create_internal will have set an error
2426 return &surf->base;
2429 surf->base.is_clear = TRUE;
2431 surf->imageData = imageData;
2432 surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2434 return &surf->base;
2438 * cairo_quartz_surface_get_cg_context:
2439 * @surface: the Cairo Quartz surface
2441 * Returns the CGContextRef that the given Quartz surface is backed
2442 * by.
2444 * A call to cairo_surface_flush() is required before using the
2445 * CGContextRef to ensure that all pending drawing operations are
2446 * finished and to restore any temporary modification cairo has made
2447 * to its state. A call to cairo_surface_mark_dirty() is required
2448 * after the state or the content of the CGContextRef has been
2449 * modified.
2451 * Return value: the CGContextRef for the given surface.
2453 * Since: 1.6
2455 CGContextRef
2456 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2458 if (surface && _cairo_surface_is_quartz (surface)) {
2459 cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
2460 return quartz->cgContext;
2461 } else
2462 return NULL;
2466 * _cairo_surface_is_quartz:
2467 * @surface: a #cairo_surface_t
2469 * Checks if a surface is a #cairo_quartz_surface_t
2471 * Return value: True if the surface is an quartz surface
2473 cairo_bool_t
2474 _cairo_surface_is_quartz (const cairo_surface_t *surface)
2476 return surface->backend == &cairo_quartz_surface_backend;
2479 /* Debug stuff */
2481 #ifdef QUARTZ_DEBUG
2483 #include <Movies.h>
2485 void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
2487 Handle dataRef = NULL;
2488 OSType dataRefType;
2489 CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
2491 GraphicsExportComponent grex = 0;
2492 unsigned long sizeWritten;
2494 ComponentResult result;
2496 // create the data reference
2497 result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
2498 0, &dataRef, &dataRefType);
2500 if (NULL != dataRef && noErr == result) {
2501 // get the PNG exporter
2502 result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
2503 &grex);
2505 if (grex) {
2506 // tell the exporter where to find its source image
2507 result = GraphicsExportSetInputCGImage (grex, inImageRef);
2509 if (noErr == result) {
2510 // tell the exporter where to save the exporter image
2511 result = GraphicsExportSetOutputDataReference (grex, dataRef,
2512 dataRefType);
2514 if (noErr == result) {
2515 // write the PNG file
2516 result = GraphicsExportDoExport (grex, &sizeWritten);
2520 // remember to close the component
2521 CloseComponent (grex);
2524 // remember to dispose of the data reference handle
2525 DisposeHandle (dataRef);
2529 void
2530 quartz_image_to_png (CGImageRef imgref, char *dest)
2532 static int sctr = 0;
2533 char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2535 if (dest == NULL) {
2536 fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2537 sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2538 sctr++;
2539 dest = sptr;
2542 ExportCGImageToPNGFile (imgref, dest);
2545 void
2546 quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2548 static int sctr = 0;
2549 char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2551 if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2552 fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2553 return;
2556 if (dest == NULL) {
2557 fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2558 sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2559 sctr++;
2560 dest = sptr;
2563 CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2564 if (imgref == NULL) {
2565 fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2566 return;
2569 ExportCGImageToPNGFile (imgref, dest);
2571 CGImageRelease (imgref);
2574 #endif /* QUARTZ_DEBUG */