Xamarin Public Jenkins (auto-signing) 6bdd276d05 Imported Upstream version 5.0.0.42
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
2017-04-10 11:41:01 +00:00

2187 lines
78 KiB
Diff

--- a/src/cairo-quartz-font.c 2012-11-13 18:20:00.000000000 -0800
+++ b/src/cairo-quartz-font.c 2012-11-13 18:06:56.000000000 -0800
@@ -90,8 +90,9 @@ static int (*CGFontGetAscentPtr) (CGFont
static int (*CGFontGetDescentPtr) (CGFontRef fontRef) = NULL;
static int (*CGFontGetLeadingPtr) (CGFontRef fontRef) = NULL;
-/* Not public anymore in 64-bits nor in 10.7 */
-static ATSFontRef (*FMGetATSFontRefFromFontPtr) (FMFont iFont) = NULL;
+/* CTFontCreateWithGraphicsFont is not public until 10.5. */
+typedef const struct __CTFontDescriptor *CTFontDescriptorRef;
+static CTFontRef (*CTFontCreateWithGraphicsFontPtr) (CGFontRef, CGFloat, const CGAffineTransform *, CTFontDescriptorRef) = NULL;
static cairo_bool_t _cairo_quartz_font_symbol_lookup_done = FALSE;
static cairo_bool_t _cairo_quartz_font_symbols_present = FALSE;
@@ -130,7 +131,7 @@ quartz_font_ensure_symbols(void)
CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
- FMGetATSFontRefFromFontPtr = dlsym(RTLD_DEFAULT, "FMGetATSFontRefFromFont");
+ CTFontCreateWithGraphicsFontPtr = dlsym(RTLD_DEFAULT, "CTFontCreateWithGraphicsFont");
if ((CGFontCreateWithFontNamePtr || CGFontCreateWithNamePtr) &&
CGFontGetGlyphBBoxesPtr &&
@@ -155,6 +156,7 @@ struct _cairo_quartz_font_face {
cairo_font_face_t base;
CGFontRef cgFont;
+ CTFontRef ctFont;
};
/*
@@ -239,6 +241,10 @@ _cairo_quartz_font_face_destroy (void *a
{
cairo_quartz_font_face_t *font_face = (cairo_quartz_font_face_t*) abstract_face;
+ if (font_face->ctFont) {
+ CFRelease (font_face->ctFont);
+ }
+
CGFontRelease (font_face->cgFont);
}
@@ -363,6 +369,12 @@ cairo_quartz_font_face_create_for_cgfont
font_face->cgFont = CGFontRetain (font);
+ if (CTFontCreateWithGraphicsFontPtr) {
+ font_face->ctFont = CTFontCreateWithGraphicsFontPtr (font, 1.0, NULL, NULL);
+ } else {
+ font_face->ctFont = NULL;
+ }
+
_cairo_font_face_init (&font_face->base, &_cairo_quartz_font_face_backend);
return &font_face->base;
@@ -782,49 +794,10 @@ _cairo_quartz_scaled_font_get_cg_font_re
return ffont->cgFont;
}
-/*
- * compat with old ATSUI backend
- */
-
-/**
- * cairo_quartz_font_face_create_for_atsu_font_id
- * @font_id: an ATSUFontID for the font.
- *
- * Creates a new font for the Quartz font backend based on an
- * #ATSUFontID. This font can then be used with
- * cairo_set_font_face() or cairo_scaled_font_create().
- *
- * Return value: a newly created #cairo_font_face_t. Free with
- * cairo_font_face_destroy() when you are done using it.
- *
- * Since: 1.6
- **/
-cairo_font_face_t *
-cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id)
+CTFontRef
+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *abstract_font)
{
- quartz_font_ensure_symbols();
-
- if (FMGetATSFontRefFromFontPtr != NULL) {
- ATSFontRef atsFont = FMGetATSFontRefFromFontPtr (font_id);
- CGFontRef cgFont = CGFontCreateWithPlatformFont (&atsFont);
- cairo_font_face_t *ff;
-
- ff = cairo_quartz_font_face_create_for_cgfont (cgFont);
-
- CGFontRelease (cgFont);
-
- return ff;
- } else {
- _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
- return (cairo_font_face_t *)&_cairo_font_face_nil;
- }
-}
-
-/* This is the old name for the above function, exported for compat purposes */
-cairo_font_face_t *cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+ cairo_quartz_font_face_t *ffont = _cairo_quartz_scaled_to_face(abstract_font);
-cairo_font_face_t *
-cairo_atsui_font_face_create_for_atsu_font_id (ATSUFontID font_id)
-{
- return cairo_quartz_font_face_create_for_atsu_font_id (font_id);
+ return ffont->ctFont;
}
--- a/src/cairo-quartz-image-surface.c 2010-06-18 04:47:13.000000000 -0700
+++ b/src/cairo-quartz-image-surface.c 2012-11-13 18:06:56.000000000 -0800
@@ -148,6 +148,8 @@ _cairo_quartz_image_surface_flush (void
surface->image = newImage;
CGImageRelease (oldImage);
+ surface->base.is_clear = surface->imageSurface->base.is_clear;
+
return CAIRO_STATUS_SUCCESS;
}
@@ -270,6 +272,8 @@ cairo_quartz_image_surface_create (cairo
qisurf->image = image;
qisurf->imageSurface = image_surface;
+ qisurf->base.is_clear = image_surface->base.is_clear;
+
return &qisurf->base;
}
--- a/src/cairo-quartz-private.h 2010-12-25 06:21:34.000000000 -0800
+++ b/src/cairo-quartz-private.h 2012-11-13 18:06:56.000000000 -0800
@@ -50,6 +50,9 @@ typedef CGFloat cairo_quartz_float_t;
typedef float cairo_quartz_float_t;
#endif
+/* define CTFontRef for pre-10.5 SDKs */
+typedef const struct __CTFont *CTFontRef;
+
typedef struct cairo_quartz_surface {
cairo_surface_t base;
@@ -60,21 +63,22 @@ typedef struct cairo_quartz_surface {
cairo_surface_t *imageSurfaceEquiv;
cairo_surface_clipper_t clipper;
- cairo_rectangle_int_t extents;
- /* These are stored while drawing operations are in place, set up
- * by quartz_setup_source() and quartz_finish_source()
+ /**
+ * If non-null, this is a CGImage representing the contents of the surface.
+ * We clear this out before any painting into the surface, so that we
+ * don't force a copy to be created.
*/
- CGAffineTransform sourceTransform;
+ CGImageRef bitmapContextImage;
- CGImageRef sourceImage;
- cairo_surface_t *sourceImageSurface;
- CGRect sourceImageRect;
+ /**
+ * If non-null, this is the CGLayer for the surface.
+ */
+ CGLayerRef cgLayer;
- CGShadingRef sourceShading;
- CGPatternRef sourcePattern;
+ cairo_rectangle_int_t extents;
- CGInterpolationQuality oldInterpolationQuality;
+ cairo_bool_t ownsData;
} cairo_quartz_surface_t;
typedef struct cairo_quartz_image_surface {
@@ -103,6 +107,9 @@ _cairo_quartz_create_cgimage (cairo_form
CGFontRef
_cairo_quartz_scaled_font_get_cg_font_ref (cairo_scaled_font_t *sfont);
+CTFontRef
+_cairo_quartz_scaled_font_get_ct_font_ref (cairo_scaled_font_t *sfont);
+
#else
# error Cairo was not compiled with support for the quartz backend
--- a/src/cairo-quartz-surface.c 2012-11-13 18:20:00.000000000 -0800
+++ b/src/cairo-quartz-surface.c 2012-11-13 18:06:56.000000000 -0800
@@ -41,6 +41,8 @@
#include "cairo-error-private.h"
#include "cairo-surface-clipper-private.h"
+#include "cairo-gstate-private.h"
+#include "cairo-private.h"
#include <dlfcn.h>
@@ -77,6 +79,11 @@
* This macro can be used to conditionally compile backend-specific code.
*/
+/* Here are some of the differences between cairo and CoreGraphics
+ - cairo has only a single source active at once vs. CoreGraphics having
+ separate sources for stroke and fill
+*/
+
/* This method is private, but it exists. Its params are are exposed
* as args to the NS* method, but not as CG.
*/
@@ -126,6 +133,12 @@ static void (*CGContextSetShouldAntialia
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
+static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL;
+
+/* CTFontDrawGlyphs is not available until 10.7 */
+static void (*CTFontDrawGlyphsPtr) (CTFontRef, const CGGlyph[], const CGPoint[], size_t, CGContextRef) = NULL;
+
+static SInt32 _cairo_quartz_osx_version = 0x0;
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
@@ -160,6 +173,14 @@ static void quartz_ensure_symbols(void)
CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+ CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha");
+
+ CTFontDrawGlyphsPtr = dlsym(RTLD_DEFAULT, "CTFontDrawGlyphs");
+
+ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) {
+ // assume 10.5
+ _cairo_quartz_osx_version = 0x1050;
+ }
_cairo_quartz_symbol_lookup_done = TRUE;
}
@@ -430,6 +446,7 @@ _cairo_quartz_cairo_operator_to_quartz_c
case CAIRO_OPERATOR_HSL_LUMINOSITY:
default:
assert (0);
+ return kPrivateCGCompositeClear;
}
}
@@ -598,10 +615,13 @@ _cairo_quartz_cairo_matrix_to_quartz (co
typedef struct {
bool isClipping;
CGGlyph *cg_glyphs;
- CGSize *cg_advances;
+ union {
+ CGSize *cg_advances;
+ CGPoint *cg_positions;
+ } u;
size_t nglyphs;
CGAffineTransform textTransform;
- CGFontRef font;
+ cairo_scaled_font_t *scaled_font;
CGPoint origin;
} unbounded_show_glyphs_t;
@@ -679,12 +699,6 @@ _cairo_quartz_fixup_unbounded_operation
else
CGContextEOFillPath (cgc);
} else if (op->op == UNBOUNDED_SHOW_GLYPHS) {
- CGContextSetFont (cgc, op->u.show_glyphs.font);
- CGContextSetFontSize (cgc, 1.0);
- CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
- CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
- CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
-
if (op->u.show_glyphs.isClipping) {
/* Note that the comment in show_glyphs about kCGTextClip
* and the text transform still applies here; however, the
@@ -693,12 +707,25 @@ _cairo_quartz_fixup_unbounded_operation
CGContextSetTextDrawingMode (cgc, kCGTextClip);
CGContextSaveGState (cgc);
}
+ CGContextTranslateCTM (cgc, op->u.show_glyphs.origin.x, op->u.show_glyphs.origin.y);
+ CGContextConcatCTM (cgc, op->u.show_glyphs.textTransform);
+ if (CTFontDrawGlyphsPtr) {
+ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (op->u.show_glyphs.scaled_font),
+ op->u.show_glyphs.cg_glyphs,
+ op->u.show_glyphs.u.cg_positions,
+ op->u.show_glyphs.nglyphs,
+ cgc);
+ } else {
+ CGContextSetFont (cgc, _cairo_quartz_scaled_font_get_cg_font_ref (op->u.show_glyphs.scaled_font));
+ CGContextSetFontSize (cgc, 1.0);
+ CGContextSetTextMatrix (cgc, CGAffineTransformIdentity);
+
+ CGContextShowGlyphsWithAdvances (cgc,
+ op->u.show_glyphs.cg_glyphs,
+ op->u.show_glyphs.u.cg_advances,
+ op->u.show_glyphs.nglyphs);
- CGContextShowGlyphsWithAdvances (cgc,
- op->u.show_glyphs.cg_glyphs,
- op->u.show_glyphs.cg_advances,
- op->u.show_glyphs.nglyphs);
-
+ }
if (op->u.show_glyphs.isClipping) {
CGContextClearRect (cgc, clipBoxRound);
CGContextRestoreGState (cgc);
@@ -1102,12 +1129,12 @@ DataProviderReleaseCallback (void *info,
{
quartz_source_image_t *source_img = info;
_cairo_surface_release_source_image (source_img->surface, source_img->image_out, source_img->image_extra);
+ cairo_surface_destroy (source_img->surface);
free (source_img);
}
static cairo_status_t
-_cairo_surface_to_cgimage (cairo_surface_t *target,
- cairo_surface_t *source,
+_cairo_surface_to_cgimage (cairo_surface_t *source,
CGImageRef *image_out)
{
cairo_status_t status;
@@ -1127,9 +1154,14 @@ _cairo_surface_to_cgimage (cairo_surface
}
if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
- *image_out = CGBitmapContextCreateImage (surface->cgContext);
- if (*image_out)
- return CAIRO_STATUS_SUCCESS;
+ if (!surface->bitmapContextImage) {
+ surface->bitmapContextImage =
+ CGBitmapContextCreateImage (surface->cgContext);
+ }
+ if (surface->bitmapContextImage) {
+ *image_out = CGImageRetain (surface->bitmapContextImage);
+ return CAIRO_STATUS_SUCCESS;
+ }
}
}
@@ -1137,10 +1169,11 @@ _cairo_surface_to_cgimage (cairo_surface
if (source_img == NULL)
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- source_img->surface = source;
+ source_img->surface = cairo_surface_reference(source);
status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
if (status) {
+ cairo_surface_destroy (source_img->surface);
free (source_img);
return status;
}
@@ -1251,7 +1284,7 @@ _cairo_quartz_cairo_repeating_surface_pa
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
- status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
+ status = _cairo_surface_to_cgimage (pat_surf, &image);
if (status)
return status;
if (image == NULL)
@@ -1322,16 +1355,43 @@ typedef enum {
DO_SHADING,
DO_PATTERN,
DO_IMAGE,
+ DO_TILED_IMAGE,
+ DO_LAYER,
DO_UNSUPPORTED,
- DO_NOTHING,
- DO_TILED_IMAGE
+ DO_NOTHING
} cairo_quartz_action_t;
-static cairo_quartz_action_t
+/* State used during a drawing operation. */
+typedef struct {
+ CGContextRef context;
+ cairo_quartz_action_t action;
+
+ // Used with DO_SHADING, DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGAffineTransform transform;
+
+ // Used with DO_IMAGE and DO_TILED_IMAGE
+ CGImageRef image;
+ cairo_surface_t *imageSurface;
+
+ // Used with DO_IMAGE, DO_TILED_IMAGE and DO_LAYER
+ CGRect imageRect;
+
+ // Used with DO_LAYER
+ CGLayerRef layer;
+
+ // Used with DO_SHADING
+ CGShadingRef shading;
+
+ // Used with DO_PATTERN
+ CGPatternRef pattern;
+} cairo_quartz_drawing_state_t;
+
+static void
_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source)
+ const cairo_pattern_t *source,
+ cairo_quartz_drawing_state_t *state)
{
- CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
+ CGRect clipBox = CGContextGetClipBoundingBox (state->context);
double x0, y0, w, h;
cairo_surface_t *fallback;
@@ -1340,8 +1400,10 @@ _cairo_quartz_setup_fallback_source (cai
cairo_status_t status;
if (clipBox.size.width == 0.0f ||
- clipBox.size.height == 0.0f)
- return DO_NOTHING;
+ clipBox.size.height == 0.0f) {
+ state->action = DO_NOTHING;
+ return;
+ }
x0 = floor(clipBox.origin.x);
y0 = floor(clipBox.origin.y);
@@ -1384,18 +1446,21 @@ _cairo_quartz_setup_fallback_source (cai
}
#endif
- status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
- if (status)
- return DO_UNSUPPORTED;
- if (img == NULL)
- return DO_NOTHING;
-
- surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
- surface->sourceImage = img;
- surface->sourceImageSurface = fallback;
- surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
+ status = _cairo_surface_to_cgimage (fallback, &img);
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+ if (img == NULL) {
+ state->action = DO_NOTHING;
+ return;
+ }
- return DO_IMAGE;
+ state->imageRect = CGRectMake (0.0, 0.0, w, h);
+ state->image = img;
+ state->imageSurface = fallback;
+ state->transform = CGAffineTransformMakeTranslation (x0, y0);
+ state->action = DO_IMAGE;
}
/*
@@ -1411,10 +1476,11 @@ based on the extents of the object (the
we don't want the rasterization of the entire gradient to depend on the
clip region).
*/
-static cairo_quartz_action_t
+static void
_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
const cairo_linear_pattern_t *lpat,
- cairo_rectangle_int_t *extents)
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
{
const cairo_pattern_t *abspat = &lpat->base.base;
cairo_matrix_t mat;
@@ -1424,9 +1490,10 @@ _cairo_quartz_setup_linear_source (cairo
bool extend = abspat->extend == CAIRO_EXTEND_PAD;
if (lpat->base.n_stops == 0) {
- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
- return DO_SOLID;
+ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
+ state->action = DO_SOLID;
+ return;
}
if (lpat->p1.x == lpat->p2.x &&
@@ -1436,12 +1503,13 @@ _cairo_quartz_setup_linear_source (cairo
* Whatever the correct behaviour is, let's at least have only pixman's
* implementation to worry about.
*/
- return _cairo_quartz_setup_fallback_source (surface, abspat);
+ _cairo_quartz_setup_fallback_source (surface, abspat, state);
+ return;
}
mat = abspat->matrix;
cairo_matrix_invert (&mat);
- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
rgb = CGColorSpaceCreateDeviceRGB();
@@ -1461,21 +1529,22 @@ _cairo_quartz_setup_linear_source (cairo
extents);
}
- surface->sourceShading = CGShadingCreateAxial (rgb,
- start, end,
- gradFunc,
- extend, extend);
+ state->shading = CGShadingCreateAxial (rgb,
+ start, end,
+ gradFunc,
+ extend, extend);
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
- return DO_SHADING;
+ state->action = DO_SHADING;
}
-static cairo_quartz_action_t
+static void
_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
const cairo_radial_pattern_t *rpat,
- cairo_rectangle_int_t *extents)
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
{
const cairo_pattern_t *abspat = &rpat->base.base;
cairo_matrix_t mat;
@@ -1494,9 +1563,10 @@ _cairo_quartz_setup_radial_source (cairo
double centerDistance = sqrt (dx*dx + dy*dy);
if (rpat->base.n_stops == 0) {
- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
- return DO_SOLID;
+ CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.);
+ CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.);
+ state->action = DO_SOLID;
+ return;
}
if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
@@ -1507,12 +1577,13 @@ _cairo_quartz_setup_radial_source (cairo
* implementation to worry about.
* Note that this also catches the cases where r1 == r2.
*/
- return _cairo_quartz_setup_fallback_source (surface, abspat);
+ _cairo_quartz_setup_fallback_source (surface, abspat, state);
+ return;
}
mat = abspat->matrix;
cairo_matrix_invert (&mat);
- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
+ _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
rgb = CGColorSpaceCreateDeviceRGB();
@@ -1531,90 +1602,79 @@ _cairo_quartz_setup_radial_source (cairo
extents);
}
- surface->sourceShading = CGShadingCreateRadial (rgb,
- start,
- r1,
- end,
- r2,
- gradFunc,
- extend, extend);
+ state->shading = CGShadingCreateRadial (rgb,
+ start,
+ r1,
+ end,
+ r2,
+ gradFunc,
+ extend, extend);
CGColorSpaceRelease(rgb);
CGFunctionRelease(gradFunc);
- return DO_SHADING;
+ state->action = DO_SHADING;
}
-static cairo_quartz_action_t
-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source,
- cairo_rectangle_int_t *extents)
+static void
+_cairo_quartz_setup_surface_source (cairo_quartz_surface_t *surface,
+ const cairo_surface_pattern_t *spat,
+ cairo_rectangle_int_t *extents,
+ cairo_quartz_drawing_state_t *state)
{
- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
-
- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
+ const cairo_pattern_t *source = &spat->base;
+ CGContextRef context = state->context;
- if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
- cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
-
- CGContextSetRGBStrokeColor (surface->cgContext,
- solid->color.red,
- solid->color.green,
- solid->color.blue,
- solid->color.alpha);
- CGContextSetRGBFillColor (surface->cgContext,
- solid->color.red,
- solid->color.green,
- solid->color.blue,
- solid->color.alpha);
-
- return DO_SOLID;
- }
-
- if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
- const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
- return _cairo_quartz_setup_linear_source (surface, lpat, extents);
- }
-
- if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
- const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
- return _cairo_quartz_setup_radial_source (surface, rpat, extents);
- }
-
- if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
- (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT)))
+ if (source->extend == CAIRO_EXTEND_NONE || source->extend == CAIRO_EXTEND_PAD ||
+ (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))
{
- const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
cairo_surface_t *pat_surf = spat->surface;
CGImageRef img;
cairo_matrix_t m = spat->base.matrix;
cairo_rectangle_int_t extents;
- cairo_status_t status;
CGAffineTransform xform;
CGRect srcRect;
cairo_fixed_t fw, fh;
cairo_bool_t is_bounded;
+ cairo_bool_t repeat = source->extend == CAIRO_EXTEND_REPEAT;
+ cairo_status_t status;
- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
- if (status)
- return DO_UNSUPPORTED;
- if (img == NULL)
- return DO_NOTHING;
+ cairo_matrix_invert(&m);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+ /* Draw nonrepeating CGLayer surface using DO_LAYER */
+ if (!repeat && cairo_surface_get_type (pat_surf) == CAIRO_SURFACE_TYPE_QUARTZ) {
+ cairo_quartz_surface_t *quartz_surf = (cairo_quartz_surface_t *) pat_surf;
+ if (quartz_surf->cgLayer) {
+ state->imageRect = CGRectMake (0, 0, quartz_surf->extents.width, quartz_surf->extents.height);
+ state->layer = quartz_surf->cgLayer;
+ state->action = DO_LAYER;
+ return;
+ }
+ }
+
+ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
+ }
+ if (img == NULL) {
+ state->action = DO_NOTHING;
+ return;
+ }
- surface->sourceImage = img;
+ /* XXXroc what is this for? */
+ CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
- cairo_matrix_invert(&m);
- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
+ state->image = img;
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
- if (source->extend == CAIRO_EXTEND_NONE) {
- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
- return DO_IMAGE;
+ if (!repeat) {
+ state->imageRect = CGRectMake (0, 0, extents.width, extents.height);
+ state->action = DO_IMAGE;
+ return;
}
/* Quartz seems to tile images at pixel-aligned regions only -- this
@@ -1624,8 +1684,8 @@ _cairo_quartz_setup_source (cairo_quartz
* epsilon), and if not, fall back to the CGPattern type.
*/
- xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
- surface->sourceTransform);
+ xform = CGAffineTransformConcat (CGContextGetCTM (context),
+ state->transform);
srcRect = CGRectMake (0, 0, extents.width, extents.height);
srcRect = CGRectApplyAffineTransform (srcRect, xform);
@@ -1646,101 +1706,218 @@ _cairo_quartz_setup_source (cairo_quartz
srcRect = CGRectApplyAffineTransform (srcRect, xform);
- surface->sourceImageRect = srcRect;
-
- return DO_TILED_IMAGE;
+ state->imageRect = srcRect;
+ state->action = DO_TILED_IMAGE;
+ return;
}
/* Fall through to generic SURFACE case */
}
- if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
- cairo_quartz_float_t patternAlpha = 1.0f;
- CGColorSpaceRef patternSpace;
- CGPatternRef pattern;
- cairo_int_status_t status;
-
- status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
- if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
- return DO_NOTHING;
- if (status)
- return DO_UNSUPPORTED;
-
- // Save before we change the pattern, colorspace, etc. so that
- // we can restore and make sure that quartz releases our
- // pattern (which may be stack allocated)
- CGContextSaveGState(surface->cgContext);
-
- patternSpace = CGColorSpaceCreatePattern(NULL);
- CGContextSetFillColorSpace (surface->cgContext, patternSpace);
- CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha);
- CGContextSetStrokeColorSpace (surface->cgContext, patternSpace);
- CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha);
- CGColorSpaceRelease (patternSpace);
-
- /* Quartz likes to munge the pattern phase (as yet unexplained
- * why); force it to 0,0 as we've already baked in the correct
- * pattern translation into the pattern matrix
- */
- CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0));
-
- surface->sourcePattern = pattern;
+ CGFloat patternAlpha = 1.0f;
+ CGColorSpaceRef patternSpace;
+ CGPatternRef pattern;
+ cairo_int_status_t status;
- return DO_PATTERN;
+ status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ state->action = DO_NOTHING;
+ return;
+ }
+ if (status) {
+ state->action = DO_UNSUPPORTED;
+ return;
}
- return DO_UNSUPPORTED;
+ patternSpace = CGColorSpaceCreatePattern (NULL);
+ CGContextSetFillColorSpace (context, patternSpace);
+ CGContextSetFillPattern (context, pattern, &patternAlpha);
+ CGContextSetStrokeColorSpace (context, patternSpace);
+ CGContextSetStrokePattern (context, pattern, &patternAlpha);
+ CGColorSpaceRelease (patternSpace);
+
+ /* Quartz likes to munge the pattern phase (as yet unexplained
+ * why); force it to 0,0 as we've already baked in the correct
+ * pattern translation into the pattern matrix
+ */
+ CGContextSetPatternPhase (context, CGSizeMake(0,0));
+
+ state->pattern = pattern;
+ state->action = DO_PATTERN;
+ return;
}
+/**
+ * Call this before any operation that can modify the contents of a
+ * cairo_quartz_surface_t.
+ */
static void
-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source)
+_cairo_quartz_surface_will_change (cairo_quartz_surface_t *surface)
{
- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
+ if (surface->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+}
- if (surface->sourceImage) {
- CGImageRelease(surface->sourceImage);
- surface->sourceImage = NULL;
+/**
+ * Sets up internal state to be used to draw the source mask, stored in
+ * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on
+ * surface->cgContext.
+ */
+static cairo_quartz_drawing_state_t
+_cairo_quartz_setup_state (cairo_quartz_surface_t *surface,
+ const cairo_pattern_t *source,
+ cairo_operator_t op,
+ cairo_rectangle_int_t *extents)
+{
+ CGContextRef context = surface->cgContext;
+ cairo_quartz_drawing_state_t state;
+ cairo_status_t status;
- cairo_surface_destroy(surface->sourceImageSurface);
- surface->sourceImageSurface = NULL;
+ state.context = context;
+ state.image = NULL;
+ state.imageSurface = NULL;
+ state.layer = NULL;
+ state.shading = NULL;
+ state.pattern = NULL;
+
+ _cairo_quartz_surface_will_change (surface);
+
+ // Save before we change the pattern, colorspace, etc. so that
+ // we can restore and make sure that quartz releases our
+ // pattern (which may be stack allocated)
+ CGContextSaveGState(context);
+
+ CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter));
+
+ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ state.action = DO_NOTHING;
+ return state;
+ }
+ if (status) {
+ state.action = DO_UNSUPPORTED;
+ return state;
}
- if (surface->sourceShading) {
- CGShadingRelease(surface->sourceShading);
- surface->sourceShading = NULL;
+ if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
+
+ CGContextSetRGBStrokeColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+ CGContextSetRGBFillColor (context,
+ solid->color.red,
+ solid->color.green,
+ solid->color.blue,
+ solid->color.alpha);
+
+ state.action = DO_SOLID;
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
+ const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
+ _cairo_quartz_setup_linear_source (surface, lpat, extents, &state);
+ return state;
+ }
+
+ if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
+ const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
+ _cairo_quartz_setup_radial_source (surface, rpat, extents, &state);
+ return state;
}
- if (surface->sourcePattern) {
- CGPatternRelease(surface->sourcePattern);
- // To tear down the pattern and colorspace
- CGContextRestoreGState(surface->cgContext);
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
+ if (op == CAIRO_OPERATOR_OVER && _cairo_pattern_is_opaque (source, NULL) &&
+ CGContextGetAlphaPtr &&
+ CGContextGetAlphaPtr (surface->cgContext) == 1.0) {
+ // Quartz won't touch pixels outside the bounds of the
+ // source surface, so we can just go ahead and use Copy here
+ // to accelerate things.
+ // Quartz won't necessarily be able to do this optimization internally;
+ // for CGLayer surfaces, we can know all the pixels are opaque
+ // (because it's CONTENT_COLOR), but Quartz won't know.
+ CGContextSetCompositeOperation (context, kPrivateCGCompositeCopy);
+ }
- surface->sourcePattern = NULL;
+ const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source;
+ _cairo_quartz_setup_surface_source (surface, spat, extents, &state);
+ return state;
}
-}
+ state.action = DO_UNSUPPORTED;
+ return state;
+}
+/**
+ * 1) Tears down internal state used to draw the source
+ * 2) Does CGContextRestoreGState(state->context)
+ */
static void
-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
+_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state)
{
- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE));
+ if (state->image) {
+ CGImageRelease(state->image);
+ }
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height);
- CGContextScaleCTM (surface->cgContext, 1, -1);
-
- if (action == DO_IMAGE) {
- CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
- if (!_cairo_operator_bounded_by_source(op)) {
- CGContextBeginPath (surface->cgContext);
- CGContextAddRect (surface->cgContext, surface->sourceImageRect);
- CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0);
- CGContextEOFillPath (surface->cgContext);
+ if (state->imageSurface) {
+ cairo_surface_destroy(state->imageSurface);
+ }
+
+ if (state->shading) {
+ CGShadingRelease(state->shading);
+ }
+
+ if (state->pattern) {
+ CGPatternRelease(state->pattern);
+ }
+
+ CGContextRestoreGState(state->context);
+}
+
+
+static void
+_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op)
+{
+ assert (state &&
+ ((state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)) ||
+ (state->layer && state->action == DO_LAYER)));
+
+ CGContextConcatCTM (state->context, state->transform);
+ CGContextTranslateCTM (state->context, 0, state->imageRect.size.height);
+ CGContextScaleCTM (state->context, 1, -1);
+
+ if (state->action == DO_TILED_IMAGE) {
+ CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image);
+ /* no need to worry about unbounded operators, since tiled images
+ fill the entire clip region */
+ } else {
+ if (state->action == DO_LAYER) {
+ /* Note that according to Apple docs it's completely legal
+ * to draw a CGLayer to any CGContext, even one it wasn't
+ * created for.
+ */
+ CGContextSetInterpolationQuality (state->context, kCGInterpolationNone);
+ CGContextDrawLayerAtPoint (state->context, state->imageRect.origin,
+ state->layer);
+ } else {
+ CGContextDrawImage (state->context, state->imageRect, state->image);
+ }
+
+ /* disable this EXTEND_NONE correctness code because we use this path
+ * for both EXTEND_NONE and EXTEND_PAD */
+ if (0 && !_cairo_operator_bounded_by_source (op)) {
+ CGContextBeginPath (state->context);
+ CGContextAddRect (state->context, state->imageRect);
+ CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context));
+ CGContextSetRGBFillColor (state->context, 0, 0, 0, 0);
+ CGContextEOFillPath (state->context);
}
- } else
- CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+ }
}
@@ -1762,6 +1939,7 @@ _cairo_quartz_get_image (cairo_quartz_su
}
if (surface->imageSurfaceEquiv) {
+ CGContextFlush(surface->cgContext);
*image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
return CAIRO_STATUS_SUCCESS;
}
@@ -1773,6 +1951,7 @@ _cairo_quartz_get_image (cairo_quartz_su
CGColorSpaceRef colorspace;
unsigned int color_comps;
+ CGContextFlush(surface->cgContext);
imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
#ifdef USE_10_3_WORKAROUNDS
@@ -1860,53 +2039,79 @@ _cairo_quartz_surface_finish (void *abst
surface->cgContext = NULL;
+ if (surface->bitmapContextImage) {
+ CGImageRelease (surface->bitmapContextImage);
+ surface->bitmapContextImage = NULL;
+ }
+
if (surface->imageSurfaceEquiv) {
+ if (surface->ownsData)
+ _cairo_image_surface_assume_ownership_of_data (surface->imageSurfaceEquiv);
cairo_surface_destroy (surface->imageSurfaceEquiv);
surface->imageSurfaceEquiv = NULL;
+ } else if (surface->imageData && surface->ownsData) {
+ free (surface->imageData);
}
- if (surface->imageData) {
- free (surface->imageData);
- surface->imageData = NULL;
+ surface->imageData = NULL;
+
+ if (surface->cgLayer) {
+ CGLayerRelease (surface->cgLayer);
}
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
-_cairo_quartz_surface_acquire_source_image (void *abstract_surface,
- cairo_image_surface_t **image_out,
- void **image_extra)
+_cairo_quartz_surface_acquire_image (void *abstract_surface,
+ cairo_image_surface_t **image_out,
+ void **image_extra)
{
cairo_int_status_t status;
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- //ND((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
-
- status = _cairo_quartz_get_image (surface, image_out);
- if (status)
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
*image_extra = NULL;
- return CAIRO_STATUS_SUCCESS;
-}
+ /* ND((stderr, "%p _cairo_quartz_surface_acquire_image\n", surface)); */
-static cairo_surface_t *
-_cairo_quartz_surface_snapshot (void *abstract_surface)
-{
- cairo_int_status_t status;
- cairo_quartz_surface_t *surface = abstract_surface;
- cairo_image_surface_t *image;
+ status = _cairo_quartz_get_image (surface, image_out);
- if (surface->imageSurfaceEquiv)
- return NULL;
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED && surface->cgLayer) {
+ /* copy the layer into a Quartz bitmap context so we can get the data */
+ cairo_surface_t *tmp =
+ cairo_quartz_surface_create (CAIRO_FORMAT_ARGB32,
+ surface->extents.width,
+ surface->extents.height);
+ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) tmp;
+
+ /* if surface creation failed, we won't have a Quartz surface here */
+ if (cairo_surface_get_type (tmp) == CAIRO_SURFACE_TYPE_QUARTZ &&
+ tmp_surface->imageSurfaceEquiv) {
+ CGContextSaveGState (tmp_surface->cgContext);
+ CGContextTranslateCTM (tmp_surface->cgContext, 0, surface->extents.height);
+ CGContextScaleCTM (tmp_surface->cgContext, 1, -1);
+ /* Note that according to Apple docs it's completely legal
+ * to draw a CGLayer to any CGContext, even one it wasn't
+ * created for.
+ */
+ CGContextDrawLayerAtPoint (tmp_surface->cgContext,
+ CGPointMake (0.0, 0.0),
+ surface->cgLayer);
+ CGContextRestoreGState (tmp_surface->cgContext);
+
+ *image_out = (cairo_image_surface_t*)
+ cairo_surface_reference(tmp_surface->imageSurfaceEquiv);
+ *image_extra = tmp;
+ status = CAIRO_STATUS_SUCCESS;
+ } else {
+ cairo_surface_destroy (tmp);
+ }
+ }
- status = _cairo_quartz_get_image (surface, &image);
- if (unlikely (status))
- return _cairo_surface_create_in_error (CAIRO_STATUS_NO_MEMORY);
+ if (status)
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- return &image->base;
+ return CAIRO_STATUS_SUCCESS;
}
static void
@@ -1915,6 +2120,10 @@ _cairo_quartz_surface_release_source_ima
void *image_extra)
{
cairo_surface_destroy ((cairo_surface_t *) image);
+
+ if (image_extra) {
+ cairo_surface_destroy ((cairo_surface_t *) image_extra);
+ }
}
@@ -1926,18 +2135,16 @@ _cairo_quartz_surface_acquire_dest_image
void **image_extra)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- cairo_int_status_t status;
ND((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
- status = _cairo_quartz_get_image (surface, image_out);
- if (status)
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
*image_rect = surface->extents;
*image_extra = NULL;
- return CAIRO_STATUS_SUCCESS;
+ _cairo_quartz_surface_will_change (surface);
+
+ return _cairo_quartz_surface_acquire_image (abstract_surface,
+ image_out, image_extra);
}
static void
@@ -1947,11 +2154,31 @@ _cairo_quartz_surface_release_dest_image
cairo_rectangle_int_t *image_rect,
void *image_extra)
{
- //cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
-
- //ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface));
+ /* ND((stderr, "%p _cairo_quartz_surface_release_dest_image\n", surface)); */
cairo_surface_destroy ((cairo_surface_t *) image);
+
+ if (image_extra) {
+ /* we need to write the data from the temp surface back to the layer */
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_surface_t *tmp_surface = (cairo_quartz_surface_t *) image_extra;
+ CGImageRef img;
+ cairo_status_t status = _cairo_surface_to_cgimage (&tmp_surface->base, &img);
+ if (status) {
+ cairo_surface_destroy (&tmp_surface->base);
+ return;
+ }
+
+ CGContextSaveGState (surface->cgContext);
+ CGContextTranslateCTM (surface->cgContext, 0, surface->extents.height);
+ CGContextScaleCTM (surface->cgContext, 1, -1);
+ CGContextDrawImage (surface->cgContext,
+ CGRectMake (0.0, 0.0, surface->extents.width, surface->extents.height),
+ img);
+ CGContextRestoreGState (surface->cgContext);
+
+ cairo_surface_destroy (&tmp_surface->base);
+ }
}
static cairo_surface_t *
@@ -1960,10 +2187,13 @@ _cairo_quartz_surface_create_similar (vo
int width,
int height)
{
- /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
-
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_format_t format;
+ if (surface->cgLayer)
+ return cairo_quartz_surface_create_cg_layer (abstract_surface, content,
+ width, height);
+
if (content == CAIRO_CONTENT_COLOR_ALPHA)
format = CAIRO_FORMAT_ARGB32;
else if (content == CAIRO_CONTENT_COLOR)
@@ -2027,7 +2257,7 @@ _cairo_quartz_surface_clone_similar (voi
}
}
- status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
+ status = _cairo_surface_to_cgimage (src, &quartz_image);
if (status)
return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -2087,7 +2317,7 @@ _cairo_quartz_surface_paint_cg (void *ab
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
+ cairo_quartz_drawing_state_t state;
ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
@@ -2098,31 +2328,24 @@ _cairo_quartz_surface_paint_cg (void *ab
if (unlikely (rv))
return rv;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
- if (unlikely (rv))
- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
- action = _cairo_quartz_setup_source (surface, source, NULL);
-
- if (action == DO_SOLID || action == DO_PATTERN) {
- CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x,
- surface->extents.y,
- surface->extents.width,
- surface->extents.height));
- } else if (action == DO_SHADING) {
- CGContextSaveGState (surface->cgContext);
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
- CGContextRestoreGState (surface->cgContext);
- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
- CGContextSaveGState (surface->cgContext);
- _cairo_quartz_draw_image (surface, op, action);
- CGContextRestoreGState (surface->cgContext);
- } else if (action != DO_NOTHING) {
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextFillRect (state.context, CGRectMake(surface->extents.x,
+ surface->extents.y,
+ surface->extents.width,
+ surface->extents.height));
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
}
- _cairo_quartz_teardown_source (surface, source);
+ _cairo_quartz_teardown_state (&state);
ND((stderr, "-- paint\n"));
return rv;
@@ -2186,7 +2409,7 @@ _cairo_quartz_surface_fill_cg (void *abs
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
+ cairo_quartz_drawing_state_t state;
CGPathRef path_for_unbounded = NULL;
ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
@@ -2198,14 +2421,6 @@ _cairo_quartz_surface_fill_cg (void *abs
if (unlikely (rv))
return rv;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
- if (unlikely (rv))
- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
-
- CGContextSaveGState (surface->cgContext);
-
- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
-
if (_cairo_quartz_source_needs_extents (source))
{
/* We don't need precise extents since these are only used to
@@ -2213,46 +2428,47 @@ _cairo_quartz_surface_fill_cg (void *abs
object. */
cairo_rectangle_int_t path_extents;
_cairo_path_fixed_approximate_fill_extents (path, &path_extents);
- action = _cairo_quartz_setup_source (surface, source, &path_extents);
+ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
} else {
- action = _cairo_quartz_setup_source (surface, source, NULL);
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
}
- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
+ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
+
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+ path_for_unbounded = CGContextCopyPathPtr (state.context);
- if (action == DO_SOLID || action == DO_PATTERN) {
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextFillPath (surface->cgContext);
+ CGContextFillPath (state.context);
else
- CGContextEOFillPath (surface->cgContext);
- } else if (action == DO_SHADING) {
+ CGContextEOFillPath (state.context);
+ } else if (state.action == DO_SHADING) {
// we have to clip and then paint the shading; we can't fill
// with the shading
if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextClip (surface->cgContext);
+ CGContextClip (state.context);
else
- CGContextEOClip (surface->cgContext);
+ CGContextEOClip (state.context);
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextClip (surface->cgContext);
+ CGContextClip (state.context);
else
- CGContextEOClip (surface->cgContext);
+ CGContextEOClip (state.context);
- _cairo_quartz_draw_image (surface, op, action);
- } else if (action != DO_NOTHING) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action != DO_NOTHING) {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
}
- _cairo_quartz_teardown_source (surface, source);
-
- CGContextRestoreGState (surface->cgContext);
+ _cairo_quartz_teardown_state (&state);
if (path_for_unbounded) {
unbounded_op_data_t ub;
@@ -2319,7 +2535,7 @@ _cairo_quartz_surface_stroke_cg (void *a
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
+ cairo_quartz_drawing_state_t state;
CGAffineTransform origCTM, strokeTransform;
CGPathRef path_for_unbounded = NULL;
@@ -2336,16 +2552,25 @@ _cairo_quartz_surface_stroke_cg (void *a
if (unlikely (rv))
return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+ if (_cairo_quartz_source_needs_extents (source))
+ {
+ cairo_rectangle_int_t path_extents;
+ _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
+ state = _cairo_quartz_setup_state (surface, source, op, &path_extents);
+ } else {
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
+ }
+
// Turning antialiasing off used to cause misrendering with
// single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels).
// That's been since fixed in at least 10.5, and in the latest 10.4 dot releases.
- CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE));
- CGContextSetLineWidth (surface->cgContext, style->line_width);
- CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
- CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
- CGContextSetMiterLimit (surface->cgContext, style->miter_limit);
+ CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE));
+ CGContextSetLineWidth (state.context, style->line_width);
+ CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+ CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+ CGContextSetMiterLimit (state.context, style->miter_limit);
- origCTM = CGContextGetCTM (surface->cgContext);
+ origCTM = CGContextGetCTM (state.context);
if (style->dash && style->num_dashes) {
#define STATIC_DASH 32
@@ -2368,72 +2593,62 @@ _cairo_quartz_surface_stroke_cg (void *a
if (fdash != sdash)
free (fdash);
} else
- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
+ CGContextSetLineDash (state.context, 0, NULL, 0);
- CGContextSaveGState (surface->cgContext);
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.context);
- if (_cairo_quartz_source_needs_extents (source))
- {
- cairo_rectangle_int_t path_extents;
- _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents);
- action = _cairo_quartz_setup_source (surface, source, &path_extents);
- } else {
- action = _cairo_quartz_setup_source (surface, source, NULL);
- }
-
- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
+ _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
+ CGContextConcatCTM (state.context, strokeTransform);
if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
-
- _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
- CGContextConcatCTM (surface->cgContext, strokeTransform);
+ path_for_unbounded = CGContextCopyPathPtr (state.context);
- if (action == DO_SOLID || action == DO_PATTERN) {
- CGContextStrokePath (surface->cgContext);
- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
- CGContextReplacePathWithStrokedPath (surface->cgContext);
- CGContextClip (surface->cgContext);
-
- CGContextSetCTM (surface->cgContext, origCTM);
- _cairo_quartz_draw_image (surface, op, action);
- } else if (action == DO_SHADING) {
- CGContextReplacePathWithStrokedPath (surface->cgContext);
- CGContextClip (surface->cgContext);
-
- CGContextSetCTM (surface->cgContext, origCTM);
-
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
- } else if (action != DO_NOTHING) {
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextStrokePath (state.context);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+
+ CGContextSetCTM (state.context, origCTM);
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextClip (state.context);
+
+ CGContextSetCTM (state.context, origCTM);
+
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
+ } else if (state.action != DO_NOTHING) {
rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ goto BAIL;
}
- _cairo_quartz_teardown_source (surface, source);
-
- CGContextRestoreGState (surface->cgContext);
-
if (path_for_unbounded) {
unbounded_op_data_t ub;
ub.op = UNBOUNDED_STROKE_FILL;
ub.u.stroke_fill.fill_rule = CAIRO_FILL_RULE_WINDING;
- CGContextBeginPath (surface->cgContext);
- CGContextAddPath (surface->cgContext, path_for_unbounded);
+ CGContextBeginPath (state.context);
+ CGContextAddPath (state.context, path_for_unbounded);
CGPathRelease (path_for_unbounded);
- CGContextSaveGState (surface->cgContext);
- CGContextConcatCTM (surface->cgContext, strokeTransform);
- CGContextReplacePathWithStrokedPath (surface->cgContext);
- CGContextRestoreGState (surface->cgContext);
+ CGContextSaveGState (state.context);
+ CGContextConcatCTM (state.context, strokeTransform);
+ CGContextReplacePathWithStrokedPath (state.context);
+ CGContextRestoreGState (state.context);
- ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
+ ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (state.context);
_cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
CGPathRelease (ub.u.stroke_fill.cgPath);
}
+ BAIL:
+ _cairo_quartz_teardown_state (&state);
+
ND((stderr, "-- stroke\n"));
return rv;
}
@@ -2490,18 +2705,22 @@ _cairo_quartz_surface_show_glyphs_cg (vo
CGGlyph glyphs_static[STATIC_BUF_SIZE];
CGSize cg_advances_static[STATIC_BUF_SIZE];
CGGlyph *cg_glyphs = &glyphs_static[0];
+ /* We'll use the cg_advances array for either advances or positions,
+ depending which API we're using to actually draw. The types involved
+ have the same size, so this is safe. */
CGSize *cg_advances = &cg_advances_static[0];
cairo_rectangle_int_t glyph_extents;
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
+ cairo_quartz_drawing_state_t state;
cairo_quartz_float_t xprev, yprev;
int i;
CGFontRef cgfref = NULL;
cairo_bool_t isClipping = FALSE;
cairo_bool_t didForceFontSmoothing = FALSE;
+ cairo_antialias_t effective_antialiasing;
if (IS_EMPTY(surface))
return CAIRO_STATUS_SUCCESS;
@@ -2516,54 +2735,51 @@ _cairo_quartz_surface_show_glyphs_cg (vo
if (unlikely (rv))
return rv;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
- if (unlikely (rv))
- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
-
- CGContextSaveGState (surface->cgContext);
-
if (_cairo_quartz_source_needs_extents (source) &&
!_cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs,
&glyph_extents, NULL))
{
- action = _cairo_quartz_setup_source (surface, source, &glyph_extents);
+ state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents);
} else {
- action = _cairo_quartz_setup_source (surface, source, NULL);
+ state = _cairo_quartz_setup_state (surface, source, op, NULL);
}
- if (action == DO_SOLID || action == DO_PATTERN) {
- CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill);
- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) {
- CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip);
+ if (state.action == DO_SOLID || state.action == DO_PATTERN) {
+ CGContextSetTextDrawingMode (state.context, kCGTextFill);
+ } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_SHADING || state.action == DO_LAYER) {
+ CGContextSetTextDrawingMode (state.context, kCGTextClip);
isClipping = TRUE;
} else {
- if (action != DO_NOTHING)
+ if (state.action != DO_NOTHING)
rv = CAIRO_INT_STATUS_UNSUPPORTED;
goto BAIL;
}
/* this doesn't addref */
cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font);
- CGContextSetFont (surface->cgContext, cgfref);
- CGContextSetFontSize (surface->cgContext, 1.0);
+ CGContextSetFont (state.context, cgfref);
+ CGContextSetFontSize (state.context, 1.0);
+
+ effective_antialiasing = scaled_font->options.antialias;
switch (scaled_font->options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
- CGContextSetShouldAntialias (surface->cgContext, TRUE);
- CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
+ CGContextSetShouldAntialias (state.context, TRUE);
+ CGContextSetShouldSmoothFonts (state.context, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
- !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
+ !CGContextGetAllowsFontSmoothingPtr (state.context))
{
didForceFontSmoothing = TRUE;
- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
+ CGContextSetAllowsFontSmoothingPtr (state.context, TRUE);
}
break;
case CAIRO_ANTIALIAS_NONE:
- CGContextSetShouldAntialias (surface->cgContext, FALSE);
+ CGContextSetShouldAntialias (state.context, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
- CGContextSetShouldAntialias (surface->cgContext, TRUE);
- CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
+ CGContextSetShouldAntialias (state.context, TRUE);
+ CGContextSetShouldSmoothFonts (state.context, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
/* Don't do anything */
@@ -2584,57 +2800,84 @@ _cairo_quartz_surface_show_glyphs_cg (vo
}
}
+ /* scale(1,-1) * scaled_font->scale */
textTransform = CGAffineTransformMake (scaled_font->scale.xx,
scaled_font->scale.yx,
-scaled_font->scale.xy,
-scaled_font->scale.yy,
0, 0);
- _cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform);
- CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
+ /* scaled_font->scale_inverse * scale(1,-1) */
+ invTextTransform = CGAffineTransformMake (scaled_font->scale_inverse.xx,
+ -scaled_font->scale_inverse.yx,
+ scaled_font->scale_inverse.xy,
+ -scaled_font->scale_inverse.yy,
+ 0.0, 0.0);
- /* Convert our glyph positions to glyph advances. We need n-1 advances,
- * since the advance at index 0 is applied after glyph 0. */
- xprev = glyphs[0].x;
- yprev = glyphs[0].y;
-
- cg_glyphs[0] = glyphs[0].index;
-
- for (i = 1; i < num_glyphs; i++) {
- cairo_quartz_float_t xf = glyphs[i].x;
- cairo_quartz_float_t yf = glyphs[i].y;
- cg_glyphs[i] = glyphs[i].index;
- cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
- xprev = xf;
- yprev = yf;
- }
+ CGContextSetTextMatrix (state.context, CGAffineTransformIdentity);
/* Translate to the first glyph's position before drawing */
- ctm = CGContextGetCTM (surface->cgContext);
- CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y);
- CGContextConcatCTM (surface->cgContext, textTransform);
-
- CGContextShowGlyphsWithAdvances (surface->cgContext,
- cg_glyphs,
- cg_advances,
- num_glyphs);
-
- CGContextSetCTM (surface->cgContext, ctm);
+ ctm = CGContextGetCTM (state.context);
+ CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.context, textTransform);
+
+ if (CTFontDrawGlyphsPtr) {
+ /* If CTFontDrawGlyphs is available (i.e. OS X 10.7 or later), we want to use
+ * that in preference to CGContextShowGlyphsWithAdvances so that colored-bitmap
+ * fonts like Apple Color Emoji will render properly.
+ * For this, we need to convert our glyph positions to Core Graphics's CGPoint.
+ * We borrow the cg_advances array, as CGPoint and CGSize are the same size. */
+
+ CGPoint *cg_positions = (CGPoint*) cg_advances;
+ cairo_quartz_float_t origin_x = glyphs[0].x;
+ cairo_quartz_float_t origin_y = glyphs[0].y;
+
+ for (i = 0; i < num_glyphs; i++) {
+ CGPoint pt = CGPointMake (glyphs[i].x - origin_x, glyphs[i].y - origin_y);
+ cg_positions[i] = CGPointApplyAffineTransform (pt, invTextTransform);
+ cg_glyphs[i] = glyphs[i].index;
+ }
- if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
- _cairo_quartz_draw_image (surface, op, action);
- } else if (action == DO_SHADING) {
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
+ CTFontDrawGlyphsPtr (_cairo_quartz_scaled_font_get_ct_font_ref (scaled_font),
+ cg_glyphs, cg_positions, num_glyphs, state.context);
+ } else {
+ /* Convert our glyph positions to glyph advances. We need n-1 advances,
+ * since the advance at index 0 is applied after glyph 0. */
+ xprev = glyphs[0].x;
+ yprev = glyphs[0].y;
+
+ cg_glyphs[0] = glyphs[0].index;
+
+ for (i = 1; i < num_glyphs; i++) {
+ cairo_quartz_float_t xf = glyphs[i].x;
+ cairo_quartz_float_t yf = glyphs[i].y;
+ cg_glyphs[i] = glyphs[i].index;
+ cg_advances[i - 1] = CGSizeApplyAffineTransform(CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
+ xprev = xf;
+ yprev = yf;
+ }
+
+ CGContextShowGlyphsWithAdvances (state.context,
+ cg_glyphs,
+ cg_advances,
+ num_glyphs);
+ }
+
+ CGContextSetCTM (state.context, ctm);
+
+ if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE ||
+ state.action == DO_LAYER) {
+ _cairo_quartz_draw_image (&state, op);
+ } else if (state.action == DO_SHADING) {
+ CGContextConcatCTM (state.context, state.transform);
+ CGContextDrawShading (state.context, state.shading);
}
BAIL:
- _cairo_quartz_teardown_source (surface, source);
-
if (didForceFontSmoothing)
- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
+ CGContextSetAllowsFontSmoothingPtr (state.context, FALSE);
- CGContextRestoreGState (surface->cgContext);
+ _cairo_quartz_teardown_state (&state);
if (rv == CAIRO_STATUS_SUCCESS &&
cgfref &&
@@ -2645,10 +2888,17 @@ BAIL:
ub.u.show_glyphs.isClipping = isClipping;
ub.u.show_glyphs.cg_glyphs = cg_glyphs;
- ub.u.show_glyphs.cg_advances = cg_advances;
+ if (CTFontDrawGlyphsPtr) {
+ /* we're using Core Text API: the cg_advances array was
+ reused (above) for glyph positions */
+ CGPoint *cg_positions = (CGPoint*) cg_advances;
+ ub.u.show_glyphs.u.cg_positions = cg_positions;
+ } else {
+ ub.u.show_glyphs.u.cg_advances = cg_advances;
+ }
ub.u.show_glyphs.nglyphs = num_glyphs;
ub.u.show_glyphs.textTransform = textTransform;
- ub.u.show_glyphs.font = cgfref;
+ ub.u.show_glyphs.scaled_font = scaled_font;
ub.u.show_glyphs.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
_cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
@@ -2717,7 +2967,7 @@ _cairo_quartz_surface_mask_with_surface
cairo_status_t status = CAIRO_STATUS_SUCCESS;
CGAffineTransform ctm, mask_matrix;
- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
+ status = _cairo_surface_to_cgimage (pat_surf, &img);
if (status)
return status;
if (img == NULL) {
@@ -2820,7 +3070,9 @@ _cairo_quartz_surface_mask_cg (void *abs
if (unlikely (rv))
return rv;
- if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
+ /* Using CGContextSetAlpha to implement mask alpha doesn't work for all operators. */
+ if (mask->type == CAIRO_PATTERN_TYPE_SOLID &&
+ op == CAIRO_OPERATOR_OVER) {
/* This is easy; we just need to paint with the alpha. */
cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
@@ -2834,8 +3086,11 @@ _cairo_quartz_surface_mask_cg (void *abs
/* If we have CGContextClipToMask, we can do more complex masks */
if (CGContextClipToMaskPtr) {
/* For these, we can skip creating a temporary surface, since we already have one */
- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE)
+ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */
+ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE &&
+ mask->extend == CAIRO_EXTEND_NONE) {
return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
+ }
return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
}
@@ -2920,13 +3175,24 @@ _cairo_quartz_surface_clipper_intersect_
return CAIRO_STATUS_SUCCESS;
}
+static cairo_status_t
+_cairo_quartz_surface_mark_dirty_rectangle (void *abstract_surface,
+ int x, int y,
+ int width, int height)
+{
+ cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ _cairo_quartz_surface_will_change (surface);
+ return CAIRO_STATUS_SUCCESS;
+}
+
+
// XXXtodo implement show_page; need to figure out how to handle begin/end
static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ,
_cairo_quartz_surface_create_similar,
_cairo_quartz_surface_finish,
- _cairo_quartz_surface_acquire_source_image,
+ _cairo_quartz_surface_acquire_image,
_cairo_quartz_surface_release_source_image,
_cairo_quartz_surface_acquire_dest_image,
_cairo_quartz_surface_release_dest_image,
@@ -2942,7 +3208,7 @@ static const struct _cairo_surface_backe
NULL, /* old_show_glyphs */
NULL, /* get_font_options */
NULL, /* flush */
- NULL, /* mark_dirty_rectangle */
+ _cairo_quartz_surface_mark_dirty_rectangle,
NULL, /* scaled_font_fini */
NULL, /* scaled_glyph_fini */
@@ -2952,7 +3218,7 @@ static const struct _cairo_surface_backe
_cairo_quartz_surface_fill,
_cairo_quartz_surface_show_glyphs,
- _cairo_quartz_surface_snapshot,
+ NULL, /* snapshot */
NULL, /* is_similar */
NULL /* fill_stroke */
};
@@ -3004,6 +3270,9 @@ _cairo_quartz_surface_create_internal (C
surface->imageData = NULL;
surface->imageSurfaceEquiv = NULL;
+ surface->bitmapContextImage = NULL;
+ surface->cgLayer = NULL;
+ surface->ownsData = TRUE;
return surface;
}
@@ -3056,6 +3325,81 @@ cairo_quartz_surface_create_for_cg_conte
}
/**
+ * cairo_quartz_cglayer_surface_create_similar
+ * @surface: The returned surface can be efficiently drawn into this
+ * destination surface (if tiling is not used)."
+ * @content: the content type of the surface
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGLayer, if the given surface
+ * is a Quartz surface; the CGLayer is created to match the surface's
+ * Quartz context. Otherwise just calls cairo_surface_create_similar.
+ * The returned surface can be efficiently blitted to the given surface,
+ * but tiling and 'extend' modes other than NONE are not so efficient.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.10
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
+ cairo_content_t content,
+ unsigned int width,
+ unsigned int height)
+{
+ cairo_quartz_surface_t *surf;
+ CGLayerRef layer;
+ CGContextRef ctx;
+ CGContextRef cgContext;
+
+ cgContext = cairo_quartz_surface_get_cg_context (surface);
+ if (!cgContext)
+ return cairo_surface_create_similar (surface, content,
+ width, height);
+
+
+ if (!_cairo_quartz_verify_surface_size(width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ /* If we pass zero width or height into CGLayerCreateWithContext below,
+ * it will fail.
+ */
+ if (width == 0 || height == 0) {
+ return (cairo_surface_t*)
+ _cairo_quartz_surface_create_internal (NULL, content,
+ width, height);
+ }
+
+ layer = CGLayerCreateWithContext (cgContext,
+ CGSizeMake (width, height),
+ NULL);
+ if (!layer)
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+
+ ctx = CGLayerGetContext (layer);
+ CGContextSetInterpolationQuality (ctx, kCGInterpolationNone);
+ /* Flip it when we draw into it, so that when we finally composite it
+ * to a flipped target, the directions match and Quartz will optimize
+ * the composition properly
+ */
+ CGContextTranslateCTM (ctx, 0, height);
+ CGContextScaleCTM (ctx, 1, -1);
+
+ CGContextRetain (ctx);
+ surf = _cairo_quartz_surface_create_internal (ctx, content,
+ width, height);
+ if (surf->base.status) {
+ CGLayerRelease (layer);
+ // create_internal will have set an error
+ return (cairo_surface_t*) surf;
+ }
+ surf->cgLayer = layer;
+
+ return (cairo_surface_t *) surf;
+}
+
+/**
* cairo_quartz_surface_create
* @format: format of pixels in the surface to create
* @width: width of the surface, in pixels
@@ -3075,13 +3419,93 @@ cairo_quartz_surface_create (cairo_forma
unsigned int width,
unsigned int height)
{
+ int stride;
+ unsigned char *data;
+
+ if (!_cairo_quartz_verify_surface_size(width, height))
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
+
+ if (width == 0 || height == 0) {
+ return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+ width, height);
+ }
+
+ if (format == CAIRO_FORMAT_ARGB32 ||
+ format == CAIRO_FORMAT_RGB24)
+ {
+ stride = width * 4;
+ } else if (format == CAIRO_FORMAT_A8) {
+ stride = width;
+ } else if (format == CAIRO_FORMAT_A1) {
+ /* I don't think we can usefully support this, as defined by
+ * cairo_format_t -- these are 1-bit pixels stored in 32-bit
+ * quantities.
+ */
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ } else {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
+ }
+
+ /* The Apple docs say that for best performance, the stride and the data
+ * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
+ * so we don't have to anything special on allocation.
+ */
+ stride = (stride + 15) & ~15;
+
+ data = _cairo_malloc_ab (height, stride);
+ if (!data) {
+ return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+ }
+
+ /* zero the memory to match the image surface behaviour */
+ memset (data, 0, height * stride);
+
+ cairo_quartz_surface_t *surf;
+ surf = (cairo_quartz_surface_t *) cairo_quartz_surface_create_for_data
+ (data, format, width, height, stride);
+ if (surf->base.status) {
+ free (data);
+ return (cairo_surface_t *) surf;
+ }
+
+ // We created this data, so we can delete it.
+ surf->ownsData = TRUE;
+
+ return (cairo_surface_t *) surf;
+}
+
+/**
+ * cairo_quartz_surface_create_for_data
+ * @data: a pointer to a buffer supplied by the application in which
+ * to write contents. This pointer must be suitably aligned for any
+ * kind of variable, (for example, a pointer returned by malloc).
+ * @format: format of pixels in the surface to create
+ * @width: width of the surface, in pixels
+ * @height: height of the surface, in pixels
+ *
+ * Creates a Quartz surface backed by a CGBitmap. The surface is
+ * created using the Device RGB (or Device Gray, for A8) color space.
+ * All Cairo operations, including those that require software
+ * rendering, will succeed on this surface.
+ *
+ * Return value: the newly created surface.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_quartz_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride)
+{
cairo_quartz_surface_t *surf;
CGContextRef cgc;
CGColorSpaceRef cgColorspace;
CGBitmapInfo bitinfo;
- void *imageData;
- int stride;
+ void *imageData = data;
int bitsPerComponent;
+ unsigned int i;
// verify width and height of surface
if (!_cairo_quartz_verify_surface_size(width, height))
@@ -3102,10 +3526,8 @@ cairo_quartz_surface_create (cairo_forma
else
bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
- stride = width * 4;
} else if (format == CAIRO_FORMAT_A8) {
cgColorspace = NULL;
- stride = width;
bitinfo = kCGImageAlphaOnly;
bitsPerComponent = 8;
} else if (format == CAIRO_FORMAT_A1) {
@@ -3118,21 +3540,6 @@ cairo_quartz_surface_create (cairo_forma
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
}
- /* The Apple docs say that for best performance, the stride and the data
- * pointer should be 16-byte aligned. malloc already aligns to 16-bytes,
- * so we don't have to anything special on allocation.
- */
- stride = (stride + 15) & ~15;
-
- imageData = _cairo_malloc_ab (height, stride);
- if (!imageData) {
- CGColorSpaceRelease (cgColorspace);
- return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
- }
-
- /* zero the memory to match the image surface behaviour */
- memset (imageData, 0, height * stride);
-
cgc = CGBitmapContextCreate (imageData,
width,
height,
@@ -3161,7 +3568,19 @@ cairo_quartz_surface_create (cairo_forma
}
surf->imageData = imageData;
- surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
+
+ cairo_surface_t* tmpImageSurfaceEquiv =
+ cairo_image_surface_create_for_data (imageData, format,
+ width, height, stride);
+
+ if (cairo_surface_status (tmpImageSurfaceEquiv)) {
+ // Tried & failed to create an imageSurfaceEquiv!
+ cairo_surface_destroy (tmpImageSurfaceEquiv);
+ surf->imageSurfaceEquiv = NULL;
+ } else {
+ surf->imageSurfaceEquiv = tmpImageSurfaceEquiv;
+ surf->ownsData = FALSE;
+ }
return (cairo_surface_t *) surf;
}
@@ -3193,6 +3612,74 @@ _cairo_surface_is_quartz (const cairo_su
return surface->backend == &cairo_quartz_surface_backend;
}
+CGContextRef
+cairo_quartz_get_cg_context_with_clip (cairo_t *cr)
+{
+
+ cairo_surface_t *surface = cr->gstate->target;
+ cairo_clip_t *clip = &cr->gstate->clip;
+ cairo_status_t status;
+
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
+ return NULL;
+
+ if (!clip->path) {
+ if (clip->all_clipped) {
+ /* Save the state before we set an empty clip rect so that
+ * our previous clip will be restored */
+
+ /* _cairo_surface_clipper_set_clip doesn't deal with
+ * clip->all_clipped because drawing is normally discarded earlier */
+ CGRect empty = {{0,0}, {0,0}};
+ CGContextClipToRect (quartz->cgContext, empty);
+ CGContextSaveGState (quartz->cgContext);
+
+ return quartz->cgContext;
+ }
+
+ /* an empty clip is represented by NULL */
+ clip = NULL;
+ }
+
+ status = _cairo_surface_clipper_set_clip (&quartz->clipper, clip);
+
+ /* Save the state after we set the clip so that it persists
+ * after we restore */
+ CGContextSaveGState (quartz->cgContext);
+
+ if (unlikely (status))
+ return NULL;
+
+ return quartz->cgContext;
+}
+
+void
+cairo_quartz_finish_cg_context_with_clip (cairo_t *cr)
+{
+ cairo_surface_t *surface = cr->gstate->target;
+
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t*)surface;
+
+ if (cairo_surface_get_type(surface) != CAIRO_SURFACE_TYPE_QUARTZ)
+ return;
+
+ CGContextRestoreGState (quartz->cgContext);
+}
+
+cairo_surface_t *
+cairo_quartz_surface_get_image (cairo_surface_t *surface)
+{
+ cairo_quartz_surface_t *quartz = (cairo_quartz_surface_t *)surface;
+ cairo_image_surface_t *image;
+
+ if (_cairo_quartz_get_image(quartz, &image))
+ return NULL;
+
+ return (cairo_surface_t *)image;
+}
+
/* Debug stuff */
#ifdef QUARTZ_DEBUG
--- a/src/cairo-quartz.h 2012-11-13 18:20:00.000000000 -0800
+++ b/src/cairo-quartz.h 2012-11-13 18:06:56.000000000 -0800
@@ -50,6 +50,19 @@ cairo_quartz_surface_create (cairo_forma
unsigned int height);
cairo_public cairo_surface_t *
+cairo_quartz_surface_create_for_data (unsigned char *data,
+ cairo_format_t format,
+ unsigned int width,
+ unsigned int height,
+ unsigned int stride);
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_create_cg_layer (cairo_surface_t *surface,
+ cairo_content_t content,
+ unsigned int width,
+ unsigned int height);
+
+cairo_public cairo_surface_t *
cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
unsigned int width,
unsigned int height);
@@ -57,6 +70,15 @@ cairo_quartz_surface_create_for_cg_conte
cairo_public CGContextRef
cairo_quartz_surface_get_cg_context (cairo_surface_t *surface);
+cairo_public CGContextRef
+cairo_quartz_get_cg_context_with_clip (cairo_t *cr);
+
+cairo_public void
+cairo_quartz_finish_cg_context_with_clip (cairo_t *cr);
+
+cairo_public cairo_surface_t *
+cairo_quartz_surface_get_image (cairo_surface_t *surface);
+
#if CAIRO_HAS_QUARTZ_FONT
/*
@@ -66,8 +88,10 @@ cairo_quartz_surface_get_cg_context (cai
cairo_public cairo_font_face_t *
cairo_quartz_font_face_create_for_cgfont (CGFontRef font);
+#ifndef __LP64__
cairo_public cairo_font_face_t *
cairo_quartz_font_face_create_for_atsu_font_id (ATSUFontID font_id);
+#endif
#endif /* CAIRO_HAS_QUARTZ_FONT */