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.
34 * Vladimir Vukicevic <vladimir@mozilla.com>
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
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"
55 #define RTLD_DEFAULT ((void *) 0)
63 #define ND(_x) fprintf _x
65 #define ND(_x) do {} while(0)
68 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
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.
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.
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
);
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
;
123 static unsigned int (*CGContextGetTypePtr
) (CGContextRef
) = NULL
;
124 static bool (*CGContextGetAllowsFontSmoothingPtr
) (CGContextRef
) = NULL
;
126 static cairo_bool_t _cairo_quartz_symbol_lookup_done
= FALSE
;
133 static void quartz_surface_to_png (cairo_quartz_surface_t
*nq
, char *dest
);
134 static void quartz_image_to_png (CGImageRef
, char *dest
);
137 static cairo_quartz_surface_t
*
138 _cairo_quartz_surface_create_internal (CGContextRef cgContext
,
139 cairo_content_t content
,
141 unsigned int height
);
143 /* Load all extra symbols */
144 static void quartz_ensure_symbols (void)
146 if (likely (_cairo_quartz_symbol_lookup_done
))
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
;
159 CairoQuartzCreateCGImage (cairo_format_t format
,
164 cairo_bool_t interpolate
,
165 CGColorSpaceRef colorSpaceOverride
,
166 CGDataProviderReleaseDataCallback releaseCallback
,
169 CGImageRef image
= NULL
;
170 CGDataProviderRef dataProvider
= NULL
;
171 CGColorSpaceRef colorSpace
= colorSpaceOverride
;
172 CGBitmapInfo bitinfo
= kCGBitmapByteOrder32Host
;
173 int bitsPerComponent
, bitsPerPixel
;
176 case CAIRO_FORMAT_ARGB32
:
177 if (colorSpace
== NULL
)
178 colorSpace
= CGColorSpaceCreateDeviceRGB ();
179 bitinfo
|= kCGImageAlphaPremultipliedFirst
;
180 bitsPerComponent
= 8;
184 case CAIRO_FORMAT_RGB24
:
185 if (colorSpace
== NULL
)
186 colorSpace
= CGColorSpaceCreateDeviceRGB ();
187 bitinfo
|= kCGImageAlphaNoneSkipFirst
;
188 bitsPerComponent
= 8;
192 case CAIRO_FORMAT_A8
:
193 bitsPerComponent
= 8;
197 case CAIRO_FORMAT_A1
:
198 #ifdef WORDS_BIGENDIAN
199 bitsPerComponent
= 1;
204 case CAIRO_FORMAT_RGB30
:
205 case CAIRO_FORMAT_RGB16_565
:
206 case CAIRO_FORMAT_INVALID
:
211 dataProvider
= CGDataProviderCreateWithData (releaseInfo
,
216 if (unlikely (!dataProvider
)) {
219 releaseCallback (releaseInfo
, data
, height
* stride
);
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
,
233 image
= CGImageCreate (width
, height
,
242 kCGRenderingIntentDefault
);
246 CGDataProviderRelease (dataProvider
);
248 if (colorSpace
!= colorSpaceOverride
)
249 CGColorSpaceRelease (colorSpace
);
254 static inline cairo_bool_t
255 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc
)
257 if (unlikely (cgc
== NULL
))
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? */
276 _cairo_quartz_verify_surface_size (int width
, int height
)
278 /* hmmm, allow width, height == 0 ? */
279 if (width
< 0 || height
< 0)
282 if (width
> CG_MAX_WIDTH
|| height
> CG_MAX_HEIGHT
)
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
;
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
,
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
)
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
:
421 _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t 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
;
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
:
495 case CAIRO_OPERATOR_DEST
:
496 case CAIRO_OPERATOR_SATURATE
:
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
;
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
)
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
)
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
)
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
;
621 return kCGInterpolationDefault
;
626 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t
*src
,
627 CGAffineTransform
*dst
)
639 * Source -> Quartz setup and finish functions
643 ComputeGradientValue (void *info
,
644 const cairo_quartz_float_t
*in
,
645 cairo_quartz_float_t
*out
)
648 const cairo_gradient_pattern_t
*grad
= (cairo_gradient_pattern_t
*) info
;
651 /* Put fdist back in the 0.0..1.0 range if we're doing
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);
662 for (i
= 0; i
< grad
->n_stops
; i
++)
663 if (grad
->stops
[i
].offset
> fdist
)
666 if (i
== 0 || i
== grad
->n_stops
) {
667 if (i
== grad
->n_stops
)
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
;
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
;
680 grad
->stops
[i
-1].color
.red
* ap
+
681 grad
->stops
[i
].color
.red
* bp
;
683 grad
->stops
[i
-1].color
.green
* ap
+
684 grad
->stops
[i
].color
.green
* bp
;
686 grad
->stops
[i
-1].color
.blue
* ap
+
687 grad
->stops
[i
].color
.blue
* bp
;
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
704 #define MAX_GRADIENT_RANGE 1024
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
,
731 _cairo_gradient_pattern_box_to_parameter (gradient
,
732 bounds_x1
, bounds_y1
,
733 bounds_x2
, bounds_y2
,
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
)
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];
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
))
758 return CGFunctionCreate (pat
,
762 gradient_output_value_ranges
,
763 &gradient_callbacks
);
766 /* Obtain a CGImageRef from a #cairo_surface_t * */
769 cairo_surface_t
*surface
;
770 cairo_image_surface_t
*image_out
;
772 } quartz_source_image_t
;
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
);
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
)) {
804 return CAIRO_INT_STATUS_NOTHING_TO_DO
;
807 if (_cairo_quartz_is_cgcontext_bitmap_context (surface
->cgContext
)) {
808 *image_out
= CGBitmapContextCreateImage (surface
->cgContext
);
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
);
830 status
= _cairo_recording_surface_replay_with_clip (source
,
832 &image_surface
->base
,
834 if (unlikely (status
)) {
835 cairo_surface_destroy (&image_surface
->base
);
840 source_img
->image_out
= image_surface
;
841 source_img
->image_extra
= NULL
;
843 cairo_matrix_init_identity (matrix
);
846 status
= _cairo_surface_acquire_source_image (source_img
->surface
,
847 &source_img
->image_out
,
848 &source_img
->image_extra
);
849 if (unlikely (status
)) {
855 if (source_img
->image_out
->width
== 0 || source_img
->image_out
->height
== 0) {
857 DataProviderReleaseCallback (source_img
,
858 source_img
->image_out
->data
,
859 source_img
->image_out
->height
* source_img
->image_out
->stride
);
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
,
868 DataProviderReleaseCallback
,
871 /* TODO: differentiate memory error and unsupported surface type */
872 if (unlikely (*image_out
== NULL
))
873 status
= CAIRO_INT_STATUS_UNSUPPORTED
;
879 /* Generic #cairo_pattern_t -> CGPattern function */
884 cairo_bool_t do_reflect
;
885 } SurfacePatternDrawInfo
;
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
);
921 SurfacePatternReleaseInfoFunc (void *ainfo
)
923 SurfacePatternDrawInfo
*info
= (SurfacePatternDrawInfo
*) ainfo
;
925 CGImageRelease (info
->image
);
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
,
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
);
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
;
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
);
964 _cairo_surface_get_extents (&dest
->base
, &extents
);
966 m
= spattern
->base
.matrix
;
967 status
= _cairo_surface_to_cgimage (pat_surf
, &extents
, format
,
969 if (unlikely (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.
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
;
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
);
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
));
1020 *cgpat
= CGPatternCreate (info
,
1024 kCGPatternTilingConstantSpacing
, /* kCGPatternTilingNoDistortion, */
1028 return CAIRO_STATUS_SUCCESS
;
1031 /* State used during a drawing operation. */
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
;
1043 cairo_quartz_action_t action
;
1045 /* Destination rect */
1048 /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1049 CGAffineTransform transform
;
1051 /* Used with DO_IMAGE and DO_TILED_IMAGE */
1054 /* Used with DO_SHADING */
1055 CGShadingRef shading
;
1057 /* Temporary destination for unbounded operations */
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
)
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
,
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
,
1101 CGPointMake (end
.center
.x
,
1106 state
->shading
= CGShadingCreateRadial (rgb
,
1107 CGPointMake (start
.center
.x
,
1109 MAX (start
.radius
, 0),
1110 CGPointMake (end
.center
.x
,
1112 MAX (end
.radius
, 0),
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
))
1146 status
= _cairo_quartz_surface_set_cairo_operator (surface
, op
);
1147 if (unlikely (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
1180 * - CLEAR completely ignores the source, thus we can just use a
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
);
1193 state
->layer
= CGLayerCreateWithContext (surface
->cgContext
,
1194 state
->clipRect
.size
,
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
,
1210 solid
->color
.alpha
);
1211 CGContextSetRGBFillColor (state
->cgDrawContext
,
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
;
1241 cairo_matrix_t m
= spat
->base
.matrix
;
1242 cairo_rectangle_int_t extents
;
1243 CGAffineTransform xform
;
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
,
1251 if (unlikely (status
))
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);
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
) {
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
,
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
),
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
))
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
;
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
;
1371 CGContextDrawLayerInRect (surface
->cgContext
,
1374 CGLayerRelease (state
->layer
);
1377 if (state
->cgMaskContext
)
1378 CGContextRestoreGState (surface
->cgContext
);
1381 CGImageRelease (state
->image
);
1384 CGShadingRelease (state
->shading
);
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
);
1399 CGContextConcatCTM (state
->cgDrawContext
, state
->transform
);
1401 if (state
->action
== DO_SHADING
) {
1402 CGContextDrawShading (state
->cgDrawContext
, state
->shading
);
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
);
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
;
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
;
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
,
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
,
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
);
1556 return _cairo_error (CAIRO_STATUS_NO_MEMORY
);
1559 return CAIRO_STATUS_SUCCESS
;
1563 _cairo_quartz_surface_release_source_image (void *abstract_surface
,
1564 cairo_image_surface_t
*image
,
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
,
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
;
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
))
1599 surface
= (cairo_quartz_surface_t
*) abstract_surface
;
1600 similar_quartz
= (cairo_quartz_surface_t
*) similar
;
1601 similar_quartz
->virtual_extents
= surface
->virtual_extents
;
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
;
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
);
1630 _cairo_quartz_draw_source (&state
, extents
->op
);
1633 _cairo_quartz_teardown_state (&state
, extents
);
1635 ND ((stderr
, "-- paint\n"));
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
)
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
))
1660 status
= _cairo_quartz_setup_state (&state
, extents
);
1661 if (unlikely (status
))
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
);
1687 _cairo_quartz_teardown_state (&state
, extents
);
1689 CGImageRelease (img
);
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
))
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
);
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
);
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);
1762 simple_transform
= _cairo_matrix_is_integer_translation (&matrix
,
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
);
1773 filter
= mask_filter
;
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
))
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
))
1792 cairo_matrix_init_identity (&matrix
);
1795 status
= _cairo_quartz_cg_mask_with_surface (extents
,
1796 mask_surf
, &matrix
, filter
);
1801 cairo_surface_destroy (mask_surf
);
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
,
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
);
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
);
1833 CGContextEOFillPath (state
.cgMaskContext
);
1835 if (fill_rule
== CAIRO_FILL_RULE_WINDING
)
1836 CGContextClip (state
.cgMaskContext
);
1838 CGContextEOClip (state
.cgMaskContext
);
1840 _cairo_quartz_draw_source (&state
, extents
->op
);
1844 _cairo_quartz_teardown_state (&state
, extents
);
1846 ND ((stderr
, "-- fill\n"));
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
,
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
);
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
;
1886 if (style
->num_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
);
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
);
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
);
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
);
1923 _cairo_quartz_teardown_state (&state
, extents
);
1925 ND ((stderr
, "-- stroke\n"));
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
,
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
;
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
);
1960 if (state
.action
== DO_DIRECT
) {
1961 assert (state
.cgDrawContext
== state
.cgMaskContext
);
1962 CGContextSetTextDrawingMode (state
.cgMaskContext
, kCGTextFill
);
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
);
1984 case CAIRO_ANTIALIAS_NONE
:
1985 CGContextSetShouldAntialias (state
.cgMaskContext
, FALSE
);
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
);
1993 case CAIRO_ANTIALIAS_DEFAULT
:
1994 /* Don't do anything */
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
);
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
,
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
,
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
);
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
,
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
);
2057 if (didForceFontSmoothing
)
2058 CGContextSetAllowsFontSmoothingPtr (state
.cgMaskContext
, FALSE
);
2060 _cairo_quartz_teardown_state (&state
, extents
);
2062 if (cg_glyphs
!= glyphs_static
)
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
,
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
;
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
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
);
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
);
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 */
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
,
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
,
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
;
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
;
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.
2311 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext
,
2313 unsigned int height
)
2315 cairo_quartz_surface_t
*surf
;
2317 surf
= _cairo_quartz_surface_create_internal (cgContext
, CAIRO_CONTENT_COLOR_ALPHA
,
2319 if (likely (!surf
->base
.status
))
2320 CGContextRetain (cgContext
);
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.
2341 cairo_quartz_surface_create (cairo_format_t format
,
2343 unsigned int height
)
2345 cairo_quartz_surface_t
*surf
;
2347 CGColorSpaceRef cgColorspace
;
2348 CGBitmapInfo bitinfo
;
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
;
2369 bitinfo
|= kCGImageAlphaNoneSkipFirst
;
2370 bitsPerComponent
= 8;
2372 } else if (format
== CAIRO_FORMAT_A8
) {
2373 cgColorspace
= NULL
;
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
2382 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT
));
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
,
2409 CGColorSpaceRelease (cgColorspace
);
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
),
2422 if (surf
->base
.status
) {
2423 CGContextRelease (cgc
);
2425 // create_internal will have set an error
2429 surf
->base
.is_clear
= TRUE
;
2431 surf
->imageData
= imageData
;
2432 surf
->imageSurfaceEquiv
= cairo_image_surface_create_for_data (imageData
, format
, width
, height
, stride
);
2438 * cairo_quartz_surface_get_cg_context:
2439 * @surface: the Cairo Quartz surface
2441 * Returns the CGContextRef that the given Quartz surface is backed
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
2451 * Return value: the CGContextRef for the given surface.
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
;
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
2474 _cairo_surface_is_quartz (const cairo_surface_t
*surface
)
2476 return surface
->backend
== &cairo_quartz_surface_backend
;
2485 void ExportCGImageToPNGFile (CGImageRef inImageRef
, char* dest
)
2487 Handle dataRef
= NULL
;
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
,
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
,
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
);
2530 quartz_image_to_png (CGImageRef imgref
, char *dest
)
2532 static int sctr
= 0;
2533 char sptr
[] = "/Users/vladimir/Desktop/barXXXXX.png";
2536 fprintf (stderr
, "** Writing %p to bar%d\n", imgref
, sctr
);
2537 sprintf (sptr
, "/Users/vladimir/Desktop/bar%d.png", sctr
);
2542 ExportCGImageToPNGFile (imgref
, dest
);
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
);
2557 fprintf (stderr
, "** Writing %p to foo%d\n", nq
, sctr
);
2558 sprintf (sptr
, "/Users/vladimir/Desktop/foo%d.png", sctr
);
2563 CGImageRef imgref
= CGBitmapContextCreateImage (nq
->cgContext
);
2564 if (imgref
== NULL
) {
2565 fprintf (stderr
, "quartz surface at %p is not a bitmap context!\n", nq
);
2569 ExportCGImageToPNGFile (imgref
, dest
);
2571 CGImageRelease (imgref
);
2574 #endif /* QUARTZ_DEBUG */