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-private.h"
47 #include "cairo-pattern-private.h"
48 #include "cairo-surface-backend-private.h"
49 #include "cairo-surface-clipper-private.h"
54 #define RTLD_DEFAULT ((void *) 0)
62 #define ND(_x) fprintf _x
64 #define ND(_x) do {} while(0)
67 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
70 * SECTION:cairo-quartz
71 * @Title: Quartz Surfaces
72 * @Short_Description: Rendering to Quartz surfaces
73 * @See_Also: #cairo_surface_t
75 * The Quartz surface is used to render cairo graphics targeting the
76 * Apple OS X Quartz rendering system.
80 * CAIRO_HAS_QUARTZ_SURFACE:
82 * Defined if the Quartz surface backend is available.
83 * This macro can be used to conditionally compile backend-specific code.
88 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
89 /* This method is private, but it exists. Its params are are exposed
90 * as args to the NS* method, but not as CG.
92 enum PrivateCGCompositeMode {
93 kPrivateCGCompositeClear = 0,
94 kPrivateCGCompositeCopy = 1,
95 kPrivateCGCompositeSourceOver = 2,
96 kPrivateCGCompositeSourceIn = 3,
97 kPrivateCGCompositeSourceOut = 4,
98 kPrivateCGCompositeSourceAtop = 5,
99 kPrivateCGCompositeDestinationOver = 6,
100 kPrivateCGCompositeDestinationIn = 7,
101 kPrivateCGCompositeDestinationOut = 8,
102 kPrivateCGCompositeDestinationAtop = 9,
103 kPrivateCGCompositeXOR = 10,
104 kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
105 kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
107 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
108 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
111 /* Some of these are present in earlier versions of the OS than where
112 * they are public; other are not public at all
114 /* public since 10.5 */
115 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
117 /* public since 10.6 */
118 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
119 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
122 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
123 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
125 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
132 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
133 static void quartz_image_to_png (CGImageRef, char *dest);
136 static cairo_quartz_surface_t *
137 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
138 cairo_content_t content,
140 unsigned int height);
143 _cairo_surface_is_quartz (const cairo_surface_t *surface);
145 /* Load all extra symbols */
146 static void quartz_ensure_symbols (void)
148 if (likely (_cairo_quartz_symbol_lookup_done))
151 CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
152 CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
153 CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
154 CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
155 CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
157 _cairo_quartz_symbol_lookup_done = TRUE;
161 CairoQuartzCreateCGImage (cairo_format_t format,
166 cairo_bool_t interpolate,
167 CGColorSpaceRef colorSpaceOverride,
168 CGDataProviderReleaseDataCallback releaseCallback,
171 CGImageRef image = NULL;
172 CGDataProviderRef dataProvider = NULL;
173 CGColorSpaceRef colorSpace = colorSpaceOverride;
174 CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
175 int bitsPerComponent, bitsPerPixel;
178 case CAIRO_FORMAT_ARGB32:
179 if (colorSpace == NULL)
180 colorSpace = CGColorSpaceCreateDeviceRGB ();
181 bitinfo |= kCGImageAlphaPremultipliedFirst;
182 bitsPerComponent = 8;
186 case CAIRO_FORMAT_RGB24:
187 if (colorSpace == NULL)
188 colorSpace = CGColorSpaceCreateDeviceRGB ();
189 bitinfo |= kCGImageAlphaNoneSkipFirst;
190 bitsPerComponent = 8;
194 case CAIRO_FORMAT_A8:
195 bitsPerComponent = 8;
199 case CAIRO_FORMAT_A1:
200 #ifdef WORDS_BIGENDIAN
201 bitsPerComponent = 1;
206 case CAIRO_FORMAT_RGB16_565:
207 case CAIRO_FORMAT_INVALID:
212 dataProvider = CGDataProviderCreateWithData (releaseInfo,
217 if (unlikely (!dataProvider)) {
220 releaseCallback (releaseInfo, data, height * stride);
224 if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
225 cairo_quartz_float_t decode[] = {1.0, 0.0};
226 image = CGImageMaskCreate (width, height,
234 image = CGImageCreate (width, height,
243 kCGRenderingIntentDefault);
247 CGDataProviderRelease (dataProvider);
249 if (colorSpace != colorSpaceOverride)
250 CGColorSpaceRelease (colorSpace);
255 static inline cairo_bool_t
256 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
258 if (unlikely (cgc == NULL))
261 if (likely (CGContextGetTypePtr)) {
262 /* 4 is the type value of a bitmap context */
263 return CGContextGetTypePtr (cgc) == 4;
266 /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
267 return CGBitmapContextGetBitsPerPixel (cgc) != 0;
270 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
272 #define CG_MAX_HEIGHT SHRT_MAX
273 #define CG_MAX_WIDTH USHRT_MAX
275 /* is the desired size of the surface within bounds? */
277 _cairo_quartz_verify_surface_size (int width, int height)
279 /* hmmm, allow width, height == 0 ? */
280 if (width < 0 || height < 0)
283 if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
290 * Cairo path -> Quartz path conversion helpers
293 /* cairo path -> execute in context */
294 static cairo_status_t
295 _cairo_path_to_quartz_context_move_to (void *closure,
296 const cairo_point_t *point)
298 //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
299 double x = _cairo_fixed_to_double (point->x);
300 double y = _cairo_fixed_to_double (point->y);
302 CGContextMoveToPoint (closure, x, y);
303 return CAIRO_STATUS_SUCCESS;
306 static cairo_status_t
307 _cairo_path_to_quartz_context_line_to (void *closure,
308 const cairo_point_t *point)
310 //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
311 double x = _cairo_fixed_to_double (point->x);
312 double y = _cairo_fixed_to_double (point->y);
314 CGContextAddLineToPoint (closure, x, y);
315 return CAIRO_STATUS_SUCCESS;
318 static cairo_status_t
319 _cairo_path_to_quartz_context_curve_to (void *closure,
320 const cairo_point_t *p0,
321 const cairo_point_t *p1,
322 const cairo_point_t *p2)
324 //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
325 // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
326 // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
327 // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
328 double x0 = _cairo_fixed_to_double (p0->x);
329 double y0 = _cairo_fixed_to_double (p0->y);
330 double x1 = _cairo_fixed_to_double (p1->x);
331 double y1 = _cairo_fixed_to_double (p1->y);
332 double x2 = _cairo_fixed_to_double (p2->x);
333 double y2 = _cairo_fixed_to_double (p2->y);
335 CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
336 return CAIRO_STATUS_SUCCESS;
339 static cairo_status_t
340 _cairo_path_to_quartz_context_close_path (void *closure)
342 //ND ((stderr, "closepath\n"));
343 CGContextClosePath (closure);
344 return CAIRO_STATUS_SUCCESS;
348 _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
349 CGContextRef closure)
351 cairo_status_t status;
353 CGContextBeginPath (closure);
354 status = _cairo_path_fixed_interpret (path,
355 _cairo_path_to_quartz_context_move_to,
356 _cairo_path_to_quartz_context_line_to,
357 _cairo_path_to_quartz_context_curve_to,
358 _cairo_path_to_quartz_context_close_path,
361 assert (status == CAIRO_STATUS_SUCCESS);
365 * Misc helpers/callbacks
368 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
369 static PrivateCGCompositeMode
370 _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
373 case CAIRO_OPERATOR_CLEAR:
374 return kPrivateCGCompositeClear;
375 case CAIRO_OPERATOR_SOURCE:
376 return kPrivateCGCompositeCopy;
377 case CAIRO_OPERATOR_OVER:
378 return kPrivateCGCompositeSourceOver;
379 case CAIRO_OPERATOR_IN:
380 return kPrivateCGCompositeSourceIn;
381 case CAIRO_OPERATOR_OUT:
382 return kPrivateCGCompositeSourceOut;
383 case CAIRO_OPERATOR_ATOP:
384 return kPrivateCGCompositeSourceAtop;
385 case CAIRO_OPERATOR_DEST_OVER:
386 return kPrivateCGCompositeDestinationOver;
387 case CAIRO_OPERATOR_DEST_IN:
388 return kPrivateCGCompositeDestinationIn;
389 case CAIRO_OPERATOR_DEST_OUT:
390 return kPrivateCGCompositeDestinationOut;
391 case CAIRO_OPERATOR_DEST_ATOP:
392 return kPrivateCGCompositeDestinationAtop;
393 case CAIRO_OPERATOR_XOR:
394 return kPrivateCGCompositeXOR;
395 case CAIRO_OPERATOR_ADD:
396 return kPrivateCGCompositePlusLighter;
398 case CAIRO_OPERATOR_DEST:
399 case CAIRO_OPERATOR_SATURATE:
400 case CAIRO_OPERATOR_MULTIPLY:
401 case CAIRO_OPERATOR_SCREEN:
402 case CAIRO_OPERATOR_OVERLAY:
403 case CAIRO_OPERATOR_DARKEN:
404 case CAIRO_OPERATOR_LIGHTEN:
405 case CAIRO_OPERATOR_COLOR_DODGE:
406 case CAIRO_OPERATOR_COLOR_BURN:
407 case CAIRO_OPERATOR_HARD_LIGHT:
408 case CAIRO_OPERATOR_SOFT_LIGHT:
409 case CAIRO_OPERATOR_DIFFERENCE:
410 case CAIRO_OPERATOR_EXCLUSION:
411 case CAIRO_OPERATOR_HSL_HUE:
412 case CAIRO_OPERATOR_HSL_SATURATION:
413 case CAIRO_OPERATOR_HSL_COLOR:
414 case CAIRO_OPERATOR_HSL_LUMINOSITY:
422 _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
425 case CAIRO_OPERATOR_MULTIPLY:
426 return kCGBlendModeMultiply;
427 case CAIRO_OPERATOR_SCREEN:
428 return kCGBlendModeScreen;
429 case CAIRO_OPERATOR_OVERLAY:
430 return kCGBlendModeOverlay;
431 case CAIRO_OPERATOR_DARKEN:
432 return kCGBlendModeDarken;
433 case CAIRO_OPERATOR_LIGHTEN:
434 return kCGBlendModeLighten;
435 case CAIRO_OPERATOR_COLOR_DODGE:
436 return kCGBlendModeColorDodge;
437 case CAIRO_OPERATOR_COLOR_BURN:
438 return kCGBlendModeColorBurn;
439 case CAIRO_OPERATOR_HARD_LIGHT:
440 return kCGBlendModeHardLight;
441 case CAIRO_OPERATOR_SOFT_LIGHT:
442 return kCGBlendModeSoftLight;
443 case CAIRO_OPERATOR_DIFFERENCE:
444 return kCGBlendModeDifference;
445 case CAIRO_OPERATOR_EXCLUSION:
446 return kCGBlendModeExclusion;
447 case CAIRO_OPERATOR_HSL_HUE:
448 return kCGBlendModeHue;
449 case CAIRO_OPERATOR_HSL_SATURATION:
450 return kCGBlendModeSaturation;
451 case CAIRO_OPERATOR_HSL_COLOR:
452 return kCGBlendModeColor;
453 case CAIRO_OPERATOR_HSL_LUMINOSITY:
454 return kCGBlendModeLuminosity;
456 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
457 case CAIRO_OPERATOR_CLEAR:
458 return kCGBlendModeClear;
459 case CAIRO_OPERATOR_SOURCE:
460 return kCGBlendModeCopy;
461 case CAIRO_OPERATOR_OVER:
462 return kCGBlendModeNormal;
463 case CAIRO_OPERATOR_IN:
464 return kCGBlendModeSourceIn;
465 case CAIRO_OPERATOR_OUT:
466 return kCGBlendModeSourceOut;
467 case CAIRO_OPERATOR_ATOP:
468 return kCGBlendModeSourceAtop;
469 case CAIRO_OPERATOR_DEST_OVER:
470 return kCGBlendModeDestinationOver;
471 case CAIRO_OPERATOR_DEST_IN:
472 return kCGBlendModeDestinationIn;
473 case CAIRO_OPERATOR_DEST_OUT:
474 return kCGBlendModeDestinationOut;
475 case CAIRO_OPERATOR_DEST_ATOP:
476 return kCGBlendModeDestinationAtop;
477 case CAIRO_OPERATOR_XOR:
478 return kCGBlendModeXOR;
479 case CAIRO_OPERATOR_ADD:
480 return kCGBlendModePlusLighter;
482 case CAIRO_OPERATOR_CLEAR:
483 case CAIRO_OPERATOR_SOURCE:
484 case CAIRO_OPERATOR_OVER:
485 case CAIRO_OPERATOR_IN:
486 case CAIRO_OPERATOR_OUT:
487 case CAIRO_OPERATOR_ATOP:
488 case CAIRO_OPERATOR_DEST_OVER:
489 case CAIRO_OPERATOR_DEST_IN:
490 case CAIRO_OPERATOR_DEST_OUT:
491 case CAIRO_OPERATOR_DEST_ATOP:
492 case CAIRO_OPERATOR_XOR:
493 case CAIRO_OPERATOR_ADD:
496 case CAIRO_OPERATOR_DEST:
497 case CAIRO_OPERATOR_SATURATE:
503 static cairo_int_status_t
504 _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
506 CGBlendMode blendmode;
508 assert (op != CAIRO_OPERATOR_DEST);
510 /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
511 * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
512 * uses the definition from the Adobe Supplement.
514 if (op == CAIRO_OPERATOR_SATURATE ||
515 op == CAIRO_OPERATOR_COLOR_DODGE ||
516 op == CAIRO_OPERATOR_COLOR_BURN)
518 return CAIRO_INT_STATUS_UNSUPPORTED;
521 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
522 if (op <= CAIRO_OPERATOR_ADD) {
523 PrivateCGCompositeMode compmode;
525 compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
526 CGContextSetCompositeOperation (context, compmode);
527 return CAIRO_STATUS_SUCCESS;
531 blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
532 CGContextSetBlendMode (context, blendmode);
533 return CAIRO_STATUS_SUCCESS;
536 static cairo_int_status_t
537 _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
539 ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
541 /* When the destination has no color components, we can avoid some
542 * fallbacks, but we have to workaround operators which behave
543 * differently in Quartz. */
544 if (surface->base.content == CAIRO_CONTENT_ALPHA) {
545 assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
547 if (op == CAIRO_OPERATOR_SOURCE ||
548 op == CAIRO_OPERATOR_IN ||
549 op == CAIRO_OPERATOR_OUT ||
550 op == CAIRO_OPERATOR_DEST_IN ||
551 op == CAIRO_OPERATOR_DEST_ATOP ||
552 op == CAIRO_OPERATOR_XOR)
554 return CAIRO_INT_STATUS_UNSUPPORTED;
557 if (op == CAIRO_OPERATOR_DEST_OVER)
558 op = CAIRO_OPERATOR_OVER;
559 else if (op == CAIRO_OPERATOR_SATURATE)
560 op = CAIRO_OPERATOR_ADD;
561 else if (op == CAIRO_OPERATOR_COLOR_DODGE)
562 op = CAIRO_OPERATOR_OVER;
563 else if (op == CAIRO_OPERATOR_COLOR_BURN)
564 op = CAIRO_OPERATOR_OVER;
567 return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
570 static inline CGLineCap
571 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
577 case CAIRO_LINE_CAP_BUTT:
578 return kCGLineCapButt;
580 case CAIRO_LINE_CAP_ROUND:
581 return kCGLineCapRound;
583 case CAIRO_LINE_CAP_SQUARE:
584 return kCGLineCapSquare;
588 static inline CGLineJoin
589 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
595 case CAIRO_LINE_JOIN_MITER:
596 return kCGLineJoinMiter;
598 case CAIRO_LINE_JOIN_ROUND:
599 return kCGLineJoinRound;
601 case CAIRO_LINE_JOIN_BEVEL:
602 return kCGLineJoinBevel;
606 static inline CGInterpolationQuality
607 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
610 case CAIRO_FILTER_NEAREST:
611 case CAIRO_FILTER_FAST:
612 return kCGInterpolationNone;
614 case CAIRO_FILTER_BEST:
615 case CAIRO_FILTER_GOOD:
616 case CAIRO_FILTER_BILINEAR:
617 case CAIRO_FILTER_GAUSSIAN:
618 return kCGInterpolationDefault;
622 return kCGInterpolationDefault;
627 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
628 CGAffineTransform *dst)
640 * Source -> Quartz setup and finish functions
644 ComputeGradientValue (void *info,
645 const cairo_quartz_float_t *in,
646 cairo_quartz_float_t *out)
649 const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
652 /* Put fdist back in the 0.0..1.0 range if we're doing
655 if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
656 fdist = fdist - floor (fdist);
657 } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
658 fdist = fmod (fabs (fdist), 2.0);
663 for (i = 0; i < grad->n_stops; i++)
664 if (grad->stops[i].offset > fdist)
667 if (i == 0 || i == grad->n_stops) {
668 if (i == grad->n_stops)
670 out[0] = grad->stops[i].color.red;
671 out[1] = grad->stops[i].color.green;
672 out[2] = grad->stops[i].color.blue;
673 out[3] = grad->stops[i].color.alpha;
675 cairo_quartz_float_t ax = grad->stops[i-1].offset;
676 cairo_quartz_float_t bx = grad->stops[i].offset - ax;
677 cairo_quartz_float_t bp = (fdist - ax)/bx;
678 cairo_quartz_float_t ap = 1.0 - bp;
681 grad->stops[i-1].color.red * ap +
682 grad->stops[i].color.red * bp;
684 grad->stops[i-1].color.green * ap +
685 grad->stops[i].color.green * bp;
687 grad->stops[i-1].color.blue * ap +
688 grad->stops[i].color.blue * bp;
690 grad->stops[i-1].color.alpha * ap +
691 grad->stops[i].color.alpha * bp;
695 static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
696 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
698 static const CGFunctionCallbacks gradient_callbacks = {
699 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
702 /* Quartz computes a small number of samples of the gradient color
703 * function. On MacOS X 10.5 it apparently computes only 1024
705 #define MAX_GRADIENT_RANGE 1024
708 CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
709 const cairo_rectangle_int_t *extents,
710 cairo_circle_double_t *start,
711 cairo_circle_double_t *end)
713 cairo_pattern_t *pat;
714 cairo_quartz_float_t input_value_range[2];
716 if (gradient->base.extend != CAIRO_EXTEND_NONE) {
717 double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
718 double t[2], tolerance;
720 tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
721 tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
723 bounds_x1 = extents->x;
724 bounds_y1 = extents->y;
725 bounds_x2 = extents->x + extents->width;
726 bounds_y2 = extents->y + extents->height;
727 _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
728 &bounds_x1, &bounds_y1,
729 &bounds_x2, &bounds_y2,
732 _cairo_gradient_pattern_box_to_parameter (gradient,
733 bounds_x1, bounds_y1,
734 bounds_x2, bounds_y2,
738 if (gradient->base.extend == CAIRO_EXTEND_PAD) {
739 t[0] = MAX (t[0], -0.5);
740 t[1] = MIN (t[1], 1.5);
741 } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
744 /* set the input range for the function -- the function knows how
745 to map values outside of 0.0 .. 1.0 to the correct color */
746 input_value_range[0] = t[0];
747 input_value_range[1] = t[1];
749 input_value_range[0] = 0;
750 input_value_range[1] = 1;
753 _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
754 _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
756 if (_cairo_pattern_create_copy (&pat, &gradient->base))
759 return CGFunctionCreate (pat,
763 gradient_output_value_ranges,
764 &gradient_callbacks);
767 /* Obtain a CGImageRef from a #cairo_surface_t * */
770 cairo_surface_t *surface;
771 cairo_image_surface_t *image_out;
773 } quartz_source_image_t;
776 DataProviderReleaseCallback (void *info, const void *data, size_t size)
778 quartz_source_image_t *source_img = info;
779 _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
783 static cairo_status_t
784 _cairo_surface_to_cgimage (cairo_surface_t *source,
785 CGImageRef *image_out)
787 cairo_status_t status;
788 quartz_source_image_t *source_img;
790 if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
791 cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
792 *image_out = CGImageRetain (surface->image);
793 return CAIRO_STATUS_SUCCESS;
796 if (_cairo_surface_is_quartz (source)) {
797 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
798 if (IS_EMPTY (surface)) {
800 return CAIRO_INT_STATUS_NOTHING_TO_DO;
803 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
804 *image_out = CGBitmapContextCreateImage (surface->cgContext);
806 return CAIRO_STATUS_SUCCESS;
810 source_img = malloc (sizeof (quartz_source_image_t));
811 if (unlikely (source_img == NULL))
812 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
814 source_img->surface = source;
816 status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
817 if (unlikely (status)) {
822 if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
824 DataProviderReleaseCallback (source_img,
825 source_img->image_out->data,
826 source_img->image_out->height * source_img->image_out->stride);
828 *image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
829 source_img->image_out->width,
830 source_img->image_out->height,
831 source_img->image_out->stride,
832 source_img->image_out->data,
835 DataProviderReleaseCallback,
838 /* TODO: differentiate memory error and unsupported surface type */
839 if (unlikely (*image_out == NULL))
840 status = CAIRO_INT_STATUS_UNSUPPORTED;
846 /* Generic #cairo_pattern_t -> CGPattern function */
851 cairo_bool_t do_reflect;
852 } SurfacePatternDrawInfo;
855 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
857 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
859 CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
860 CGContextScaleCTM (context, 1, -1);
862 CGContextDrawImage (context, info->imageBounds, info->image);
863 if (info->do_reflect) {
864 /* draw 3 more copies of the image, flipped.
865 * DrawImage draws the image according to the current Y-direction into the rectangle given
866 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
867 * of the base image position, and the Y axis is extending upwards.
870 /* Make the y axis extend downwards, and draw a flipped image below */
871 CGContextScaleCTM (context, 1, -1);
872 CGContextDrawImage (context, info->imageBounds, info->image);
874 /* Shift over to the right, and flip vertically (translation is 2x,
875 * since we'll be flipping and thus rendering the rectangle "backwards"
877 CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
878 CGContextScaleCTM (context, -1, 1);
879 CGContextDrawImage (context, info->imageBounds, info->image);
881 /* Then unflip the Y-axis again, and draw the image above the point. */
882 CGContextScaleCTM (context, 1, -1);
883 CGContextDrawImage (context, info->imageBounds, info->image);
888 SurfacePatternReleaseInfoFunc (void *ainfo)
890 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
892 CGImageRelease (info->image);
896 static cairo_int_status_t
897 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
898 const cairo_pattern_t *apattern,
901 cairo_surface_pattern_t *spattern;
902 cairo_surface_t *pat_surf;
903 cairo_rectangle_int_t extents;
907 CGAffineTransform ptransform, stransform;
908 CGPatternCallbacks cb = { 0,
909 SurfacePatternDrawFunc,
910 SurfacePatternReleaseInfoFunc };
911 SurfacePatternDrawInfo *info;
912 cairo_quartz_float_t rw, rh;
913 cairo_status_t status;
914 cairo_bool_t is_bounded;
918 /* SURFACE is the only type we'll handle here */
919 assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
921 spattern = (cairo_surface_pattern_t *) apattern;
922 pat_surf = spattern->surface;
924 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
927 status = _cairo_surface_to_cgimage (pat_surf, &image);
928 if (unlikely (status))
931 info = malloc (sizeof (SurfacePatternDrawInfo));
932 if (unlikely (!info))
933 return CAIRO_STATUS_NO_MEMORY;
935 /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
936 * that the data will stick around for this image when the printer gets to it.
937 * Otherwise, the underlying data store may disappear from under us!
939 * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
940 * since the Quartz surfaces have a higher chance of sticking around. If the
941 * source is a quartz image surface, then it's set up to retain a ref to the
942 * image surface that it's backed by.
945 info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
946 info->do_reflect = FALSE;
948 pbounds.origin.x = 0;
949 pbounds.origin.y = 0;
951 if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
952 pbounds.size.width = 2.0 * extents.width;
953 pbounds.size.height = 2.0 * extents.height;
954 info->do_reflect = TRUE;
956 pbounds.size.width = extents.width;
957 pbounds.size.height = extents.height;
959 rw = pbounds.size.width;
960 rh = pbounds.size.height;
962 m = spattern->base.matrix;
963 cairo_matrix_invert (&m);
964 _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
966 /* The pattern matrix is relative to the bottom left, again; the
967 * incoming cairo pattern matrix is relative to the upper left.
968 * So we take the pattern matrix and the original context matrix,
969 * which gives us the correct base translation/y flip.
971 ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
974 ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
975 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));
976 CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
977 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));
980 *cgpat = CGPatternCreate (info,
984 kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
988 return CAIRO_STATUS_SUCCESS;
991 /* State used during a drawing operation. */
993 /* The destination of the mask */
994 CGContextRef cgMaskContext;
996 /* The destination of the drawing of the source */
997 CGContextRef cgDrawContext;
999 /* The filter to be used when drawing the source */
1000 CGInterpolationQuality filter;
1003 cairo_quartz_action_t action;
1005 /* Destination rect */
1008 /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1009 CGAffineTransform transform;
1011 /* Used with DO_IMAGE and DO_TILED_IMAGE */
1014 /* Used with DO_SHADING */
1015 CGShadingRef shading;
1017 /* Temporary destination for unbounded operations */
1020 } cairo_quartz_drawing_state_t;
1023 Quartz does not support repeating radients. We handle repeating gradients
1024 by manually extending the gradient and repeating color stops. We need to
1025 minimize the number of repetitions since Quartz seems to sample our color
1026 function across the entire range, even if part of that range is not needed
1027 for the visible area of the gradient, and it samples with some fixed resolution,
1028 so if the gradient range is too large it samples with very low resolution and
1029 the gradient is very coarse. _cairo_quartz_create_gradient_function computes
1030 the number of repetitions needed based on the extents.
1032 static cairo_int_status_t
1033 _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
1034 const cairo_gradient_pattern_t *gradient,
1035 const cairo_rectangle_int_t *extents)
1038 cairo_circle_double_t start, end;
1039 CGFunctionRef gradFunc;
1040 CGColorSpaceRef rgb;
1041 bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
1043 assert (gradient->n_stops > 0);
1045 mat = gradient->base.matrix;
1046 cairo_matrix_invert (&mat);
1047 _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
1049 gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
1052 if (unlikely (gradFunc == NULL))
1053 return CAIRO_INT_STATUS_UNSUPPORTED;
1055 rgb = CGColorSpaceCreateDeviceRGB ();
1057 if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
1058 state->shading = CGShadingCreateAxial (rgb,
1059 CGPointMake (start.center.x,
1061 CGPointMake (end.center.x,
1066 state->shading = CGShadingCreateRadial (rgb,
1067 CGPointMake (start.center.x,
1069 MAX (start.radius, 0),
1070 CGPointMake (end.center.x,
1072 MAX (end.radius, 0),
1077 CGColorSpaceRelease (rgb);
1078 CGFunctionRelease (gradFunc);
1080 state->action = DO_SHADING;
1081 return CAIRO_STATUS_SUCCESS;
1084 static cairo_int_status_t
1085 _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
1086 cairo_composite_rectangles_t *extents)
1088 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1089 cairo_operator_t op = extents->op;
1090 const cairo_pattern_t *source = &extents->source_pattern.base;
1091 const cairo_clip_t *clip = extents->clip;
1092 cairo_bool_t needs_temp;
1093 cairo_status_t status;
1095 state->layer = NULL;
1096 state->image = NULL;
1097 state->shading = NULL;
1098 state->cgDrawContext = NULL;
1099 state->cgMaskContext = NULL;
1101 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1102 if (unlikely (status))
1105 status = _cairo_quartz_surface_set_cairo_operator (surface, op);
1106 if (unlikely (status))
1109 /* Save before we change the pattern, colorspace, etc. so that
1110 * we can restore and make sure that quartz releases our
1111 * pattern (which may be stack allocated)
1114 CGContextSaveGState (surface->cgContext);
1115 state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
1116 state->clipRect = CGRectIntegral (state->clipRect);
1117 state->rect = state->clipRect;
1119 state->cgMaskContext = surface->cgContext;
1120 state->cgDrawContext = state->cgMaskContext;
1122 state->filter = _cairo_quartz_filter_to_quartz (source->filter);
1124 if (op == CAIRO_OPERATOR_CLEAR) {
1125 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1127 state->action = DO_DIRECT;
1128 return CAIRO_STATUS_SUCCESS;
1132 * To implement mask unbounded operations Quartz needs a temporary
1133 * surface which will be composited entirely (ignoring the mask).
1134 * To implement source unbounded operations Quartz needs a
1135 * temporary surface which allows extending the source to a size
1136 * covering the whole mask, but there are some optimization
1139 * - CLEAR completely ignores the source, thus we can just use a
1142 * - SOURCE can be implemented by drawing the source and clearing
1143 * outside of the source as long as the two regions have no
1144 * intersection. This happens when the source is a pixel-aligned
1145 * rectangle. If the source is at least as big as the
1146 * intersection between the clip rectangle and the mask
1147 * rectangle, no clear operation is needed.
1149 needs_temp = ! _cairo_operator_bounded_by_mask (op);
1152 state->layer = CGLayerCreateWithContext (surface->cgContext,
1153 state->clipRect.size,
1155 state->cgDrawContext = CGLayerGetContext (state->layer);
1156 state->cgMaskContext = state->cgDrawContext;
1157 CGContextTranslateCTM (state->cgDrawContext,
1158 -state->clipRect.origin.x,
1159 -state->clipRect.origin.y);
1162 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1163 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1165 CGContextSetRGBStrokeColor (state->cgDrawContext,
1169 solid->color.alpha);
1170 CGContextSetRGBFillColor (state->cgDrawContext,
1174 solid->color.alpha);
1176 state->action = DO_DIRECT;
1177 return CAIRO_STATUS_SUCCESS;
1180 if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
1181 source->type == CAIRO_PATTERN_TYPE_RADIAL)
1183 const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
1184 cairo_rectangle_int_t extents;
1186 extents = surface->virtual_extents;
1187 extents.x -= surface->base.device_transform.x0;
1188 extents.y -= surface->base.device_transform.y0;
1189 _cairo_rectangle_union (&extents, &surface->extents);
1191 return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
1194 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1195 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1197 const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1198 cairo_surface_t *pat_surf = spat->surface;
1200 cairo_matrix_t m = spat->base.matrix;
1201 cairo_rectangle_int_t extents;
1202 CGAffineTransform xform;
1204 cairo_fixed_t fw, fh;
1205 cairo_bool_t is_bounded;
1207 status = _cairo_surface_to_cgimage (pat_surf, &img);
1208 if (unlikely (status))
1213 if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
1214 m.x0 = -ceil (m.x0 - 0.5);
1215 m.y0 = -ceil (m.y0 - 0.5);
1217 cairo_matrix_invert (&m);
1220 _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
1222 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
1223 assert (is_bounded);
1225 srcRect = CGRectMake (0, 0, extents.width, extents.height);
1227 if (source->extend == CAIRO_EXTEND_NONE) {
1229 if (op == CAIRO_OPERATOR_SOURCE &&
1230 (pat_surf->content == CAIRO_CONTENT_ALPHA ||
1231 ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
1233 state->layer = CGLayerCreateWithContext (surface->cgContext,
1234 state->clipRect.size,
1236 state->cgDrawContext = CGLayerGetContext (state->layer);
1237 CGContextTranslateCTM (state->cgDrawContext,
1238 -state->clipRect.origin.x,
1239 -state->clipRect.origin.y);
1242 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1244 state->rect = srcRect;
1245 state->action = DO_IMAGE;
1246 return CAIRO_STATUS_SUCCESS;
1249 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1251 /* Quartz seems to tile images at pixel-aligned regions only -- this
1252 * leads to seams if the image doesn't end up scaling to fill the
1253 * space exactly. The CGPattern tiling approach doesn't have this
1254 * problem. Check if we're going to fill up the space (within some
1255 * epsilon), and if not, fall back to the CGPattern type.
1258 xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
1261 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1263 fw = _cairo_fixed_from_double (srcRect.size.width);
1264 fh = _cairo_fixed_from_double (srcRect.size.height);
1266 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1267 (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1269 /* We're good to use DrawTiledImage, but ensure that
1270 * the math works out */
1272 srcRect.size.width = round (srcRect.size.width);
1273 srcRect.size.height = round (srcRect.size.height);
1275 xform = CGAffineTransformInvert (xform);
1277 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1279 state->rect = srcRect;
1280 state->action = DO_TILED_IMAGE;
1281 return CAIRO_STATUS_SUCCESS;
1284 /* Fall through to generic SURFACE case */
1287 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1288 cairo_quartz_float_t patternAlpha = 1.0f;
1289 CGColorSpaceRef patternSpace;
1290 CGPatternRef pattern = NULL;
1291 cairo_int_status_t status;
1293 status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
1294 if (unlikely (status))
1297 patternSpace = CGColorSpaceCreatePattern (NULL);
1298 CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
1299 CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
1300 CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
1301 CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
1302 CGColorSpaceRelease (patternSpace);
1304 /* Quartz likes to munge the pattern phase (as yet unexplained
1305 * why); force it to 0,0 as we've already baked in the correct
1306 * pattern translation into the pattern matrix
1308 CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
1310 CGPatternRelease (pattern);
1312 state->action = DO_DIRECT;
1313 return CAIRO_STATUS_SUCCESS;
1316 return CAIRO_INT_STATUS_UNSUPPORTED;
1320 _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
1321 cairo_composite_rectangles_t *extents)
1323 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1326 CGContextDrawLayerInRect (surface->cgContext,
1329 CGContextRelease (state->cgDrawContext);
1330 CGLayerRelease (state->layer);
1333 if (state->cgMaskContext)
1334 CGContextRestoreGState (surface->cgContext);
1337 CGImageRelease (state->image);
1340 CGShadingRelease (state->shading);
1344 _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
1345 cairo_operator_t op)
1347 CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
1348 CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
1350 if (state->action == DO_DIRECT) {
1351 CGContextFillRect (state->cgDrawContext, state->rect);
1355 CGContextConcatCTM (state->cgDrawContext, state->transform);
1357 if (state->action == DO_SHADING) {
1358 CGContextDrawShading (state->cgDrawContext, state->shading);
1362 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1363 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1365 if (state->action == DO_IMAGE) {
1366 CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
1367 if (op == CAIRO_OPERATOR_SOURCE &&
1368 state->cgDrawContext == state->cgMaskContext)
1370 CGContextBeginPath (state->cgDrawContext);
1371 CGContextAddRect (state->cgDrawContext, state->rect);
1373 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1374 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1375 CGContextConcatCTM (state->cgDrawContext,
1376 CGAffineTransformInvert (state->transform));
1378 CGContextAddRect (state->cgDrawContext, state->clipRect);
1380 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
1381 CGContextEOFillPath (state->cgDrawContext);
1384 CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
1389 * get source/dest image implementation
1392 /* Read the image from the surface's front buffer */
1393 static cairo_int_status_t
1394 _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
1395 cairo_image_surface_t **image_out)
1397 unsigned char *imageData;
1398 cairo_image_surface_t *isurf;
1400 if (IS_EMPTY (surface)) {
1401 *image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1402 return CAIRO_STATUS_SUCCESS;
1405 if (surface->imageSurfaceEquiv) {
1406 *image_out = (cairo_image_surface_t*) cairo_surface_reference (surface->imageSurfaceEquiv);
1407 return CAIRO_STATUS_SUCCESS;
1410 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
1411 unsigned int stride;
1412 unsigned int bitinfo;
1413 unsigned int bpc, bpp;
1414 CGColorSpaceRef colorspace;
1415 unsigned int color_comps;
1417 imageData = (unsigned char *) CGBitmapContextGetData (surface->cgContext);
1419 bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1420 stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1421 bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1422 bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
1424 // let's hope they don't add YUV under us
1425 colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1426 color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
1428 // XXX TODO: We can handle all of these by converting to
1429 // pixman masks, including non-native-endian masks
1431 return CAIRO_INT_STATUS_UNSUPPORTED;
1433 if (bpp != 32 && bpp != 8)
1434 return CAIRO_INT_STATUS_UNSUPPORTED;
1436 if (color_comps != 3 && color_comps != 1)
1437 return CAIRO_INT_STATUS_UNSUPPORTED;
1439 if (bpp == 32 && color_comps == 3 &&
1440 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1441 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1443 isurf = (cairo_image_surface_t *)
1444 cairo_image_surface_create_for_data (imageData,
1445 CAIRO_FORMAT_ARGB32,
1446 surface->extents.width,
1447 surface->extents.height,
1449 } else if (bpp == 32 && color_comps == 3 &&
1450 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1451 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1453 isurf = (cairo_image_surface_t *)
1454 cairo_image_surface_create_for_data (imageData,
1456 surface->extents.width,
1457 surface->extents.height,
1459 } else if (bpp == 8 && color_comps == 1)
1461 isurf = (cairo_image_surface_t *)
1462 cairo_image_surface_create_for_data (imageData,
1464 surface->extents.width,
1465 surface->extents.height,
1468 return CAIRO_INT_STATUS_UNSUPPORTED;
1471 return CAIRO_INT_STATUS_UNSUPPORTED;
1475 return CAIRO_STATUS_SUCCESS;
1479 * Cairo surface backend implementations
1482 static cairo_status_t
1483 _cairo_quartz_surface_finish (void *abstract_surface)
1485 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1487 ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1489 if (IS_EMPTY (surface))
1490 return CAIRO_STATUS_SUCCESS;
1492 /* Restore our saved gstate that we use to reset clipping */
1493 CGContextRestoreGState (surface->cgContext);
1494 _cairo_surface_clipper_reset (&surface->clipper);
1496 CGContextRelease (surface->cgContext);
1498 surface->cgContext = NULL;
1500 if (surface->imageSurfaceEquiv) {
1501 cairo_surface_destroy (surface->imageSurfaceEquiv);
1502 surface->imageSurfaceEquiv = NULL;
1505 free (surface->imageData);
1506 surface->imageData = NULL;
1508 return CAIRO_STATUS_SUCCESS;
1511 static cairo_status_t
1512 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1513 cairo_image_surface_t **image_out,
1516 cairo_int_status_t status;
1517 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1519 //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1521 status = _cairo_quartz_get_image (surface, image_out);
1522 if (unlikely (status))
1523 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1525 *image_extra = NULL;
1527 return CAIRO_STATUS_SUCCESS;
1530 static cairo_surface_t *
1531 _cairo_quartz_surface_snapshot (void *abstract_surface)
1533 cairo_int_status_t status;
1534 cairo_quartz_surface_t *surface = abstract_surface;
1535 cairo_image_surface_t *image;
1537 if (surface->imageSurfaceEquiv)
1540 status = _cairo_quartz_get_image (surface, &image);
1541 if (unlikely (status))
1542 return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
1544 return &image->base;
1548 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1549 cairo_image_surface_t *image,
1552 cairo_surface_destroy (&image->base);
1556 static cairo_surface_t *
1557 _cairo_quartz_surface_map_to_image (void *abstract_surface,
1558 const cairo_rectangle_int_t *extents)
1560 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1561 cairo_image_surface_t *image;
1562 cairo_surface_t *subsurface;
1563 cairo_status_t status;
1565 status = _cairo_quartz_get_image (surface, &image);
1566 if (unlikely (status))
1567 return _cairo_surface_create_in_error (status);
1569 /* Is this legitimate? shouldn't it return an image surface? */
1571 subsurface = _cairo_surface_create_for_rectangle_int (&image->base, extents);
1572 cairo_surface_destroy (&image->base);
1577 static cairo_int_status_t
1578 _cairo_quartz_surface_unmap_image (void *abstract_surface,
1579 cairo_image_surface_t *image)
1581 cairo_surface_destroy (&image->base);
1583 return CAIRO_STATUS_SUCCESS;
1586 static cairo_surface_t *
1587 _cairo_quartz_surface_create_similar (void *abstract_surface,
1588 cairo_content_t content,
1592 cairo_quartz_surface_t *surface, *similar_quartz;
1593 cairo_surface_t *similar;
1594 cairo_format_t format;
1596 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1597 format = CAIRO_FORMAT_ARGB32;
1598 else if (content == CAIRO_CONTENT_COLOR)
1599 format = CAIRO_FORMAT_RGB24;
1600 else if (content == CAIRO_CONTENT_ALPHA)
1601 format = CAIRO_FORMAT_A8;
1605 // verify width and height of surface
1606 if (!_cairo_quartz_verify_surface_size (width, height)) {
1607 return _cairo_surface_create_in_error (_cairo_error
1608 (CAIRO_STATUS_INVALID_SIZE));
1611 similar = cairo_quartz_surface_create (format, width, height);
1612 if (unlikely (similar->status))
1615 surface = (cairo_quartz_surface_t *) abstract_surface;
1616 similar_quartz = (cairo_quartz_surface_t *) similar;
1617 similar_quartz->virtual_extents = surface->virtual_extents;
1623 _cairo_quartz_surface_get_extents (void *abstract_surface,
1624 cairo_rectangle_int_t *extents)
1626 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1628 *extents = surface->extents;
1632 static cairo_int_status_t
1633 _cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
1634 cairo_composite_rectangles_t *extents)
1636 cairo_quartz_drawing_state_t state;
1637 cairo_int_status_t rv;
1639 ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
1640 extents->surface, extents->op, extents->source_pattern.base.type));
1642 rv = _cairo_quartz_setup_state (&state, extents);
1646 _cairo_quartz_draw_source (&state, extents->op);
1649 _cairo_quartz_teardown_state (&state, extents);
1651 ND ((stderr, "-- paint\n"));
1655 static cairo_int_status_t
1656 _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
1657 cairo_surface_t *mask_surf,
1658 const cairo_matrix_t *mask_mat,
1659 CGInterpolationQuality filter)
1663 cairo_status_t status;
1664 CGAffineTransform mask_matrix;
1665 cairo_quartz_drawing_state_t state;
1667 status = _cairo_surface_to_cgimage (mask_surf, &img);
1668 if (unlikely (status))
1671 status = _cairo_quartz_setup_state (&state, extents);
1672 if (unlikely (status))
1675 rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
1676 _cairo_quartz_cairo_matrix_to_quartz (mask_mat, &mask_matrix);
1678 /* ClipToMask is essentially drawing an image, so we need to flip the CTM
1679 * to get the image to appear oriented the right way */
1680 CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
1681 CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
1682 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1684 state.filter = filter;
1686 CGContextSetInterpolationQuality (state.cgMaskContext, filter);
1687 CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
1689 CGContextClipToMask (state.cgMaskContext, rect, img);
1691 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1692 CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
1693 CGContextConcatCTM (state.cgMaskContext, mask_matrix);
1695 _cairo_quartz_draw_source (&state, extents->op);
1698 _cairo_quartz_teardown_state (&state, extents);
1700 CGImageRelease (img);
1705 static cairo_int_status_t
1706 _cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
1707 cairo_composite_rectangles_t *extents)
1709 cairo_quartz_drawing_state_t state;
1710 double alpha = extents->mask_pattern.solid.color.alpha;
1711 cairo_status_t status;
1713 status = _cairo_quartz_setup_state (&state, extents);
1714 if (unlikely (status))
1717 CGContextSetAlpha (surface->cgContext, alpha);
1718 _cairo_quartz_draw_source (&state, extents->op);
1720 _cairo_quartz_teardown_state (&state, extents);
1722 return CAIRO_STATUS_SUCCESS;
1725 static cairo_int_status_t
1726 _cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
1727 cairo_composite_rectangles_t *extents)
1729 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
1730 const cairo_pattern_t *source = &extents->source_pattern.base;
1731 const cairo_pattern_t *mask = &extents->mask_pattern.base;
1732 cairo_surface_t *mask_surf;
1733 cairo_matrix_t matrix;
1734 cairo_status_t status;
1735 cairo_bool_t need_temp;
1736 CGInterpolationQuality filter;
1738 ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
1739 extents->surface, extents->op, extents->source_pattern.base.type,
1740 extents->mask_pattern.base.type));
1742 if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
1743 return _cairo_quartz_cg_mask_with_solid (surface, extents);
1745 need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
1746 mask->extend != CAIRO_EXTEND_NONE);
1748 filter = _cairo_quartz_filter_to_quartz (source->filter);
1751 mask_surf = extents->mask_pattern.surface.surface;
1753 /* When an opaque surface used as a mask in Quartz, its
1754 * luminosity is used as the alpha value, so we con only use
1755 * surfaces with alpha without creating a temporary mask. */
1756 need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
1760 CGInterpolationQuality mask_filter;
1761 cairo_bool_t simple_transform;
1763 matrix = mask->matrix;
1765 mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
1766 if (mask_filter == kCGInterpolationNone) {
1767 simple_transform = _cairo_matrix_is_translation (&matrix);
1768 if (simple_transform) {
1769 matrix.x0 = ceil (matrix.x0 - 0.5);
1770 matrix.y0 = ceil (matrix.y0 - 0.5);
1773 simple_transform = _cairo_matrix_is_integer_translation (&matrix,
1778 /* Quartz only allows one interpolation to be set for mask and
1779 * source, so we can skip the temp surface only if the source
1780 * filtering makes the mask look correct. */
1781 if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
1782 need_temp = ! (simple_transform || filter == mask_filter);
1784 filter = mask_filter;
1788 /* Render the mask to a surface */
1789 mask_surf = _cairo_quartz_surface_create_similar (surface,
1790 CAIRO_CONTENT_ALPHA,
1791 surface->extents.width,
1792 surface->extents.height);
1793 status = mask_surf->status;
1794 if (unlikely (status))
1797 /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
1798 * temporary layer or fallback to cairo-image. */
1799 status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
1800 if (unlikely (status))
1803 cairo_matrix_init_identity (&matrix);
1806 status = _cairo_quartz_cg_mask_with_surface (extents,
1807 mask_surf, &matrix, filter);
1812 cairo_surface_destroy (mask_surf);
1817 static cairo_int_status_t
1818 _cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
1819 cairo_composite_rectangles_t *extents,
1820 const cairo_path_fixed_t *path,
1821 cairo_fill_rule_t fill_rule,
1823 cairo_antialias_t antialias)
1825 cairo_quartz_drawing_state_t state;
1826 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1828 ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
1829 extents->surface, extents->op, extents->source_pattern.base.type));
1831 rv = _cairo_quartz_setup_state (&state, extents);
1835 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1837 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1839 if (state.action == DO_DIRECT) {
1840 assert (state.cgDrawContext == state.cgMaskContext);
1841 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1842 CGContextFillPath (state.cgMaskContext);
1844 CGContextEOFillPath (state.cgMaskContext);
1846 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1847 CGContextClip (state.cgMaskContext);
1849 CGContextEOClip (state.cgMaskContext);
1851 _cairo_quartz_draw_source (&state, extents->op);
1855 _cairo_quartz_teardown_state (&state, extents);
1857 ND ((stderr, "-- fill\n"));
1861 static cairo_int_status_t
1862 _cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
1863 cairo_composite_rectangles_t *extents,
1864 const cairo_path_fixed_t *path,
1865 const cairo_stroke_style_t *style,
1866 const cairo_matrix_t *ctm,
1867 const cairo_matrix_t *ctm_inverse,
1869 cairo_antialias_t antialias)
1871 cairo_quartz_drawing_state_t state;
1872 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1873 CGAffineTransform strokeTransform, invStrokeTransform;
1875 ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
1876 extents->surface, extents->op, extents->source_pattern.base.type));
1878 rv = _cairo_quartz_setup_state (&state, extents);
1882 // Turning antialiasing off used to cause misrendering with
1883 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1884 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1885 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1886 CGContextSetLineWidth (state.cgMaskContext, style->line_width);
1887 CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1888 CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1889 CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
1891 if (style->dash && style->num_dashes) {
1892 cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
1893 cairo_quartz_float_t *fdash = sdash;
1894 unsigned int max_dashes = style->num_dashes;
1897 if (style->num_dashes%2)
1899 if (max_dashes > ARRAY_LENGTH (sdash))
1900 fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
1901 if (unlikely (fdash == NULL)) {
1902 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1906 for (k = 0; k < max_dashes; k++)
1907 fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
1909 CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
1913 CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
1915 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1917 _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1918 CGContextConcatCTM (state.cgMaskContext, strokeTransform);
1920 if (state.action == DO_DIRECT) {
1921 assert (state.cgDrawContext == state.cgMaskContext);
1922 CGContextStrokePath (state.cgMaskContext);
1924 CGContextReplacePathWithStrokedPath (state.cgMaskContext);
1925 CGContextClip (state.cgMaskContext);
1927 _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
1928 CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
1930 _cairo_quartz_draw_source (&state, extents->op);
1934 _cairo_quartz_teardown_state (&state, extents);
1936 ND ((stderr, "-- stroke\n"));
1940 #if CAIRO_HAS_QUARTZ_FONT
1941 static cairo_int_status_t
1942 _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
1943 cairo_composite_rectangles_t *extents,
1944 cairo_scaled_font_t *scaled_font,
1945 cairo_glyph_t *glyphs,
1947 cairo_bool_t overlap)
1949 CGAffineTransform textTransform, invTextTransform;
1950 CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1951 CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1952 CGGlyph *cg_glyphs = &glyphs_static[0];
1953 CGSize *cg_advances = &cg_advances_static[0];
1954 COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
1956 cairo_quartz_drawing_state_t state;
1957 cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
1958 cairo_quartz_float_t xprev, yprev;
1960 CGFontRef cgfref = NULL;
1962 cairo_bool_t didForceFontSmoothing = FALSE;
1964 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
1965 return CAIRO_INT_STATUS_UNSUPPORTED;
1967 rv = _cairo_quartz_setup_state (&state, extents);
1971 if (state.action == DO_DIRECT) {
1972 assert (state.cgDrawContext == state.cgMaskContext);
1973 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
1975 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
1978 /* this doesn't addref */
1979 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1980 CGContextSetFont (state.cgMaskContext, cgfref);
1981 CGContextSetFontSize (state.cgMaskContext, 1.0);
1983 switch (scaled_font->options.antialias) {
1984 case CAIRO_ANTIALIAS_SUBPIXEL:
1985 case CAIRO_ANTIALIAS_BEST:
1986 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1987 CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
1988 if (CGContextSetAllowsFontSmoothingPtr &&
1989 !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
1991 didForceFontSmoothing = TRUE;
1992 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
1995 case CAIRO_ANTIALIAS_NONE:
1996 CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
1998 case CAIRO_ANTIALIAS_GRAY:
1999 case CAIRO_ANTIALIAS_GOOD:
2000 case CAIRO_ANTIALIAS_FAST:
2001 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
2002 CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
2004 case CAIRO_ANTIALIAS_DEFAULT:
2005 /* Don't do anything */
2009 if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
2010 cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
2011 if (unlikely (cg_glyphs == NULL)) {
2012 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2016 cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
2019 /* scale(1,-1) * scaled_font->scale */
2020 textTransform = CGAffineTransformMake (scaled_font->scale.xx,
2021 scaled_font->scale.yx,
2022 -scaled_font->scale.xy,
2023 -scaled_font->scale.yy,
2026 /* scaled_font->scale_inverse * scale(1,-1) */
2027 invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
2028 -scaled_font->scale_inverse.yx,
2029 scaled_font->scale_inverse.xy,
2030 -scaled_font->scale_inverse.yy,
2033 CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
2034 CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
2036 /* Convert our glyph positions to glyph advances. We need n-1 advances,
2037 * since the advance at index 0 is applied after glyph 0. */
2038 xprev = glyphs[0].x;
2039 yprev = glyphs[0].y;
2041 cg_glyphs[0] = glyphs[0].index;
2043 for (i = 1; i < num_glyphs; i++) {
2044 cairo_quartz_float_t xf = glyphs[i].x;
2045 cairo_quartz_float_t yf = glyphs[i].y;
2046 cg_glyphs[i] = glyphs[i].index;
2047 cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
2052 /* Translate to the first glyph's position before drawing */
2053 CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
2054 CGContextConcatCTM (state.cgMaskContext, textTransform);
2056 CGContextShowGlyphsWithAdvances (state.cgMaskContext,
2061 CGContextConcatCTM (state.cgMaskContext, invTextTransform);
2062 CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
2064 if (state.action != DO_DIRECT)
2065 _cairo_quartz_draw_source (&state, extents->op);
2068 if (didForceFontSmoothing)
2069 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
2071 _cairo_quartz_teardown_state (&state, extents);
2073 if (cg_glyphs != glyphs_static)
2078 #endif /* CAIRO_HAS_QUARTZ_FONT */
2080 static const cairo_compositor_t _cairo_quartz_cg_compositor = {
2081 &_cairo_fallback_compositor,
2083 _cairo_quartz_cg_paint,
2084 _cairo_quartz_cg_mask,
2085 _cairo_quartz_cg_stroke,
2086 _cairo_quartz_cg_fill,
2087 #if CAIRO_HAS_QUARTZ_FONT
2088 _cairo_quartz_cg_glyphs,
2094 static cairo_int_status_t
2095 _cairo_quartz_surface_paint (void *surface,
2096 cairo_operator_t op,
2097 const cairo_pattern_t *source,
2098 const cairo_clip_t *clip)
2100 return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
2101 surface, op, source, clip);
2104 static cairo_int_status_t
2105 _cairo_quartz_surface_mask (void *surface,
2106 cairo_operator_t op,
2107 const cairo_pattern_t *source,
2108 const cairo_pattern_t *mask,
2109 const cairo_clip_t *clip)
2111 return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
2112 surface, op, source, mask,
2116 static cairo_int_status_t
2117 _cairo_quartz_surface_fill (void *surface,
2118 cairo_operator_t op,
2119 const cairo_pattern_t *source,
2120 const cairo_path_fixed_t *path,
2121 cairo_fill_rule_t fill_rule,
2123 cairo_antialias_t antialias,
2124 const cairo_clip_t *clip)
2126 return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
2127 surface, op, source, path,
2128 fill_rule, tolerance, antialias,
2132 static cairo_int_status_t
2133 _cairo_quartz_surface_stroke (void *surface,
2134 cairo_operator_t op,
2135 const cairo_pattern_t *source,
2136 const cairo_path_fixed_t *path,
2137 const cairo_stroke_style_t *style,
2138 const cairo_matrix_t *ctm,
2139 const cairo_matrix_t *ctm_inverse,
2141 cairo_antialias_t antialias,
2142 const cairo_clip_t *clip)
2144 return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
2145 surface, op, source, path,
2146 style, ctm,ctm_inverse,
2147 tolerance, antialias, clip);
2150 static cairo_int_status_t
2151 _cairo_quartz_surface_glyphs (void *surface,
2152 cairo_operator_t op,
2153 const cairo_pattern_t *source,
2154 cairo_glyph_t *glyphs,
2156 cairo_scaled_font_t *scaled_font,
2157 const cairo_clip_t *clip)
2159 return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
2160 surface, op, source,
2161 glyphs, num_glyphs, scaled_font,
2165 static cairo_status_t
2166 _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
2167 cairo_path_fixed_t *path,
2168 cairo_fill_rule_t fill_rule,
2170 cairo_antialias_t antialias)
2172 cairo_quartz_surface_t *surface =
2173 cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
2175 ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2177 if (IS_EMPTY (surface))
2178 return CAIRO_STATUS_SUCCESS;
2181 /* If we're being asked to reset the clip, we can only do it
2182 * by restoring the gstate to our previous saved one, and
2185 * Note that this assumes that ALL quartz surface creation
2186 * functions will do a SaveGState first; we do this in create_internal.
2188 CGContextRestoreGState (surface->cgContext);
2189 CGContextSaveGState (surface->cgContext);
2191 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2193 _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
2195 if (fill_rule == CAIRO_FILL_RULE_WINDING)
2196 CGContextClip (surface->cgContext);
2198 CGContextEOClip (surface->cgContext);
2201 ND ((stderr, "-- intersect_clip_path\n"));
2203 return CAIRO_STATUS_SUCCESS;
2206 // XXXtodo implement show_page; need to figure out how to handle begin/end
2208 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2209 CAIRO_SURFACE_TYPE_QUARTZ,
2210 _cairo_quartz_surface_finish,
2212 _cairo_default_context_create,
2214 _cairo_quartz_surface_create_similar,
2215 NULL, /* similar image */
2216 _cairo_quartz_surface_map_to_image,
2217 _cairo_quartz_surface_unmap_image,
2219 _cairo_surface_default_source,
2220 _cairo_quartz_surface_acquire_source_image,
2221 _cairo_quartz_surface_release_source_image,
2222 _cairo_quartz_surface_snapshot,
2224 NULL, /* copy_page */
2225 NULL, /* show_page */
2227 _cairo_quartz_surface_get_extents,
2228 NULL, /* get_font_options */
2231 NULL, /* mark_dirty_rectangle */
2233 _cairo_quartz_surface_paint,
2234 _cairo_quartz_surface_mask,
2235 _cairo_quartz_surface_stroke,
2236 _cairo_quartz_surface_fill,
2237 NULL, /* fill-stroke */
2238 _cairo_quartz_surface_glyphs,
2241 cairo_quartz_surface_t *
2242 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2243 cairo_content_t content,
2245 unsigned int height)
2247 cairo_quartz_surface_t *surface;
2249 quartz_ensure_symbols ();
2251 /* Init the base surface */
2252 surface = malloc (sizeof (cairo_quartz_surface_t));
2253 if (unlikely (surface == NULL))
2254 return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2256 memset (surface, 0, sizeof (cairo_quartz_surface_t));
2258 _cairo_surface_init (&surface->base,
2259 &cairo_quartz_surface_backend,
2263 _cairo_surface_clipper_init (&surface->clipper,
2264 _cairo_quartz_surface_clipper_intersect_clip_path);
2266 /* Save our extents */
2267 surface->extents.x = surface->extents.y = 0;
2268 surface->extents.width = width;
2269 surface->extents.height = height;
2270 surface->virtual_extents = surface->extents;
2272 if (IS_EMPTY (surface)) {
2273 surface->cgContext = NULL;
2274 surface->cgContextBaseCTM = CGAffineTransformIdentity;
2275 surface->imageData = NULL;
2279 /* Save so we can always get back to a known-good CGContext -- this is
2280 * required for proper behaviour of intersect_clip_path(NULL)
2282 CGContextSaveGState (cgContext);
2284 surface->cgContext = cgContext;
2285 surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2287 surface->imageData = NULL;
2288 surface->imageSurfaceEquiv = NULL;
2294 * cairo_quartz_surface_create_for_cg_context:
2295 * @cgContext: the existing CGContext for which to create the surface
2296 * @width: width of the surface, in pixels
2297 * @height: height of the surface, in pixels
2299 * Creates a Quartz surface that wraps the given CGContext. The
2300 * CGContext is assumed to be in the standard Cairo coordinate space
2301 * (that is, with the origin at the upper left and the Y axis
2302 * increasing downward). If the CGContext is in the Quartz coordinate
2303 * space (with the origin at the bottom left), then it should be
2304 * flipped before this function is called. The flip can be accomplished
2305 * using a translate and a scale; for example:
2307 * <informalexample><programlisting>
2308 * CGContextTranslateCTM (cgContext, 0.0, height);
2309 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2310 * </programlisting></informalexample>
2312 * All Cairo operations are implemented in terms of Quartz operations,
2313 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2315 * Return value: the newly created Cairo surface.
2321 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2323 unsigned int height)
2325 cairo_quartz_surface_t *surf;
2327 surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2329 if (likely (!surf->base.status))
2330 CGContextRetain (cgContext);
2336 * cairo_quartz_surface_create:
2337 * @format: format of pixels in the surface to create
2338 * @width: width of the surface, in pixels
2339 * @height: height of the surface, in pixels
2341 * Creates a Quartz surface backed by a CGBitmap. The surface is
2342 * created using the Device RGB (or Device Gray, for A8) color space.
2343 * All Cairo operations, including those that require software
2344 * rendering, will succeed on this surface.
2346 * Return value: the newly created surface.
2351 cairo_quartz_surface_create (cairo_format_t format,
2353 unsigned int height)
2355 cairo_quartz_surface_t *surf;
2357 CGColorSpaceRef cgColorspace;
2358 CGBitmapInfo bitinfo;
2361 int bitsPerComponent;
2363 if (!_cairo_quartz_verify_surface_size (width, height))
2364 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2366 if (width == 0 || height == 0) {
2367 return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2368 width, height)->base;
2371 if (format == CAIRO_FORMAT_ARGB32 ||
2372 format == CAIRO_FORMAT_RGB24)
2374 cgColorspace = CGColorSpaceCreateDeviceRGB ();
2375 bitinfo = kCGBitmapByteOrder32Host;
2376 if (format == CAIRO_FORMAT_ARGB32)
2377 bitinfo |= kCGImageAlphaPremultipliedFirst;
2379 bitinfo |= kCGImageAlphaNoneSkipFirst;
2380 bitsPerComponent = 8;
2382 } else if (format == CAIRO_FORMAT_A8) {
2383 cgColorspace = NULL;
2385 bitinfo = kCGImageAlphaOnly;
2386 bitsPerComponent = 8;
2387 } else if (format == CAIRO_FORMAT_A1) {
2388 /* I don't think we can usefully support this, as defined by
2389 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2392 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2394 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2397 /* The Apple docs say that for best performance, the stride and the data
2398 * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2399 * so we don't have to anything special on allocation.
2401 stride = (stride + 15) & ~15;
2403 imageData = _cairo_malloc_ab (height, stride);
2404 if (unlikely (!imageData)) {
2405 CGColorSpaceRelease (cgColorspace);
2406 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2409 /* zero the memory to match the image surface behaviour */
2410 memset (imageData, 0, height * stride);
2412 cgc = CGBitmapContextCreate (imageData,
2419 CGColorSpaceRelease (cgColorspace);
2423 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2426 /* flip the Y axis */
2427 CGContextTranslateCTM (cgc, 0.0, height);
2428 CGContextScaleCTM (cgc, 1.0, -1.0);
2430 surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2432 if (surf->base.status) {
2433 CGContextRelease (cgc);
2435 // create_internal will have set an error
2439 surf->imageData = imageData;
2440 surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2446 * cairo_quartz_surface_get_cg_context:
2447 * @surface: the Cairo Quartz surface
2449 * Returns the CGContextRef that the given Quartz surface is backed
2452 * A call to cairo_surface_flush() is required before using the
2453 * CGContextRef to ensure that all pending drawing operations are
2454 * finished and to restore any temporary modification cairo has made
2455 * to its state. A call to cairo_surface_mark_dirty() is required
2456 * after the state or the content of the CGContextRef has been
2459 * Return value: the CGContextRef for the given surface.
2464 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2466 if (surface && _cairo_surface_is_quartz (surface)) {
2467 cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
2468 return quartz->cgContext;
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 */