1 /* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
2 /* cairo - a vector graphics library with display and print output
4 * Copyright � 2006, 2007 Mozilla Corporation
6 * This library is free software; you can redistribute it and/or
7 * modify it either under the terms of the GNU Lesser General Public
8 * License version 2.1 as published by the Free Software Foundation
9 * (the "LGPL") or, at your option, under the terms of the Mozilla
10 * Public License Version 1.1 (the "MPL"). If you do not alter this
11 * notice, a recipient may use your version of this file under either
12 * the MPL or the LGPL.
14 * You should have received a copy of the LGPL along with this library
15 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
17 * You should have received a copy of the MPL along with this library
18 * in the file COPYING-MPL-1.1
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License at
23 * http://www.mozilla.org/MPL/
25 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
26 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
27 * the specific language governing rights and limitations.
29 * The Original Code is the cairo graphics library.
31 * The Initial Developer of the Original Code is Mozilla Foundation.
34 * Vladimir Vukicevic <vladimir@mozilla.com>
37 #define _GNU_SOURCE /* required for RTLD_DEFAULT */
40 #include "cairo-quartz-private.h"
42 #include "cairo-composite-rectangles-private.h"
43 #include "cairo-compositor-private.h"
44 #include "cairo-default-context-private.h"
45 #include "cairo-error-private.h"
46 #include "cairo-image-surface-inline.h"
47 #include "cairo-pattern-private.h"
48 #include "cairo-surface-backend-private.h"
49 #include "cairo-surface-clipper-private.h"
50 #include "cairo-recording-surface-private.h"
55 #define RTLD_DEFAULT ((void *) 0)
63 #define ND(_x) fprintf _x
65 #define ND(_x) do {} while(0)
68 #define IS_EMPTY(s) ((s)->extents.width == 0 || (s)->extents.height == 0)
71 * SECTION:cairo-quartz
72 * @Title: Quartz Surfaces
73 * @Short_Description: Rendering to Quartz surfaces
74 * @See_Also: #cairo_surface_t
76 * The Quartz surface is used to render cairo graphics targeting the
77 * Apple OS X Quartz rendering system.
81 * CAIRO_HAS_QUARTZ_SURFACE:
83 * Defined if the Quartz surface backend is available.
84 * This macro can be used to conditionally compile backend-specific code.
89 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
90 /* This method is private, but it exists. Its params are are exposed
91 * as args to the NS* method, but not as CG.
93 enum PrivateCGCompositeMode {
94 kPrivateCGCompositeClear = 0,
95 kPrivateCGCompositeCopy = 1,
96 kPrivateCGCompositeSourceOver = 2,
97 kPrivateCGCompositeSourceIn = 3,
98 kPrivateCGCompositeSourceOut = 4,
99 kPrivateCGCompositeSourceAtop = 5,
100 kPrivateCGCompositeDestinationOver = 6,
101 kPrivateCGCompositeDestinationIn = 7,
102 kPrivateCGCompositeDestinationOut = 8,
103 kPrivateCGCompositeDestinationAtop = 9,
104 kPrivateCGCompositeXOR = 10,
105 kPrivateCGCompositePlusDarker = 11, // (max (0, (1-d) + (1-s)))
106 kPrivateCGCompositePlusLighter = 12, // (min (1, s + d))
108 typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
109 CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
112 /* Some of these are present in earlier versions of the OS than where
113 * they are public; other are not public at all
115 /* public since 10.5 */
116 static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
118 /* public since 10.6 */
119 static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
120 static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
123 static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
124 static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
126 static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
133 static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest);
134 static void quartz_image_to_png (CGImageRef, char *dest);
137 static cairo_quartz_surface_t *
138 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
139 cairo_content_t content,
141 unsigned int height);
144 _cairo_surface_is_quartz (const cairo_surface_t *surface);
146 /* Load all extra symbols */
147 static void quartz_ensure_symbols (void)
149 if (likely (_cairo_quartz_symbol_lookup_done))
152 CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
153 CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
154 CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
155 CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
156 CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
158 _cairo_quartz_symbol_lookup_done = TRUE;
162 CairoQuartzCreateCGImage (cairo_format_t format,
167 cairo_bool_t interpolate,
168 CGColorSpaceRef colorSpaceOverride,
169 CGDataProviderReleaseDataCallback releaseCallback,
172 CGImageRef image = NULL;
173 CGDataProviderRef dataProvider = NULL;
174 CGColorSpaceRef colorSpace = colorSpaceOverride;
175 CGBitmapInfo bitinfo = kCGBitmapByteOrder32Host;
176 int bitsPerComponent, bitsPerPixel;
179 case CAIRO_FORMAT_ARGB32:
180 if (colorSpace == NULL)
181 colorSpace = CGColorSpaceCreateDeviceRGB ();
182 bitinfo |= kCGImageAlphaPremultipliedFirst;
183 bitsPerComponent = 8;
187 case CAIRO_FORMAT_RGB24:
188 if (colorSpace == NULL)
189 colorSpace = CGColorSpaceCreateDeviceRGB ();
190 bitinfo |= kCGImageAlphaNoneSkipFirst;
191 bitsPerComponent = 8;
195 case CAIRO_FORMAT_A8:
196 bitsPerComponent = 8;
200 case CAIRO_FORMAT_A1:
201 #ifdef WORDS_BIGENDIAN
202 bitsPerComponent = 1;
207 case CAIRO_FORMAT_RGB30:
208 case CAIRO_FORMAT_RGB16_565:
209 case CAIRO_FORMAT_INVALID:
214 dataProvider = CGDataProviderCreateWithData (releaseInfo,
219 if (unlikely (!dataProvider)) {
222 releaseCallback (releaseInfo, data, height * stride);
226 if (format == CAIRO_FORMAT_A8 || format == CAIRO_FORMAT_A1) {
227 cairo_quartz_float_t decode[] = {1.0, 0.0};
228 image = CGImageMaskCreate (width, height,
236 image = CGImageCreate (width, height,
245 kCGRenderingIntentDefault);
249 CGDataProviderRelease (dataProvider);
251 if (colorSpace != colorSpaceOverride)
252 CGColorSpaceRelease (colorSpace);
257 static inline cairo_bool_t
258 _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
260 if (unlikely (cgc == NULL))
263 if (likely (CGContextGetTypePtr)) {
264 /* 4 is the type value of a bitmap context */
265 return CGContextGetTypePtr (cgc) == 4;
268 /* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
269 return CGBitmapContextGetBitsPerPixel (cgc) != 0;
272 /* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
274 #define CG_MAX_HEIGHT SHRT_MAX
275 #define CG_MAX_WIDTH USHRT_MAX
277 /* is the desired size of the surface within bounds? */
279 _cairo_quartz_verify_surface_size (int width, int height)
281 /* hmmm, allow width, height == 0 ? */
282 if (width < 0 || height < 0)
285 if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
292 * Cairo path -> Quartz path conversion helpers
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 double x = _cairo_fixed_to_double (point->x);
302 double y = _cairo_fixed_to_double (point->y);
304 CGContextMoveToPoint (closure, x, y);
305 return CAIRO_STATUS_SUCCESS;
308 static cairo_status_t
309 _cairo_path_to_quartz_context_line_to (void *closure,
310 const cairo_point_t *point)
312 //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
313 double x = _cairo_fixed_to_double (point->x);
314 double y = _cairo_fixed_to_double (point->y);
316 CGContextAddLineToPoint (closure, x, y);
317 return CAIRO_STATUS_SUCCESS;
320 static cairo_status_t
321 _cairo_path_to_quartz_context_curve_to (void *closure,
322 const cairo_point_t *p0,
323 const cairo_point_t *p1,
324 const cairo_point_t *p2)
326 //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
327 // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
328 // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
329 // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
330 double x0 = _cairo_fixed_to_double (p0->x);
331 double y0 = _cairo_fixed_to_double (p0->y);
332 double x1 = _cairo_fixed_to_double (p1->x);
333 double y1 = _cairo_fixed_to_double (p1->y);
334 double x2 = _cairo_fixed_to_double (p2->x);
335 double y2 = _cairo_fixed_to_double (p2->y);
337 CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
338 return CAIRO_STATUS_SUCCESS;
341 static cairo_status_t
342 _cairo_path_to_quartz_context_close_path (void *closure)
344 //ND ((stderr, "closepath\n"));
345 CGContextClosePath (closure);
346 return CAIRO_STATUS_SUCCESS;
350 _cairo_quartz_cairo_path_to_quartz_context (const cairo_path_fixed_t *path,
351 CGContextRef closure)
353 cairo_status_t status;
355 CGContextBeginPath (closure);
356 status = _cairo_path_fixed_interpret (path,
357 _cairo_path_to_quartz_context_move_to,
358 _cairo_path_to_quartz_context_line_to,
359 _cairo_path_to_quartz_context_curve_to,
360 _cairo_path_to_quartz_context_close_path,
363 assert (status == CAIRO_STATUS_SUCCESS);
367 * Misc helpers/callbacks
370 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
371 static PrivateCGCompositeMode
372 _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
375 case CAIRO_OPERATOR_CLEAR:
376 return kPrivateCGCompositeClear;
377 case CAIRO_OPERATOR_SOURCE:
378 return kPrivateCGCompositeCopy;
379 case CAIRO_OPERATOR_OVER:
380 return kPrivateCGCompositeSourceOver;
381 case CAIRO_OPERATOR_IN:
382 return kPrivateCGCompositeSourceIn;
383 case CAIRO_OPERATOR_OUT:
384 return kPrivateCGCompositeSourceOut;
385 case CAIRO_OPERATOR_ATOP:
386 return kPrivateCGCompositeSourceAtop;
387 case CAIRO_OPERATOR_DEST_OVER:
388 return kPrivateCGCompositeDestinationOver;
389 case CAIRO_OPERATOR_DEST_IN:
390 return kPrivateCGCompositeDestinationIn;
391 case CAIRO_OPERATOR_DEST_OUT:
392 return kPrivateCGCompositeDestinationOut;
393 case CAIRO_OPERATOR_DEST_ATOP:
394 return kPrivateCGCompositeDestinationAtop;
395 case CAIRO_OPERATOR_XOR:
396 return kPrivateCGCompositeXOR;
397 case CAIRO_OPERATOR_ADD:
398 return kPrivateCGCompositePlusLighter;
400 case CAIRO_OPERATOR_DEST:
401 case CAIRO_OPERATOR_SATURATE:
402 case CAIRO_OPERATOR_MULTIPLY:
403 case CAIRO_OPERATOR_SCREEN:
404 case CAIRO_OPERATOR_OVERLAY:
405 case CAIRO_OPERATOR_DARKEN:
406 case CAIRO_OPERATOR_LIGHTEN:
407 case CAIRO_OPERATOR_COLOR_DODGE:
408 case CAIRO_OPERATOR_COLOR_BURN:
409 case CAIRO_OPERATOR_HARD_LIGHT:
410 case CAIRO_OPERATOR_SOFT_LIGHT:
411 case CAIRO_OPERATOR_DIFFERENCE:
412 case CAIRO_OPERATOR_EXCLUSION:
413 case CAIRO_OPERATOR_HSL_HUE:
414 case CAIRO_OPERATOR_HSL_SATURATION:
415 case CAIRO_OPERATOR_HSL_COLOR:
416 case CAIRO_OPERATOR_HSL_LUMINOSITY:
424 _cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
427 case CAIRO_OPERATOR_MULTIPLY:
428 return kCGBlendModeMultiply;
429 case CAIRO_OPERATOR_SCREEN:
430 return kCGBlendModeScreen;
431 case CAIRO_OPERATOR_OVERLAY:
432 return kCGBlendModeOverlay;
433 case CAIRO_OPERATOR_DARKEN:
434 return kCGBlendModeDarken;
435 case CAIRO_OPERATOR_LIGHTEN:
436 return kCGBlendModeLighten;
437 case CAIRO_OPERATOR_COLOR_DODGE:
438 return kCGBlendModeColorDodge;
439 case CAIRO_OPERATOR_COLOR_BURN:
440 return kCGBlendModeColorBurn;
441 case CAIRO_OPERATOR_HARD_LIGHT:
442 return kCGBlendModeHardLight;
443 case CAIRO_OPERATOR_SOFT_LIGHT:
444 return kCGBlendModeSoftLight;
445 case CAIRO_OPERATOR_DIFFERENCE:
446 return kCGBlendModeDifference;
447 case CAIRO_OPERATOR_EXCLUSION:
448 return kCGBlendModeExclusion;
449 case CAIRO_OPERATOR_HSL_HUE:
450 return kCGBlendModeHue;
451 case CAIRO_OPERATOR_HSL_SATURATION:
452 return kCGBlendModeSaturation;
453 case CAIRO_OPERATOR_HSL_COLOR:
454 return kCGBlendModeColor;
455 case CAIRO_OPERATOR_HSL_LUMINOSITY:
456 return kCGBlendModeLuminosity;
458 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
459 case CAIRO_OPERATOR_CLEAR:
460 return kCGBlendModeClear;
461 case CAIRO_OPERATOR_SOURCE:
462 return kCGBlendModeCopy;
463 case CAIRO_OPERATOR_OVER:
464 return kCGBlendModeNormal;
465 case CAIRO_OPERATOR_IN:
466 return kCGBlendModeSourceIn;
467 case CAIRO_OPERATOR_OUT:
468 return kCGBlendModeSourceOut;
469 case CAIRO_OPERATOR_ATOP:
470 return kCGBlendModeSourceAtop;
471 case CAIRO_OPERATOR_DEST_OVER:
472 return kCGBlendModeDestinationOver;
473 case CAIRO_OPERATOR_DEST_IN:
474 return kCGBlendModeDestinationIn;
475 case CAIRO_OPERATOR_DEST_OUT:
476 return kCGBlendModeDestinationOut;
477 case CAIRO_OPERATOR_DEST_ATOP:
478 return kCGBlendModeDestinationAtop;
479 case CAIRO_OPERATOR_XOR:
480 return kCGBlendModeXOR;
481 case CAIRO_OPERATOR_ADD:
482 return kCGBlendModePlusLighter;
484 case CAIRO_OPERATOR_CLEAR:
485 case CAIRO_OPERATOR_SOURCE:
486 case CAIRO_OPERATOR_OVER:
487 case CAIRO_OPERATOR_IN:
488 case CAIRO_OPERATOR_OUT:
489 case CAIRO_OPERATOR_ATOP:
490 case CAIRO_OPERATOR_DEST_OVER:
491 case CAIRO_OPERATOR_DEST_IN:
492 case CAIRO_OPERATOR_DEST_OUT:
493 case CAIRO_OPERATOR_DEST_ATOP:
494 case CAIRO_OPERATOR_XOR:
495 case CAIRO_OPERATOR_ADD:
498 case CAIRO_OPERATOR_DEST:
499 case CAIRO_OPERATOR_SATURATE:
505 static cairo_int_status_t
506 _cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
508 CGBlendMode blendmode;
510 assert (op != CAIRO_OPERATOR_DEST);
512 /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
513 * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
514 * uses the definition from the Adobe Supplement.
516 if (op == CAIRO_OPERATOR_SATURATE ||
517 op == CAIRO_OPERATOR_COLOR_DODGE ||
518 op == CAIRO_OPERATOR_COLOR_BURN)
520 return CAIRO_INT_STATUS_UNSUPPORTED;
523 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
524 if (op <= CAIRO_OPERATOR_ADD) {
525 PrivateCGCompositeMode compmode;
527 compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
528 CGContextSetCompositeOperation (context, compmode);
529 return CAIRO_STATUS_SUCCESS;
533 blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
534 CGContextSetBlendMode (context, blendmode);
535 return CAIRO_STATUS_SUCCESS;
538 static cairo_int_status_t
539 _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
541 ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
543 /* When the destination has no color components, we can avoid some
544 * fallbacks, but we have to workaround operators which behave
545 * differently in Quartz. */
546 if (surface->base.content == CAIRO_CONTENT_ALPHA) {
547 assert (op != CAIRO_OPERATOR_ATOP); /* filtered by surface layer */
549 if (op == CAIRO_OPERATOR_SOURCE ||
550 op == CAIRO_OPERATOR_IN ||
551 op == CAIRO_OPERATOR_OUT ||
552 op == CAIRO_OPERATOR_DEST_IN ||
553 op == CAIRO_OPERATOR_DEST_ATOP ||
554 op == CAIRO_OPERATOR_XOR)
556 return CAIRO_INT_STATUS_UNSUPPORTED;
559 if (op == CAIRO_OPERATOR_DEST_OVER)
560 op = CAIRO_OPERATOR_OVER;
561 else if (op == CAIRO_OPERATOR_SATURATE)
562 op = CAIRO_OPERATOR_ADD;
563 else if (op == CAIRO_OPERATOR_COLOR_DODGE)
564 op = CAIRO_OPERATOR_OVER;
565 else if (op == CAIRO_OPERATOR_COLOR_BURN)
566 op = CAIRO_OPERATOR_OVER;
569 return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
572 static inline CGLineCap
573 _cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
579 case CAIRO_LINE_CAP_BUTT:
580 return kCGLineCapButt;
582 case CAIRO_LINE_CAP_ROUND:
583 return kCGLineCapRound;
585 case CAIRO_LINE_CAP_SQUARE:
586 return kCGLineCapSquare;
590 static inline CGLineJoin
591 _cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
597 case CAIRO_LINE_JOIN_MITER:
598 return kCGLineJoinMiter;
600 case CAIRO_LINE_JOIN_ROUND:
601 return kCGLineJoinRound;
603 case CAIRO_LINE_JOIN_BEVEL:
604 return kCGLineJoinBevel;
608 static inline CGInterpolationQuality
609 _cairo_quartz_filter_to_quartz (cairo_filter_t filter)
612 case CAIRO_FILTER_NEAREST:
613 case CAIRO_FILTER_FAST:
614 return kCGInterpolationNone;
616 case CAIRO_FILTER_BEST:
617 case CAIRO_FILTER_GOOD:
618 case CAIRO_FILTER_BILINEAR:
619 case CAIRO_FILTER_GAUSSIAN:
620 return kCGInterpolationDefault;
624 return kCGInterpolationDefault;
629 _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
630 CGAffineTransform *dst)
642 * Source -> Quartz setup and finish functions
646 ComputeGradientValue (void *info,
647 const cairo_quartz_float_t *in,
648 cairo_quartz_float_t *out)
651 const cairo_gradient_pattern_t *grad = (cairo_gradient_pattern_t*) info;
654 /* Put fdist back in the 0.0..1.0 range if we're doing
657 if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
658 fdist = fdist - floor (fdist);
659 } else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
660 fdist = fmod (fabs (fdist), 2.0);
665 for (i = 0; i < grad->n_stops; i++)
666 if (grad->stops[i].offset > fdist)
669 if (i == 0 || i == grad->n_stops) {
670 if (i == grad->n_stops)
672 out[0] = grad->stops[i].color.red;
673 out[1] = grad->stops[i].color.green;
674 out[2] = grad->stops[i].color.blue;
675 out[3] = grad->stops[i].color.alpha;
677 cairo_quartz_float_t ax = grad->stops[i-1].offset;
678 cairo_quartz_float_t bx = grad->stops[i].offset - ax;
679 cairo_quartz_float_t bp = (fdist - ax)/bx;
680 cairo_quartz_float_t ap = 1.0 - bp;
683 grad->stops[i-1].color.red * ap +
684 grad->stops[i].color.red * bp;
686 grad->stops[i-1].color.green * ap +
687 grad->stops[i].color.green * bp;
689 grad->stops[i-1].color.blue * ap +
690 grad->stops[i].color.blue * bp;
692 grad->stops[i-1].color.alpha * ap +
693 grad->stops[i].color.alpha * bp;
697 static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
698 0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
700 static const CGFunctionCallbacks gradient_callbacks = {
701 0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
704 /* Quartz computes a small number of samples of the gradient color
705 * function. On MacOS X 10.5 it apparently computes only 1024
707 #define MAX_GRADIENT_RANGE 1024
710 CairoQuartzCreateGradientFunction (const cairo_gradient_pattern_t *gradient,
711 const cairo_rectangle_int_t *extents,
712 cairo_circle_double_t *start,
713 cairo_circle_double_t *end)
715 cairo_pattern_t *pat;
716 cairo_quartz_float_t input_value_range[2];
718 if (gradient->base.extend != CAIRO_EXTEND_NONE) {
719 double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
720 double t[2], tolerance;
722 tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
723 tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
725 bounds_x1 = extents->x;
726 bounds_y1 = extents->y;
727 bounds_x2 = extents->x + extents->width;
728 bounds_y2 = extents->y + extents->height;
729 _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
730 &bounds_x1, &bounds_y1,
731 &bounds_x2, &bounds_y2,
734 _cairo_gradient_pattern_box_to_parameter (gradient,
735 bounds_x1, bounds_y1,
736 bounds_x2, bounds_y2,
740 if (gradient->base.extend == CAIRO_EXTEND_PAD) {
741 t[0] = MAX (t[0], -0.5);
742 t[1] = MIN (t[1], 1.5);
743 } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
746 /* set the input range for the function -- the function knows how
747 to map values outside of 0.0 .. 1.0 to the correct color */
748 input_value_range[0] = t[0];
749 input_value_range[1] = t[1];
751 input_value_range[0] = 0;
752 input_value_range[1] = 1;
755 _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
756 _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
758 if (_cairo_pattern_create_copy (&pat, &gradient->base))
761 return CGFunctionCreate (pat,
765 gradient_output_value_ranges,
766 &gradient_callbacks);
769 /* Obtain a CGImageRef from a #cairo_surface_t * */
772 cairo_surface_t *surface;
773 cairo_image_surface_t *image_out;
775 } quartz_source_image_t;
778 DataProviderReleaseCallback (void *info, const void *data, size_t size)
780 quartz_source_image_t *source_img = info;
781 _cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
785 static cairo_status_t
786 _cairo_surface_to_cgimage (cairo_surface_t *source,
787 cairo_rectangle_int_t *extents,
788 cairo_format_t format,
789 cairo_matrix_t *matrix,
790 const cairo_clip_t *clip,
791 CGImageRef *image_out)
793 cairo_status_t status;
794 quartz_source_image_t *source_img;
795 cairo_image_surface_t *image_surface;
797 if (source->backend && source->backend->type == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
798 cairo_quartz_image_surface_t *surface = (cairo_quartz_image_surface_t *) source;
799 *image_out = CGImageRetain (surface->image);
800 return CAIRO_STATUS_SUCCESS;
803 if (_cairo_surface_is_quartz (source)) {
804 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
805 if (IS_EMPTY (surface)) {
807 return CAIRO_INT_STATUS_NOTHING_TO_DO;
810 if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
811 *image_out = CGBitmapContextCreateImage (surface->cgContext);
813 return CAIRO_STATUS_SUCCESS;
817 source_img = malloc (sizeof (quartz_source_image_t));
818 if (unlikely (source_img == NULL))
819 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
821 source_img->surface = source;
823 if (source->type == CAIRO_SURFACE_TYPE_RECORDING) {
824 image_surface = (cairo_image_surface_t *)
825 cairo_image_surface_create (format, extents->width, extents->height);
826 if (unlikely (image_surface->base.status)) {
827 status = image_surface->base.status;
828 cairo_surface_destroy (&image_surface->base);
833 status = _cairo_recording_surface_replay_with_clip (source,
835 &image_surface->base,
837 if (unlikely (status)) {
838 cairo_surface_destroy (&image_surface->base);
843 source_img->image_out = image_surface;
844 source_img->image_extra = NULL;
846 cairo_matrix_init_identity (matrix);
849 status = _cairo_surface_acquire_source_image (source_img->surface,
850 &source_img->image_out,
851 &source_img->image_extra);
852 if (unlikely (status)) {
858 if (source_img->image_out->width == 0 || source_img->image_out->height == 0) {
860 DataProviderReleaseCallback (source_img,
861 source_img->image_out->data,
862 source_img->image_out->height * source_img->image_out->stride);
864 *image_out = CairoQuartzCreateCGImage (source_img->image_out->format,
865 source_img->image_out->width,
866 source_img->image_out->height,
867 source_img->image_out->stride,
868 source_img->image_out->data,
871 DataProviderReleaseCallback,
874 /* TODO: differentiate memory error and unsupported surface type */
875 if (unlikely (*image_out == NULL))
876 status = CAIRO_INT_STATUS_UNSUPPORTED;
882 /* Generic #cairo_pattern_t -> CGPattern function */
887 cairo_bool_t do_reflect;
888 } SurfacePatternDrawInfo;
891 SurfacePatternDrawFunc (void *ainfo, CGContextRef context)
893 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
895 CGContextTranslateCTM (context, 0, info->imageBounds.size.height);
896 CGContextScaleCTM (context, 1, -1);
898 CGContextDrawImage (context, info->imageBounds, info->image);
899 if (info->do_reflect) {
900 /* draw 3 more copies of the image, flipped.
901 * DrawImage draws the image according to the current Y-direction into the rectangle given
902 * (imageBounds); at the time of the first DrawImage above, the origin is at the bottom left
903 * of the base image position, and the Y axis is extending upwards.
906 /* Make the y axis extend downwards, and draw a flipped image below */
907 CGContextScaleCTM (context, 1, -1);
908 CGContextDrawImage (context, info->imageBounds, info->image);
910 /* Shift over to the right, and flip vertically (translation is 2x,
911 * since we'll be flipping and thus rendering the rectangle "backwards"
913 CGContextTranslateCTM (context, 2 * info->imageBounds.size.width, 0);
914 CGContextScaleCTM (context, -1, 1);
915 CGContextDrawImage (context, info->imageBounds, info->image);
917 /* Then unflip the Y-axis again, and draw the image above the point. */
918 CGContextScaleCTM (context, 1, -1);
919 CGContextDrawImage (context, info->imageBounds, info->image);
924 SurfacePatternReleaseInfoFunc (void *ainfo)
926 SurfacePatternDrawInfo *info = (SurfacePatternDrawInfo*) ainfo;
928 CGImageRelease (info->image);
932 static cairo_int_status_t
933 _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t *dest,
934 const cairo_pattern_t *apattern,
935 const cairo_clip_t *clip,
938 cairo_surface_pattern_t *spattern;
939 cairo_surface_t *pat_surf;
940 cairo_rectangle_int_t extents;
941 cairo_format_t format = _cairo_format_from_content (dest->base.content);
945 CGAffineTransform ptransform, stransform;
946 CGPatternCallbacks cb = { 0,
947 SurfacePatternDrawFunc,
948 SurfacePatternReleaseInfoFunc };
949 SurfacePatternDrawInfo *info;
950 cairo_quartz_float_t rw, rh;
951 cairo_status_t status;
952 cairo_bool_t is_bounded;
956 /* SURFACE is the only type we'll handle here */
957 assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
959 spattern = (cairo_surface_pattern_t *) apattern;
960 pat_surf = spattern->surface;
962 if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
963 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
967 _cairo_surface_get_extents (&dest->base, &extents);
969 m = spattern->base.matrix;
970 status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
972 if (unlikely (status))
975 info = malloc (sizeof (SurfacePatternDrawInfo));
976 if (unlikely (!info))
977 return CAIRO_STATUS_NO_MEMORY;
979 /* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
980 * that the data will stick around for this image when the printer gets to it.
981 * Otherwise, the underlying data store may disappear from under us!
983 * _cairo_surface_to_cgimage will copy when it converts non-Quartz surfaces,
984 * since the Quartz surfaces have a higher chance of sticking around. If the
985 * source is a quartz image surface, then it's set up to retain a ref to the
986 * image surface that it's backed by.
989 info->imageBounds = CGRectMake (0, 0, extents.width, extents.height);
990 info->do_reflect = FALSE;
992 pbounds.origin.x = 0;
993 pbounds.origin.y = 0;
995 if (spattern->base.extend == CAIRO_EXTEND_REFLECT) {
996 pbounds.size.width = 2.0 * extents.width;
997 pbounds.size.height = 2.0 * extents.height;
998 info->do_reflect = TRUE;
1000 pbounds.size.width = extents.width;
1001 pbounds.size.height = extents.height;
1003 rw = pbounds.size.width;
1004 rh = pbounds.size.height;
1006 cairo_matrix_invert (&m);
1007 _cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
1009 /* The pattern matrix is relative to the bottom left, again; the
1010 * incoming cairo pattern matrix is relative to the upper left.
1011 * So we take the pattern matrix and the original context matrix,
1012 * which gives us the correct base translation/y flip.
1014 ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
1017 ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
1018 ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
1019 CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
1020 ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
1023 *cgpat = CGPatternCreate (info,
1027 kCGPatternTilingConstantSpacing, /* kCGPatternTilingNoDistortion, */
1031 return CAIRO_STATUS_SUCCESS;
1034 /* State used during a drawing operation. */
1036 /* The destination of the mask */
1037 CGContextRef cgMaskContext;
1039 /* The destination of the drawing of the source */
1040 CGContextRef cgDrawContext;
1042 /* The filter to be used when drawing the source */
1043 CGInterpolationQuality filter;
1046 cairo_quartz_action_t action;
1048 /* Destination rect */
1051 /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
1052 CGAffineTransform transform;
1054 /* Used with DO_IMAGE and DO_TILED_IMAGE */
1057 /* Used with DO_SHADING */
1058 CGShadingRef shading;
1060 /* Temporary destination for unbounded operations */
1063 } cairo_quartz_drawing_state_t;
1066 Quartz does not support repeating radients. We handle repeating gradients
1067 by manually extending the gradient and repeating color stops. We need to
1068 minimize the number of repetitions since Quartz seems to sample our color
1069 function across the entire range, even if part of that range is not needed
1070 for the visible area of the gradient, and it samples with some fixed resolution,
1071 so if the gradient range is too large it samples with very low resolution and
1072 the gradient is very coarse. _cairo_quartz_create_gradient_function computes
1073 the number of repetitions needed based on the extents.
1075 static cairo_int_status_t
1076 _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
1077 const cairo_gradient_pattern_t *gradient,
1078 const cairo_rectangle_int_t *extents)
1081 cairo_circle_double_t start, end;
1082 CGFunctionRef gradFunc;
1083 CGColorSpaceRef rgb;
1084 bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
1086 assert (gradient->n_stops > 0);
1088 mat = gradient->base.matrix;
1089 cairo_matrix_invert (&mat);
1090 _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
1092 gradFunc = CairoQuartzCreateGradientFunction (gradient, extents,
1095 if (unlikely (gradFunc == NULL))
1096 return CAIRO_INT_STATUS_UNSUPPORTED;
1098 rgb = CGColorSpaceCreateDeviceRGB ();
1100 if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
1101 state->shading = CGShadingCreateAxial (rgb,
1102 CGPointMake (start.center.x,
1104 CGPointMake (end.center.x,
1109 state->shading = CGShadingCreateRadial (rgb,
1110 CGPointMake (start.center.x,
1112 MAX (start.radius, 0),
1113 CGPointMake (end.center.x,
1115 MAX (end.radius, 0),
1120 CGColorSpaceRelease (rgb);
1121 CGFunctionRelease (gradFunc);
1123 state->action = DO_SHADING;
1124 return CAIRO_STATUS_SUCCESS;
1127 static cairo_int_status_t
1128 _cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
1129 cairo_composite_rectangles_t *composite)
1131 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) composite->surface;
1132 cairo_operator_t op = composite->op;
1133 const cairo_pattern_t *source = &composite->source_pattern.base;
1134 const cairo_clip_t *clip = composite->clip;
1135 cairo_bool_t needs_temp;
1136 cairo_status_t status;
1137 cairo_format_t format = _cairo_format_from_content (composite->surface->content);
1139 state->layer = NULL;
1140 state->image = NULL;
1141 state->shading = NULL;
1142 state->cgDrawContext = NULL;
1143 state->cgMaskContext = NULL;
1145 status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
1146 if (unlikely (status))
1149 status = _cairo_quartz_surface_set_cairo_operator (surface, op);
1150 if (unlikely (status))
1153 /* Save before we change the pattern, colorspace, etc. so that
1154 * we can restore and make sure that quartz releases our
1155 * pattern (which may be stack allocated)
1158 CGContextSaveGState (surface->cgContext);
1159 state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
1160 state->clipRect = CGRectIntegral (state->clipRect);
1161 state->rect = state->clipRect;
1163 state->cgMaskContext = surface->cgContext;
1164 state->cgDrawContext = state->cgMaskContext;
1166 state->filter = _cairo_quartz_filter_to_quartz (source->filter);
1168 if (op == CAIRO_OPERATOR_CLEAR) {
1169 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1171 state->action = DO_DIRECT;
1172 return CAIRO_STATUS_SUCCESS;
1176 * To implement mask unbounded operations Quartz needs a temporary
1177 * surface which will be composited entirely (ignoring the mask).
1178 * To implement source unbounded operations Quartz needs a
1179 * temporary surface which allows extending the source to a size
1180 * covering the whole mask, but there are some optimization
1183 * - CLEAR completely ignores the source, thus we can just use a
1186 * - SOURCE can be implemented by drawing the source and clearing
1187 * outside of the source as long as the two regions have no
1188 * intersection. This happens when the source is a pixel-aligned
1189 * rectangle. If the source is at least as big as the
1190 * intersection between the clip rectangle and the mask
1191 * rectangle, no clear operation is needed.
1193 needs_temp = ! _cairo_operator_bounded_by_mask (op);
1196 state->layer = CGLayerCreateWithContext (surface->cgContext,
1197 state->clipRect.size,
1199 state->cgDrawContext = CGLayerGetContext (state->layer);
1200 state->cgMaskContext = state->cgDrawContext;
1201 CGContextTranslateCTM (state->cgDrawContext,
1202 -state->clipRect.origin.x,
1203 -state->clipRect.origin.y);
1206 if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
1207 cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
1209 CGContextSetRGBStrokeColor (state->cgDrawContext,
1213 solid->color.alpha);
1214 CGContextSetRGBFillColor (state->cgDrawContext,
1218 solid->color.alpha);
1220 state->action = DO_DIRECT;
1221 return CAIRO_STATUS_SUCCESS;
1224 if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
1225 source->type == CAIRO_PATTERN_TYPE_RADIAL)
1227 const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
1228 cairo_rectangle_int_t extents;
1230 extents = surface->virtual_extents;
1231 extents.x -= surface->base.device_transform.x0;
1232 extents.y -= surface->base.device_transform.y0;
1233 _cairo_rectangle_union (&extents, &surface->extents);
1235 return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
1238 if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
1239 (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
1241 const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
1242 cairo_surface_t *pat_surf = spat->surface;
1244 cairo_matrix_t m = spat->base.matrix;
1245 cairo_rectangle_int_t extents;
1246 CGAffineTransform xform;
1248 cairo_fixed_t fw, fh;
1249 cairo_bool_t is_bounded;
1251 _cairo_surface_get_extents (composite->surface, &extents);
1252 status = _cairo_surface_to_cgimage (pat_surf, &extents, format,
1254 if (unlikely (status))
1259 if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
1260 m.x0 = -ceil (m.x0 - 0.5);
1261 m.y0 = -ceil (m.y0 - 0.5);
1263 cairo_matrix_invert (&m);
1266 _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
1268 if (pat_surf->type != CAIRO_SURFACE_TYPE_RECORDING) {
1269 is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
1270 assert (is_bounded);
1273 srcRect = CGRectMake (0, 0, extents.width, extents.height);
1275 if (source->extend == CAIRO_EXTEND_NONE) {
1277 if (op == CAIRO_OPERATOR_SOURCE &&
1278 (pat_surf->content == CAIRO_CONTENT_ALPHA ||
1279 ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
1281 state->layer = CGLayerCreateWithContext (surface->cgContext,
1282 state->clipRect.size,
1284 state->cgDrawContext = CGLayerGetContext (state->layer);
1285 CGContextTranslateCTM (state->cgDrawContext,
1286 -state->clipRect.origin.x,
1287 -state->clipRect.origin.y);
1290 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1292 state->rect = srcRect;
1293 state->action = DO_IMAGE;
1294 return CAIRO_STATUS_SUCCESS;
1297 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
1299 /* Quartz seems to tile images at pixel-aligned regions only -- this
1300 * leads to seams if the image doesn't end up scaling to fill the
1301 * space exactly. The CGPattern tiling approach doesn't have this
1302 * problem. Check if we're going to fill up the space (within some
1303 * epsilon), and if not, fall back to the CGPattern type.
1306 xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
1309 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1311 fw = _cairo_fixed_from_double (srcRect.size.width);
1312 fh = _cairo_fixed_from_double (srcRect.size.height);
1314 if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON &&
1315 (fh & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON)
1317 /* We're good to use DrawTiledImage, but ensure that
1318 * the math works out */
1320 srcRect.size.width = round (srcRect.size.width);
1321 srcRect.size.height = round (srcRect.size.height);
1323 xform = CGAffineTransformInvert (xform);
1325 srcRect = CGRectApplyAffineTransform (srcRect, xform);
1327 state->rect = srcRect;
1328 state->action = DO_TILED_IMAGE;
1329 return CAIRO_STATUS_SUCCESS;
1332 /* Fall through to generic SURFACE case */
1335 if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
1336 cairo_quartz_float_t patternAlpha = 1.0f;
1337 CGColorSpaceRef patternSpace;
1338 CGPatternRef pattern = NULL;
1339 cairo_int_status_t status;
1341 status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, clip, &pattern);
1342 if (unlikely (status))
1345 patternSpace = CGColorSpaceCreatePattern (NULL);
1346 CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
1347 CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
1348 CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
1349 CGContextSetStrokePattern (state->cgDrawContext, pattern, &patternAlpha);
1350 CGColorSpaceRelease (patternSpace);
1352 /* Quartz likes to munge the pattern phase (as yet unexplained
1353 * why); force it to 0,0 as we've already baked in the correct
1354 * pattern translation into the pattern matrix
1356 CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
1358 CGPatternRelease (pattern);
1360 state->action = DO_DIRECT;
1361 return CAIRO_STATUS_SUCCESS;
1364 return CAIRO_INT_STATUS_UNSUPPORTED;
1368 _cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
1369 cairo_composite_rectangles_t *extents)
1371 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) extents->surface;
1374 CGContextDrawLayerInRect (surface->cgContext,
1377 CGContextRelease (state->cgDrawContext);
1378 CGLayerRelease (state->layer);
1381 if (state->cgMaskContext)
1382 CGContextRestoreGState (surface->cgContext);
1385 CGImageRelease (state->image);
1388 CGShadingRelease (state->shading);
1392 _cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
1393 cairo_operator_t op)
1395 CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
1396 CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
1398 if (state->action == DO_DIRECT) {
1399 CGContextFillRect (state->cgDrawContext, state->rect);
1403 CGContextConcatCTM (state->cgDrawContext, state->transform);
1405 if (state->action == DO_SHADING) {
1406 CGContextDrawShading (state->cgDrawContext, state->shading);
1410 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1411 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1413 if (state->action == DO_IMAGE) {
1414 CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
1415 if (op == CAIRO_OPERATOR_SOURCE &&
1416 state->cgDrawContext == state->cgMaskContext)
1418 CGContextBeginPath (state->cgDrawContext);
1419 CGContextAddRect (state->cgDrawContext, state->rect);
1421 CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
1422 CGContextScaleCTM (state->cgDrawContext, 1, -1);
1423 CGContextConcatCTM (state->cgDrawContext,
1424 CGAffineTransformInvert (state->transform));
1426 CGContextAddRect (state->cgDrawContext, state->clipRect);
1428 CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
1429 CGContextEOFillPath (state->cgDrawContext);
1432 CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
1436 static cairo_image_surface_t *
1437 _cairo_quartz_surface_map_to_image (void *abstract_surface,
1438 const cairo_rectangle_int_t *extents)
1440 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1441 unsigned int stride, bitinfo, bpp, color_comps;
1442 CGColorSpaceRef colorspace;
1444 cairo_format_t format;
1446 if (surface->imageSurfaceEquiv)
1447 return _cairo_surface_map_to_image (surface->imageSurfaceEquiv, extents);
1449 if (IS_EMPTY (surface))
1450 return (cairo_image_surface_t *) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
1452 if (! _cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext))
1453 return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1455 bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
1456 bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
1458 // let's hope they don't add YUV under us
1459 colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
1460 color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
1462 /* XXX TODO: We can handle many more data formats by
1463 * converting to pixman_format_t */
1465 if (bpp == 32 && color_comps == 3 &&
1466 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaPremultipliedFirst &&
1467 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1469 format = CAIRO_FORMAT_ARGB32;
1471 else if (bpp == 32 && color_comps == 3 &&
1472 (bitinfo & kCGBitmapAlphaInfoMask) == kCGImageAlphaNoneSkipFirst &&
1473 (bitinfo & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Host)
1475 format = CAIRO_FORMAT_RGB24;
1477 else if (bpp == 8 && color_comps == 1)
1479 format = CAIRO_FORMAT_A1;
1483 return _cairo_image_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
1486 imageData = CGBitmapContextGetData (surface->cgContext);
1487 stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
1489 return (cairo_image_surface_t *) cairo_image_surface_create_for_data (imageData,
1496 static cairo_int_status_t
1497 _cairo_quartz_surface_unmap_image (void *abstract_surface,
1498 cairo_image_surface_t *image)
1500 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1502 if (surface->imageSurfaceEquiv)
1503 return _cairo_surface_unmap_image (surface->imageSurfaceEquiv, image);
1505 cairo_surface_finish (&image->base);
1506 cairo_surface_destroy (&image->base);
1508 return CAIRO_STATUS_SUCCESS;
1513 * Cairo surface backend implementations
1516 static cairo_status_t
1517 _cairo_quartz_surface_finish (void *abstract_surface)
1519 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1521 ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
1523 if (IS_EMPTY (surface))
1524 return CAIRO_STATUS_SUCCESS;
1526 /* Restore our saved gstate that we use to reset clipping */
1527 CGContextRestoreGState (surface->cgContext);
1528 _cairo_surface_clipper_reset (&surface->clipper);
1530 CGContextRelease (surface->cgContext);
1532 surface->cgContext = NULL;
1534 if (surface->imageSurfaceEquiv) {
1535 cairo_surface_destroy (surface->imageSurfaceEquiv);
1536 surface->imageSurfaceEquiv = NULL;
1539 free (surface->imageData);
1540 surface->imageData = NULL;
1542 return CAIRO_STATUS_SUCCESS;
1545 static cairo_status_t
1546 _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
1547 cairo_image_surface_t **image_out,
1550 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1552 //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
1554 *image_extra = NULL;
1556 *image_out = _cairo_quartz_surface_map_to_image (surface, &surface->extents);
1557 if (unlikely (cairo_surface_status(&(*image_out)->base))) {
1558 cairo_surface_destroy (&(*image_out)->base);
1560 return _cairo_error (CAIRO_STATUS_NO_MEMORY);
1563 return CAIRO_STATUS_SUCCESS;
1567 _cairo_quartz_surface_release_source_image (void *abstract_surface,
1568 cairo_image_surface_t *image,
1571 _cairo_quartz_surface_unmap_image (abstract_surface, image);
1574 static cairo_surface_t *
1575 _cairo_quartz_surface_create_similar (void *abstract_surface,
1576 cairo_content_t content,
1580 cairo_quartz_surface_t *surface, *similar_quartz;
1581 cairo_surface_t *similar;
1582 cairo_format_t format;
1584 if (content == CAIRO_CONTENT_COLOR_ALPHA)
1585 format = CAIRO_FORMAT_ARGB32;
1586 else if (content == CAIRO_CONTENT_COLOR)
1587 format = CAIRO_FORMAT_RGB24;
1588 else if (content == CAIRO_CONTENT_ALPHA)
1589 format = CAIRO_FORMAT_A8;
1593 // verify width and height of surface
1594 if (!_cairo_quartz_verify_surface_size (width, height)) {
1595 return _cairo_surface_create_in_error (_cairo_error
1596 (CAIRO_STATUS_INVALID_SIZE));
1599 similar = cairo_quartz_surface_create (format, width, height);
1600 if (unlikely (similar->status))
1603 surface = (cairo_quartz_surface_t *) abstract_surface;
1604 similar_quartz = (cairo_quartz_surface_t *) similar;
1605 similar_quartz->virtual_extents = surface->virtual_extents;
1611 _cairo_quartz_surface_get_extents (void *abstract_surface,
1612 cairo_rectangle_int_t *extents)
1614 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
1616 *extents = surface->extents;
1620 static cairo_int_status_t
1621 _cairo_quartz_cg_paint (const cairo_compositor_t *compositor,
1622 cairo_composite_rectangles_t *extents)
1624 cairo_quartz_drawing_state_t state;
1625 cairo_int_status_t rv;
1627 ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n",
1628 extents->surface, extents->op, extents->source_pattern.base.type));
1630 rv = _cairo_quartz_setup_state (&state, extents);
1634 _cairo_quartz_draw_source (&state, extents->op);
1637 _cairo_quartz_teardown_state (&state, extents);
1639 ND ((stderr, "-- paint\n"));
1643 static cairo_int_status_t
1644 _cairo_quartz_cg_mask_with_surface (cairo_composite_rectangles_t *extents,
1645 cairo_surface_t *mask_surf,
1646 const cairo_matrix_t *mask_mat,
1647 CGInterpolationQuality filter)
1651 cairo_status_t status;
1652 CGAffineTransform mask_matrix;
1653 cairo_quartz_drawing_state_t state;
1654 cairo_format_t format = _cairo_format_from_content (extents->surface->content);
1655 cairo_rectangle_int_t dest_extents;
1656 cairo_matrix_t m = *mask_mat;
1658 _cairo_surface_get_extents (extents->surface, &dest_extents);
1659 status = _cairo_surface_to_cgimage (mask_surf, &dest_extents, format,
1660 &m, extents->clip, &img);
1661 if (unlikely (status))
1664 status = _cairo_quartz_setup_state (&state, extents);
1665 if (unlikely (status))
1668 rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
1669 _cairo_quartz_cairo_matrix_to_quartz (&m, &mask_matrix);
1671 /* ClipToMask is essentially drawing an image, so we need to flip the CTM
1672 * to get the image to appear oriented the right way */
1673 CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
1674 CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
1675 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1677 state.filter = filter;
1679 CGContextSetInterpolationQuality (state.cgMaskContext, filter);
1680 CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
1682 CGContextClipToMask (state.cgMaskContext, rect, img);
1684 CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
1685 CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
1686 CGContextConcatCTM (state.cgMaskContext, mask_matrix);
1688 _cairo_quartz_draw_source (&state, extents->op);
1691 _cairo_quartz_teardown_state (&state, extents);
1693 CGImageRelease (img);
1698 static cairo_int_status_t
1699 _cairo_quartz_cg_mask_with_solid (cairo_quartz_surface_t *surface,
1700 cairo_composite_rectangles_t *extents)
1702 cairo_quartz_drawing_state_t state;
1703 double alpha = extents->mask_pattern.solid.color.alpha;
1704 cairo_status_t status;
1706 status = _cairo_quartz_setup_state (&state, extents);
1707 if (unlikely (status))
1710 CGContextSetAlpha (surface->cgContext, alpha);
1711 _cairo_quartz_draw_source (&state, extents->op);
1713 _cairo_quartz_teardown_state (&state, extents);
1715 return CAIRO_STATUS_SUCCESS;
1718 static cairo_int_status_t
1719 _cairo_quartz_cg_mask (const cairo_compositor_t *compositor,
1720 cairo_composite_rectangles_t *extents)
1722 cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *)extents->surface;
1723 const cairo_pattern_t *source = &extents->source_pattern.base;
1724 const cairo_pattern_t *mask = &extents->mask_pattern.base;
1725 cairo_surface_t *mask_surf;
1726 cairo_matrix_t matrix;
1727 cairo_status_t status;
1728 cairo_bool_t need_temp;
1729 CGInterpolationQuality filter;
1731 ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n",
1732 extents->surface, extents->op, extents->source_pattern.base.type,
1733 extents->mask_pattern.base.type));
1735 if (mask->type == CAIRO_PATTERN_TYPE_SOLID)
1736 return _cairo_quartz_cg_mask_with_solid (surface, extents);
1738 need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
1739 mask->extend != CAIRO_EXTEND_NONE);
1741 filter = _cairo_quartz_filter_to_quartz (source->filter);
1744 mask_surf = extents->mask_pattern.surface.surface;
1746 /* When an opaque surface used as a mask in Quartz, its
1747 * luminosity is used as the alpha value, so we con only use
1748 * surfaces with alpha without creating a temporary mask. */
1749 need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
1753 CGInterpolationQuality mask_filter;
1754 cairo_bool_t simple_transform;
1756 matrix = mask->matrix;
1758 mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
1759 if (mask_filter == kCGInterpolationNone) {
1760 simple_transform = _cairo_matrix_is_translation (&matrix);
1761 if (simple_transform) {
1762 matrix.x0 = ceil (matrix.x0 - 0.5);
1763 matrix.y0 = ceil (matrix.y0 - 0.5);
1766 simple_transform = _cairo_matrix_is_integer_translation (&matrix,
1771 /* Quartz only allows one interpolation to be set for mask and
1772 * source, so we can skip the temp surface only if the source
1773 * filtering makes the mask look correct. */
1774 if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
1775 need_temp = ! (simple_transform || filter == mask_filter);
1777 filter = mask_filter;
1781 /* Render the mask to a surface */
1782 mask_surf = _cairo_quartz_surface_create_similar (surface,
1783 CAIRO_CONTENT_ALPHA,
1784 surface->extents.width,
1785 surface->extents.height);
1786 status = mask_surf->status;
1787 if (unlikely (status))
1790 /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
1791 * temporary layer or fallback to cairo-image. */
1792 status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
1793 if (unlikely (status))
1796 cairo_matrix_init_identity (&matrix);
1799 status = _cairo_quartz_cg_mask_with_surface (extents,
1800 mask_surf, &matrix, filter);
1805 cairo_surface_destroy (mask_surf);
1810 static cairo_int_status_t
1811 _cairo_quartz_cg_fill (const cairo_compositor_t *compositor,
1812 cairo_composite_rectangles_t *extents,
1813 const cairo_path_fixed_t *path,
1814 cairo_fill_rule_t fill_rule,
1816 cairo_antialias_t antialias)
1818 cairo_quartz_drawing_state_t state;
1819 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1821 ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n",
1822 extents->surface, extents->op, extents->source_pattern.base.type));
1824 rv = _cairo_quartz_setup_state (&state, extents);
1828 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1830 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1832 if (state.action == DO_DIRECT) {
1833 assert (state.cgDrawContext == state.cgMaskContext);
1834 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1835 CGContextFillPath (state.cgMaskContext);
1837 CGContextEOFillPath (state.cgMaskContext);
1839 if (fill_rule == CAIRO_FILL_RULE_WINDING)
1840 CGContextClip (state.cgMaskContext);
1842 CGContextEOClip (state.cgMaskContext);
1844 _cairo_quartz_draw_source (&state, extents->op);
1848 _cairo_quartz_teardown_state (&state, extents);
1850 ND ((stderr, "-- fill\n"));
1854 static cairo_int_status_t
1855 _cairo_quartz_cg_stroke (const cairo_compositor_t *compositor,
1856 cairo_composite_rectangles_t *extents,
1857 const cairo_path_fixed_t *path,
1858 const cairo_stroke_style_t *style,
1859 const cairo_matrix_t *ctm,
1860 const cairo_matrix_t *ctm_inverse,
1862 cairo_antialias_t antialias)
1864 cairo_quartz_drawing_state_t state;
1865 cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
1866 CGAffineTransform strokeTransform, invStrokeTransform;
1868 ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n",
1869 extents->surface, extents->op, extents->source_pattern.base.type));
1871 rv = _cairo_quartz_setup_state (&state, extents);
1875 // Turning antialiasing off used to cause misrendering with
1876 // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
1877 // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
1878 CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
1879 CGContextSetLineWidth (state.cgMaskContext, style->line_width);
1880 CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
1881 CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
1882 CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
1884 if (style->dash && style->num_dashes) {
1885 cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
1886 cairo_quartz_float_t *fdash = sdash;
1887 unsigned int max_dashes = style->num_dashes;
1890 if (style->num_dashes%2)
1892 if (max_dashes > ARRAY_LENGTH (sdash))
1893 fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
1894 if (unlikely (fdash == NULL)) {
1895 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
1899 for (k = 0; k < max_dashes; k++)
1900 fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
1902 CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
1906 CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
1908 _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
1910 _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
1911 CGContextConcatCTM (state.cgMaskContext, strokeTransform);
1913 if (state.action == DO_DIRECT) {
1914 assert (state.cgDrawContext == state.cgMaskContext);
1915 CGContextStrokePath (state.cgMaskContext);
1917 CGContextReplacePathWithStrokedPath (state.cgMaskContext);
1918 CGContextClip (state.cgMaskContext);
1920 _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
1921 CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
1923 _cairo_quartz_draw_source (&state, extents->op);
1927 _cairo_quartz_teardown_state (&state, extents);
1929 ND ((stderr, "-- stroke\n"));
1933 #if CAIRO_HAS_QUARTZ_FONT
1934 static cairo_int_status_t
1935 _cairo_quartz_cg_glyphs (const cairo_compositor_t *compositor,
1936 cairo_composite_rectangles_t *extents,
1937 cairo_scaled_font_t *scaled_font,
1938 cairo_glyph_t *glyphs,
1940 cairo_bool_t overlap)
1942 CGAffineTransform textTransform, invTextTransform;
1943 CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1944 CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
1945 CGGlyph *cg_glyphs = &glyphs_static[0];
1946 CGSize *cg_advances = &cg_advances_static[0];
1947 COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
1949 cairo_quartz_drawing_state_t state;
1950 cairo_int_status_t rv = CAIRO_INT_STATUS_UNSUPPORTED;
1951 cairo_quartz_float_t xprev, yprev;
1953 CGFontRef cgfref = NULL;
1955 cairo_bool_t didForceFontSmoothing = FALSE;
1957 if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
1958 return CAIRO_INT_STATUS_UNSUPPORTED;
1960 rv = _cairo_quartz_setup_state (&state, extents);
1964 if (state.action == DO_DIRECT) {
1965 assert (state.cgDrawContext == state.cgMaskContext);
1966 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
1968 CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
1971 /* this doesn't addref */
1972 cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
1973 CGContextSetFont (state.cgMaskContext, cgfref);
1974 CGContextSetFontSize (state.cgMaskContext, 1.0);
1976 switch (scaled_font->options.antialias) {
1977 case CAIRO_ANTIALIAS_SUBPIXEL:
1978 case CAIRO_ANTIALIAS_BEST:
1979 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1980 CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
1981 if (CGContextSetAllowsFontSmoothingPtr &&
1982 !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
1984 didForceFontSmoothing = TRUE;
1985 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
1988 case CAIRO_ANTIALIAS_NONE:
1989 CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
1991 case CAIRO_ANTIALIAS_GRAY:
1992 case CAIRO_ANTIALIAS_GOOD:
1993 case CAIRO_ANTIALIAS_FAST:
1994 CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
1995 CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
1997 case CAIRO_ANTIALIAS_DEFAULT:
1998 /* Don't do anything */
2002 if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
2003 cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
2004 if (unlikely (cg_glyphs == NULL)) {
2005 rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
2009 cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
2012 /* scale(1,-1) * scaled_font->scale */
2013 textTransform = CGAffineTransformMake (scaled_font->scale.xx,
2014 scaled_font->scale.yx,
2015 -scaled_font->scale.xy,
2016 -scaled_font->scale.yy,
2019 /* scaled_font->scale_inverse * scale(1,-1) */
2020 invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
2021 -scaled_font->scale_inverse.yx,
2022 scaled_font->scale_inverse.xy,
2023 -scaled_font->scale_inverse.yy,
2026 CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
2027 CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
2029 /* Convert our glyph positions to glyph advances. We need n-1 advances,
2030 * since the advance at index 0 is applied after glyph 0. */
2031 xprev = glyphs[0].x;
2032 yprev = glyphs[0].y;
2034 cg_glyphs[0] = glyphs[0].index;
2036 for (i = 1; i < num_glyphs; i++) {
2037 cairo_quartz_float_t xf = glyphs[i].x;
2038 cairo_quartz_float_t yf = glyphs[i].y;
2039 cg_glyphs[i] = glyphs[i].index;
2040 cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
2045 /* Translate to the first glyph's position before drawing */
2046 CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
2047 CGContextConcatCTM (state.cgMaskContext, textTransform);
2049 CGContextShowGlyphsWithAdvances (state.cgMaskContext,
2054 CGContextConcatCTM (state.cgMaskContext, invTextTransform);
2055 CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
2057 if (state.action != DO_DIRECT)
2058 _cairo_quartz_draw_source (&state, extents->op);
2061 if (didForceFontSmoothing)
2062 CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
2064 _cairo_quartz_teardown_state (&state, extents);
2066 if (cg_glyphs != glyphs_static)
2071 #endif /* CAIRO_HAS_QUARTZ_FONT */
2073 static const cairo_compositor_t _cairo_quartz_cg_compositor = {
2074 &_cairo_fallback_compositor,
2076 _cairo_quartz_cg_paint,
2077 _cairo_quartz_cg_mask,
2078 _cairo_quartz_cg_stroke,
2079 _cairo_quartz_cg_fill,
2080 #if CAIRO_HAS_QUARTZ_FONT
2081 _cairo_quartz_cg_glyphs,
2087 static cairo_int_status_t
2088 _cairo_quartz_surface_paint (void *surface,
2089 cairo_operator_t op,
2090 const cairo_pattern_t *source,
2091 const cairo_clip_t *clip)
2093 return _cairo_compositor_paint (&_cairo_quartz_cg_compositor,
2094 surface, op, source, clip);
2097 static cairo_int_status_t
2098 _cairo_quartz_surface_mask (void *surface,
2099 cairo_operator_t op,
2100 const cairo_pattern_t *source,
2101 const cairo_pattern_t *mask,
2102 const cairo_clip_t *clip)
2104 return _cairo_compositor_mask (&_cairo_quartz_cg_compositor,
2105 surface, op, source, mask,
2109 static cairo_int_status_t
2110 _cairo_quartz_surface_fill (void *surface,
2111 cairo_operator_t op,
2112 const cairo_pattern_t *source,
2113 const cairo_path_fixed_t *path,
2114 cairo_fill_rule_t fill_rule,
2116 cairo_antialias_t antialias,
2117 const cairo_clip_t *clip)
2119 return _cairo_compositor_fill (&_cairo_quartz_cg_compositor,
2120 surface, op, source, path,
2121 fill_rule, tolerance, antialias,
2125 static cairo_int_status_t
2126 _cairo_quartz_surface_stroke (void *surface,
2127 cairo_operator_t op,
2128 const cairo_pattern_t *source,
2129 const cairo_path_fixed_t *path,
2130 const cairo_stroke_style_t *style,
2131 const cairo_matrix_t *ctm,
2132 const cairo_matrix_t *ctm_inverse,
2134 cairo_antialias_t antialias,
2135 const cairo_clip_t *clip)
2137 return _cairo_compositor_stroke (&_cairo_quartz_cg_compositor,
2138 surface, op, source, path,
2139 style, ctm,ctm_inverse,
2140 tolerance, antialias, clip);
2143 static cairo_int_status_t
2144 _cairo_quartz_surface_glyphs (void *surface,
2145 cairo_operator_t op,
2146 const cairo_pattern_t *source,
2147 cairo_glyph_t *glyphs,
2149 cairo_scaled_font_t *scaled_font,
2150 const cairo_clip_t *clip)
2152 return _cairo_compositor_glyphs (&_cairo_quartz_cg_compositor,
2153 surface, op, source,
2154 glyphs, num_glyphs, scaled_font,
2158 static cairo_status_t
2159 _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clipper,
2160 cairo_path_fixed_t *path,
2161 cairo_fill_rule_t fill_rule,
2163 cairo_antialias_t antialias)
2165 cairo_quartz_surface_t *surface =
2166 cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
2168 ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
2170 if (IS_EMPTY (surface))
2171 return CAIRO_STATUS_SUCCESS;
2174 /* If we're being asked to reset the clip, we can only do it
2175 * by restoring the gstate to our previous saved one, and
2178 * Note that this assumes that ALL quartz surface creation
2179 * functions will do a SaveGState first; we do this in create_internal.
2181 CGContextRestoreGState (surface->cgContext);
2182 CGContextSaveGState (surface->cgContext);
2184 CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
2186 _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
2188 if (fill_rule == CAIRO_FILL_RULE_WINDING)
2189 CGContextClip (surface->cgContext);
2191 CGContextEOClip (surface->cgContext);
2194 ND ((stderr, "-- intersect_clip_path\n"));
2196 return CAIRO_STATUS_SUCCESS;
2199 // XXXtodo implement show_page; need to figure out how to handle begin/end
2201 static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
2202 CAIRO_SURFACE_TYPE_QUARTZ,
2203 _cairo_quartz_surface_finish,
2205 _cairo_default_context_create,
2207 _cairo_quartz_surface_create_similar,
2208 NULL, /* similar image */
2209 _cairo_quartz_surface_map_to_image,
2210 _cairo_quartz_surface_unmap_image,
2212 _cairo_surface_default_source,
2213 _cairo_quartz_surface_acquire_source_image,
2214 _cairo_quartz_surface_release_source_image,
2215 NULL, /* snapshot */
2217 NULL, /* copy_page */
2218 NULL, /* show_page */
2220 _cairo_quartz_surface_get_extents,
2221 NULL, /* get_font_options */
2224 NULL, /* mark_dirty_rectangle */
2226 _cairo_quartz_surface_paint,
2227 _cairo_quartz_surface_mask,
2228 _cairo_quartz_surface_stroke,
2229 _cairo_quartz_surface_fill,
2230 NULL, /* fill-stroke */
2231 _cairo_quartz_surface_glyphs,
2234 cairo_quartz_surface_t *
2235 _cairo_quartz_surface_create_internal (CGContextRef cgContext,
2236 cairo_content_t content,
2238 unsigned int height)
2240 cairo_quartz_surface_t *surface;
2242 quartz_ensure_symbols ();
2244 /* Init the base surface */
2245 surface = malloc (sizeof (cairo_quartz_surface_t));
2246 if (unlikely (surface == NULL))
2247 return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2249 memset (surface, 0, sizeof (cairo_quartz_surface_t));
2251 _cairo_surface_init (&surface->base,
2252 &cairo_quartz_surface_backend,
2256 _cairo_surface_clipper_init (&surface->clipper,
2257 _cairo_quartz_surface_clipper_intersect_clip_path);
2259 /* Save our extents */
2260 surface->extents.x = surface->extents.y = 0;
2261 surface->extents.width = width;
2262 surface->extents.height = height;
2263 surface->virtual_extents = surface->extents;
2265 if (IS_EMPTY (surface)) {
2266 surface->cgContext = NULL;
2267 surface->cgContextBaseCTM = CGAffineTransformIdentity;
2268 surface->imageData = NULL;
2269 surface->base.is_clear = TRUE;
2273 /* Save so we can always get back to a known-good CGContext -- this is
2274 * required for proper behaviour of intersect_clip_path(NULL)
2276 CGContextSaveGState (cgContext);
2278 surface->cgContext = cgContext;
2279 surface->cgContextBaseCTM = CGContextGetCTM (cgContext);
2281 surface->imageData = NULL;
2282 surface->imageSurfaceEquiv = NULL;
2288 * cairo_quartz_surface_create_for_cg_context:
2289 * @cgContext: the existing CGContext for which to create the surface
2290 * @width: width of the surface, in pixels
2291 * @height: height of the surface, in pixels
2293 * Creates a Quartz surface that wraps the given CGContext. The
2294 * CGContext is assumed to be in the standard Cairo coordinate space
2295 * (that is, with the origin at the upper left and the Y axis
2296 * increasing downward). If the CGContext is in the Quartz coordinate
2297 * space (with the origin at the bottom left), then it should be
2298 * flipped before this function is called. The flip can be accomplished
2299 * using a translate and a scale; for example:
2301 * <informalexample><programlisting>
2302 * CGContextTranslateCTM (cgContext, 0.0, height);
2303 * CGContextScaleCTM (cgContext, 1.0, -1.0);
2304 * </programlisting></informalexample>
2306 * All Cairo operations are implemented in terms of Quartz operations,
2307 * as long as Quartz-compatible elements are used (such as Quartz fonts).
2309 * Return value: the newly created Cairo surface.
2315 cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
2317 unsigned int height)
2319 cairo_quartz_surface_t *surf;
2321 surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
2323 if (likely (!surf->base.status))
2324 CGContextRetain (cgContext);
2330 * cairo_quartz_surface_create:
2331 * @format: format of pixels in the surface to create
2332 * @width: width of the surface, in pixels
2333 * @height: height of the surface, in pixels
2335 * Creates a Quartz surface backed by a CGBitmap. The surface is
2336 * created using the Device RGB (or Device Gray, for A8) color space.
2337 * All Cairo operations, including those that require software
2338 * rendering, will succeed on this surface.
2340 * Return value: the newly created surface.
2345 cairo_quartz_surface_create (cairo_format_t format,
2347 unsigned int height)
2349 cairo_quartz_surface_t *surf;
2351 CGColorSpaceRef cgColorspace;
2352 CGBitmapInfo bitinfo;
2355 int bitsPerComponent;
2357 if (!_cairo_quartz_verify_surface_size (width, height))
2358 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
2360 if (width == 0 || height == 0) {
2361 return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
2362 width, height)->base;
2365 if (format == CAIRO_FORMAT_ARGB32 ||
2366 format == CAIRO_FORMAT_RGB24)
2368 cgColorspace = CGColorSpaceCreateDeviceRGB ();
2369 bitinfo = kCGBitmapByteOrder32Host;
2370 if (format == CAIRO_FORMAT_ARGB32)
2371 bitinfo |= kCGImageAlphaPremultipliedFirst;
2373 bitinfo |= kCGImageAlphaNoneSkipFirst;
2374 bitsPerComponent = 8;
2376 } else if (format == CAIRO_FORMAT_A8) {
2377 cgColorspace = NULL;
2379 bitinfo = kCGImageAlphaOnly;
2380 bitsPerComponent = 8;
2381 } else if (format == CAIRO_FORMAT_A1) {
2382 /* I don't think we can usefully support this, as defined by
2383 * cairo_format_t -- these are 1-bit pixels stored in 32-bit
2386 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2388 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
2391 /* The Apple docs say that for best performance, the stride and the data
2392 * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
2393 * so we don't have to anything special on allocation.
2395 stride = (stride + 15) & ~15;
2397 imageData = _cairo_malloc_ab (height, stride);
2398 if (unlikely (!imageData)) {
2399 CGColorSpaceRelease (cgColorspace);
2400 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2403 /* zero the memory to match the image surface behaviour */
2404 memset (imageData, 0, height * stride);
2406 cgc = CGBitmapContextCreate (imageData,
2413 CGColorSpaceRelease (cgColorspace);
2417 return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
2420 /* flip the Y axis */
2421 CGContextTranslateCTM (cgc, 0.0, height);
2422 CGContextScaleCTM (cgc, 1.0, -1.0);
2424 surf = _cairo_quartz_surface_create_internal (cgc, _cairo_content_from_format (format),
2426 if (surf->base.status) {
2427 CGContextRelease (cgc);
2429 // create_internal will have set an error
2433 surf->base.is_clear = TRUE;
2435 surf->imageData = imageData;
2436 surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
2442 * cairo_quartz_surface_get_cg_context:
2443 * @surface: the Cairo Quartz surface
2445 * Returns the CGContextRef that the given Quartz surface is backed
2448 * A call to cairo_surface_flush() is required before using the
2449 * CGContextRef to ensure that all pending drawing operations are
2450 * finished and to restore any temporary modification cairo has made
2451 * to its state. A call to cairo_surface_mark_dirty() is required
2452 * after the state or the content of the CGContextRef has been
2455 * Return value: the CGContextRef for the given surface.
2460 cairo_quartz_surface_get_cg_context (cairo_surface_t *surface)
2462 if (surface && _cairo_surface_is_quartz (surface)) {
2463 cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *) surface;
2464 return quartz->cgContext;
2470 _cairo_surface_is_quartz (const cairo_surface_t *surface)
2472 return surface->backend == &cairo_quartz_surface_backend;
2481 void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
2483 Handle dataRef = NULL;
2485 CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
2487 GraphicsExportComponent grex = 0;
2488 unsigned long sizeWritten;
2490 ComponentResult result;
2492 // create the data reference
2493 result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
2494 0, &dataRef, &dataRefType);
2496 if (NULL != dataRef && noErr == result) {
2497 // get the PNG exporter
2498 result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
2502 // tell the exporter where to find its source image
2503 result = GraphicsExportSetInputCGImage (grex, inImageRef);
2505 if (noErr == result) {
2506 // tell the exporter where to save the exporter image
2507 result = GraphicsExportSetOutputDataReference (grex, dataRef,
2510 if (noErr == result) {
2511 // write the PNG file
2512 result = GraphicsExportDoExport (grex, &sizeWritten);
2516 // remember to close the component
2517 CloseComponent (grex);
2520 // remember to dispose of the data reference handle
2521 DisposeHandle (dataRef);
2526 quartz_image_to_png (CGImageRef imgref, char *dest)
2528 static int sctr = 0;
2529 char sptr[] = "/Users/vladimir/Desktop/barXXXXX.png";
2532 fprintf (stderr, "** Writing %p to bar%d\n", imgref, sctr);
2533 sprintf (sptr, "/Users/vladimir/Desktop/bar%d.png", sctr);
2538 ExportCGImageToPNGFile (imgref, dest);
2542 quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
2544 static int sctr = 0;
2545 char sptr[] = "/Users/vladimir/Desktop/fooXXXXX.png";
2547 if (nq->base.type != CAIRO_SURFACE_TYPE_QUARTZ) {
2548 fprintf (stderr, "** quartz_surface_to_png: surface %p isn't quartz!\n", nq);
2553 fprintf (stderr, "** Writing %p to foo%d\n", nq, sctr);
2554 sprintf (sptr, "/Users/vladimir/Desktop/foo%d.png", sctr);
2559 CGImageRef imgref = CGBitmapContextCreateImage (nq->cgContext);
2560 if (imgref == NULL) {
2561 fprintf (stderr, "quartz surface at %p is not a bitmap context!\n", nq);
2565 ExportCGImageToPNGFile (imgref, dest);
2567 CGImageRelease (imgref);
2570 #endif /* QUARTZ_DEBUG */