6bdd276d05
Former-commit-id: fd56571888259555122d8a0f58c68838229cea2b
2187 lines
78 KiB
Diff
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 */
|