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