summaryrefslogtreecommitdiff
path: root/src/cairo-quartz-surface.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-quartz-surface.c')
-rw-r--r--src/cairo-quartz-surface.c2253
1 files changed, 877 insertions, 1376 deletions
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 113674f..bb7be48 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -77,6 +77,7 @@
* This macro can be used to conditionally compile backend-specific code.
*/
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
/* This method is private, but it exists. Its params are are exposed
* as args to the NS* method, but not as CG.
*/
@@ -97,35 +98,21 @@ enum PrivateCGCompositeMode {
};
typedef enum PrivateCGCompositeMode PrivateCGCompositeMode;
CG_EXTERN void CGContextSetCompositeOperation (CGContextRef, PrivateCGCompositeMode);
-CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
-
-/* We need to work with the 10.3 SDK as well (and 10.3 machines; luckily, 10.3.9
- * has all the stuff we care about, just some of it isn't exported in the SDK.
- */
-#ifndef kCGBitmapByteOrder32Host
-#define USE_10_3_WORKAROUNDS
-#define kCGBitmapAlphaInfoMask 0x1F
-#define kCGBitmapByteOrderMask 0x7000
-#define kCGBitmapByteOrder32Host 0
-
-typedef uint32_t CGBitmapInfo;
-
-/* public in 10.4, present in 10.3.9 */
-CG_EXTERN void CGContextReplacePathWithStrokedPath (CGContextRef);
-CG_EXTERN CGImageRef CGBitmapContextCreateImage (CGContextRef);
#endif
/* Some of these are present in earlier versions of the OS than where
- * they are public; others are not public at all (CGContextCopyPath,
- * CGContextReplacePathWithClipPath, many of the getters, etc.)
+ * they are public; other are not public at all
*/
-static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL;
+/* public since 10.5 */
static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL;
-static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
-static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL;
+
+/* public since 10.6 */
+static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL;
+
+/* not yet public */
+static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL;
static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL;
-static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL;
static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE;
@@ -148,18 +135,16 @@ static cairo_bool_t
_cairo_surface_is_quartz (const cairo_surface_t *surface);
/* Load all extra symbols */
-static void quartz_ensure_symbols(void)
+static void quartz_ensure_symbols (void)
{
- if (_cairo_quartz_symbol_lookup_done)
+ if (likely (_cairo_quartz_symbol_lookup_done))
return;
- CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask");
- CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage");
- CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType");
- CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts");
- CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath");
- CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
- CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
+ CGContextDrawTiledImagePtr = dlsym (RTLD_DEFAULT, "CGContextDrawTiledImage");
+ CGContextGetTypePtr = dlsym (RTLD_DEFAULT, "CGContextGetType");
+ CGContextCopyPathPtr = dlsym (RTLD_DEFAULT, "CGContextCopyPath");
+ CGContextGetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing");
+ CGContextSetAllowsFontSmoothingPtr = dlsym (RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing");
_cairo_quartz_symbol_lookup_done = TRUE;
}
@@ -184,7 +169,7 @@ _cairo_quartz_create_cgimage (cairo_format_t format,
switch (format) {
case CAIRO_FORMAT_ARGB32:
if (colorSpace == NULL)
- colorSpace = CGColorSpaceCreateDeviceRGB();
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
bitinfo |= kCGImageAlphaPremultipliedFirst;
bitsPerComponent = 8;
bitsPerPixel = 32;
@@ -192,7 +177,7 @@ _cairo_quartz_create_cgimage (cairo_format_t format,
case CAIRO_FORMAT_RGB24:
if (colorSpace == NULL)
- colorSpace = CGColorSpaceCreateDeviceRGB();
+ colorSpace = CGColorSpaceCreateDeviceRGB ();
bitinfo |= kCGImageAlphaNoneSkipFirst;
bitsPerComponent = 8;
bitsPerPixel = 32;
@@ -221,7 +206,7 @@ _cairo_quartz_create_cgimage (cairo_format_t format,
height * stride,
releaseCallback);
- if (!dataProvider) {
+ if (unlikely (!dataProvider)) {
// manually release
if (releaseCallback)
releaseCallback (releaseInfo, data, height * stride);
@@ -260,19 +245,18 @@ FINISH:
}
static inline cairo_bool_t
-_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) {
- if (cgc == NULL)
+_cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc)
+{
+ if (unlikely (cgc == NULL))
return FALSE;
- if (CGContextGetTypePtr) {
+ if (likely (CGContextGetTypePtr)) {
/* 4 is the type value of a bitmap context */
- if (CGContextGetTypePtr(cgc) == 4)
- return TRUE;
- return FALSE;
+ return CGContextGetTypePtr (cgc) == 4;
}
/* This will cause a (harmless) warning to be printed if called on a non-bitmap context */
- return CGBitmapContextGetBitsPerPixel(cgc) != 0;
+ return CGBitmapContextGetBitsPerPixel (cgc) != 0;
}
/* CoreGraphics limitation with flipped CTM surfaces: height must be less than signed 16-bit max */
@@ -282,16 +266,14 @@ _cairo_quartz_is_cgcontext_bitmap_context (CGContextRef cgc) {
/* is the desired size of the surface within bounds? */
cairo_bool_t
-_cairo_quartz_verify_surface_size(int width, int height)
+_cairo_quartz_verify_surface_size (int width, int height)
{
/* hmmm, allow width, height == 0 ? */
- if (width < 0 || height < 0) {
+ if (width < 0 || height < 0)
return FALSE;
- }
- if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT) {
+ if (width > CG_MAX_WIDTH || height > CG_MAX_HEIGHT)
return FALSE;
- }
return TRUE;
}
@@ -305,7 +287,7 @@ static cairo_status_t
_cairo_path_to_quartz_context_move_to (void *closure,
const cairo_point_t *point)
{
- //ND((stderr, "moveto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
+ //ND ((stderr, "moveto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
@@ -317,7 +299,7 @@ static cairo_status_t
_cairo_path_to_quartz_context_line_to (void *closure,
const cairo_point_t *point)
{
- //ND((stderr, "lineto: %f %f\n", _cairo_fixed_to_double(point->x), _cairo_fixed_to_double(point->y)));
+ //ND ((stderr, "lineto: %f %f\n", _cairo_fixed_to_double (point->x), _cairo_fixed_to_double (point->y)));
double x = _cairo_fixed_to_double (point->x);
double y = _cairo_fixed_to_double (point->y);
@@ -331,10 +313,10 @@ _cairo_path_to_quartz_context_curve_to (void *closure,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
- //ND( (stderr, "curveto: %f,%f %f,%f %f,%f\n",
- // _cairo_fixed_to_double(p0->x), _cairo_fixed_to_double(p0->y),
- // _cairo_fixed_to_double(p1->x), _cairo_fixed_to_double(p1->y),
- // _cairo_fixed_to_double(p2->x), _cairo_fixed_to_double(p2->y)));
+ //ND ((stderr, "curveto: %f,%f %f,%f %f,%f\n",
+ // _cairo_fixed_to_double (p0->x), _cairo_fixed_to_double (p0->y),
+ // _cairo_fixed_to_double (p1->x), _cairo_fixed_to_double (p1->y),
+ // _cairo_fixed_to_double (p2->x), _cairo_fixed_to_double (p2->y)));
double x0 = _cairo_fixed_to_double (p0->x);
double y0 = _cairo_fixed_to_double (p0->y);
double x1 = _cairo_fixed_to_double (p1->x);
@@ -342,15 +324,14 @@ _cairo_path_to_quartz_context_curve_to (void *closure,
double x2 = _cairo_fixed_to_double (p2->x);
double y2 = _cairo_fixed_to_double (p2->y);
- CGContextAddCurveToPoint (closure,
- x0, y0, x1, y1, x2, y2);
+ CGContextAddCurveToPoint (closure, x0, y0, x1, y1, x2, y2);
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_path_to_quartz_context_close_path (void *closure)
{
- //ND((stderr, "closepath\n"));
+ //ND ((stderr, "closepath\n"));
CGContextClosePath (closure);
return CAIRO_STATUS_SUCCESS;
}
@@ -363,7 +344,6 @@ _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
CGContextBeginPath (closure);
status = _cairo_path_fixed_interpret (path,
- CAIRO_DIRECTION_FORWARD,
_cairo_path_to_quartz_context_move_to,
_cairo_path_to_quartz_context_line_to,
_cairo_path_to_quartz_context_curve_to,
@@ -377,6 +357,7 @@ _cairo_quartz_cairo_path_to_quartz_context (cairo_path_fixed_t *path,
* Misc helpers/callbacks
*/
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
static PrivateCGCompositeMode
_cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
{
@@ -424,158 +405,216 @@ _cairo_quartz_cairo_operator_to_quartz_composite (cairo_operator_t op)
case CAIRO_OPERATOR_HSL_COLOR:
case CAIRO_OPERATOR_HSL_LUMINOSITY:
default:
- assert (0);
+ ASSERT_NOT_REACHED;
+ }
+}
+#endif
+
+static CGBlendMode
+_cairo_quartz_cairo_operator_to_quartz_blend (cairo_operator_t op)
+{
+ switch (op) {
+ case CAIRO_OPERATOR_MULTIPLY:
+ return kCGBlendModeMultiply;
+ case CAIRO_OPERATOR_SCREEN:
+ return kCGBlendModeScreen;
+ case CAIRO_OPERATOR_OVERLAY:
+ return kCGBlendModeOverlay;
+ case CAIRO_OPERATOR_DARKEN:
+ return kCGBlendModeDarken;
+ case CAIRO_OPERATOR_LIGHTEN:
+ return kCGBlendModeLighten;
+ case CAIRO_OPERATOR_COLOR_DODGE:
+ return kCGBlendModeColorDodge;
+ case CAIRO_OPERATOR_COLOR_BURN:
+ return kCGBlendModeColorBurn;
+ case CAIRO_OPERATOR_HARD_LIGHT:
+ return kCGBlendModeHardLight;
+ case CAIRO_OPERATOR_SOFT_LIGHT:
+ return kCGBlendModeSoftLight;
+ case CAIRO_OPERATOR_DIFFERENCE:
+ return kCGBlendModeDifference;
+ case CAIRO_OPERATOR_EXCLUSION:
+ return kCGBlendModeExclusion;
+ case CAIRO_OPERATOR_HSL_HUE:
+ return kCGBlendModeHue;
+ case CAIRO_OPERATOR_HSL_SATURATION:
+ return kCGBlendModeSaturation;
+ case CAIRO_OPERATOR_HSL_COLOR:
+ return kCGBlendModeColor;
+ case CAIRO_OPERATOR_HSL_LUMINOSITY:
+ return kCGBlendModeLuminosity;
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
+ case CAIRO_OPERATOR_CLEAR:
+ return kCGBlendModeClear;
+ case CAIRO_OPERATOR_SOURCE:
+ return kCGBlendModeCopy;
+ case CAIRO_OPERATOR_OVER:
+ return kCGBlendModeNormal;
+ case CAIRO_OPERATOR_IN:
+ return kCGBlendModeSourceIn;
+ case CAIRO_OPERATOR_OUT:
+ return kCGBlendModeSourceOut;
+ case CAIRO_OPERATOR_ATOP:
+ return kCGBlendModeSourceAtop;
+ case CAIRO_OPERATOR_DEST_OVER:
+ return kCGBlendModeDestinationOver;
+ case CAIRO_OPERATOR_DEST_IN:
+ return kCGBlendModeDestinationIn;
+ case CAIRO_OPERATOR_DEST_OUT:
+ return kCGBlendModeDestinationOut;
+ case CAIRO_OPERATOR_DEST_ATOP:
+ return kCGBlendModeDestinationAtop;
+ case CAIRO_OPERATOR_XOR:
+ return kCGBlendModeXOR;
+ case CAIRO_OPERATOR_ADD:
+ return kCGBlendModePlusLighter;
+#else
+ case CAIRO_OPERATOR_CLEAR:
+ case CAIRO_OPERATOR_SOURCE:
+ case CAIRO_OPERATOR_OVER:
+ case CAIRO_OPERATOR_IN:
+ case CAIRO_OPERATOR_OUT:
+ case CAIRO_OPERATOR_ATOP:
+ case CAIRO_OPERATOR_DEST_OVER:
+ case CAIRO_OPERATOR_DEST_IN:
+ case CAIRO_OPERATOR_DEST_OUT:
+ case CAIRO_OPERATOR_DEST_ATOP:
+ case CAIRO_OPERATOR_XOR:
+ case CAIRO_OPERATOR_ADD:
+#endif
+
+ case CAIRO_OPERATOR_DEST:
+ case CAIRO_OPERATOR_SATURATE:
+ default:
+ ASSERT_NOT_REACHED;
}
}
static cairo_int_status_t
-_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
+_cairo_cgcontext_set_cairo_operator (CGContextRef context, cairo_operator_t op)
{
- ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
+ CGBlendMode blendmode;
- if (surface->base.content == CAIRO_CONTENT_ALPHA) {
- /* For some weird reason, some compositing operators are
- swapped when operating on masks */
- switch (op) {
- case CAIRO_OPERATOR_CLEAR:
- case CAIRO_OPERATOR_SOURCE:
- case CAIRO_OPERATOR_OVER:
- case CAIRO_OPERATOR_DEST_IN:
- case CAIRO_OPERATOR_DEST_OUT:
- case CAIRO_OPERATOR_ADD:
- CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
- return CAIRO_STATUS_SUCCESS;
+ if (op == CAIRO_OPERATOR_DEST)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
- case CAIRO_OPERATOR_IN:
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationAtop);
- return CAIRO_STATUS_SUCCESS;
+ /* Quartz doesn't support SATURATE at all. COLOR_DODGE and
+ * COLOR_BURN in Quartz follow the ISO32000 definition, but cairo
+ * uses the definition from the Adobe Supplement.
+ */
+ if (op == CAIRO_OPERATOR_SATURATE ||
+ op == CAIRO_OPERATOR_COLOR_DODGE ||
+ op == CAIRO_OPERATOR_COLOR_BURN)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
- case CAIRO_OPERATOR_DEST_OVER:
- case CAIRO_OPERATOR_MULTIPLY:
- case CAIRO_OPERATOR_SCREEN:
- case CAIRO_OPERATOR_OVERLAY:
- case CAIRO_OPERATOR_DARKEN:
- case CAIRO_OPERATOR_LIGHTEN:
- case CAIRO_OPERATOR_COLOR_DODGE:
- case CAIRO_OPERATOR_COLOR_BURN:
- case CAIRO_OPERATOR_HARD_LIGHT:
- case CAIRO_OPERATOR_SOFT_LIGHT:
- case CAIRO_OPERATOR_DIFFERENCE:
- case CAIRO_OPERATOR_EXCLUSION:
- case CAIRO_OPERATOR_HSL_HUE:
- case CAIRO_OPERATOR_HSL_SATURATION:
- case CAIRO_OPERATOR_HSL_COLOR:
- case CAIRO_OPERATOR_HSL_LUMINOSITY:
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceOver);
- return CAIRO_STATUS_SUCCESS;
+#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1050
+ if (op <= CAIRO_OPERATOR_ADD) {
+ PrivateCGCompositeMode compmode;
- case CAIRO_OPERATOR_DEST_ATOP:
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeSourceIn);
- return CAIRO_STATUS_SUCCESS;
+ compmode = _cairo_quartz_cairo_operator_to_quartz_composite (op);
+ CGContextSetCompositeOperation (context, compmode);
+ return CAIRO_STATUS_SUCCESS;
+ }
+#endif
- case CAIRO_OPERATOR_SATURATE:
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositePlusLighter);
- return CAIRO_STATUS_SUCCESS;
+ blendmode = _cairo_quartz_cairo_operator_to_quartz_blend (op);
+ CGContextSetBlendMode (context, blendmode);
+ return CAIRO_STATUS_SUCCESS;
+}
+static cairo_int_status_t
+_cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo_operator_t op)
+{
+ ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op));
- case CAIRO_OPERATOR_ATOP:
- /*
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeDestinationOver);
- return CAIRO_STATUS_SUCCESS;
- */
- case CAIRO_OPERATOR_DEST:
- return CAIRO_INT_STATUS_NOTHING_TO_DO;
-
- case CAIRO_OPERATOR_OUT:
- case CAIRO_OPERATOR_XOR:
- default:
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ /* When the destination has no color components, we can avoid some
+ * fallbacks, but we have to workaround operators which behave
+ * differently in Quartz. */
+ if (surface->base.content == CAIRO_CONTENT_ALPHA) {
+ if (op == CAIRO_OPERATOR_ATOP)
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ if (op == CAIRO_OPERATOR_SOURCE ||
+ op == CAIRO_OPERATOR_IN ||
+ op == CAIRO_OPERATOR_OUT ||
+ op == CAIRO_OPERATOR_DEST_IN ||
+ op == CAIRO_OPERATOR_DEST_ATOP ||
+ op == CAIRO_OPERATOR_XOR)
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
}
- } else {
- switch (op) {
- case CAIRO_OPERATOR_CLEAR:
- case CAIRO_OPERATOR_SOURCE:
- case CAIRO_OPERATOR_OVER:
- case CAIRO_OPERATOR_IN:
- case CAIRO_OPERATOR_OUT:
- case CAIRO_OPERATOR_ATOP:
- case CAIRO_OPERATOR_DEST_OVER:
- case CAIRO_OPERATOR_DEST_IN:
- case CAIRO_OPERATOR_DEST_OUT:
- case CAIRO_OPERATOR_DEST_ATOP:
- case CAIRO_OPERATOR_XOR:
- case CAIRO_OPERATOR_ADD:
- CGContextSetCompositeOperation (surface->cgContext, _cairo_quartz_cairo_operator_to_quartz_composite (op));
- return CAIRO_STATUS_SUCCESS;
- case CAIRO_OPERATOR_DEST:
- return CAIRO_INT_STATUS_NOTHING_TO_DO;
-
- case CAIRO_OPERATOR_SATURATE:
- /* TODO: the following are mostly supported by CGContextSetBlendMode*/
- case CAIRO_OPERATOR_MULTIPLY:
- case CAIRO_OPERATOR_SCREEN:
- case CAIRO_OPERATOR_OVERLAY:
- case CAIRO_OPERATOR_DARKEN:
- case CAIRO_OPERATOR_LIGHTEN:
- case CAIRO_OPERATOR_COLOR_DODGE:
- case CAIRO_OPERATOR_COLOR_BURN:
- case CAIRO_OPERATOR_HARD_LIGHT:
- case CAIRO_OPERATOR_SOFT_LIGHT:
- case CAIRO_OPERATOR_DIFFERENCE:
- case CAIRO_OPERATOR_EXCLUSION:
- case CAIRO_OPERATOR_HSL_HUE:
- case CAIRO_OPERATOR_HSL_SATURATION:
- case CAIRO_OPERATOR_HSL_COLOR:
- case CAIRO_OPERATOR_HSL_LUMINOSITY:
- default:
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
+ if (op == CAIRO_OPERATOR_DEST_OVER)
+ op = CAIRO_OPERATOR_OVER;
+ else if (op == CAIRO_OPERATOR_SATURATE)
+ op = CAIRO_OPERATOR_ADD;
+ else if (op == CAIRO_OPERATOR_COLOR_DODGE)
+ op = CAIRO_OPERATOR_OVER;
+ else if (op == CAIRO_OPERATOR_COLOR_BURN)
+ op = CAIRO_OPERATOR_OVER;
}
+
+ return _cairo_cgcontext_set_cairo_operator (surface->cgContext, op);
}
static inline CGLineCap
_cairo_quartz_cairo_line_cap_to_quartz (cairo_line_cap_t ccap)
{
switch (ccap) {
- case CAIRO_LINE_CAP_BUTT: return kCGLineCapButt; break;
- case CAIRO_LINE_CAP_ROUND: return kCGLineCapRound; break;
- case CAIRO_LINE_CAP_SQUARE: return kCGLineCapSquare; break;
- }
+ default:
+ ASSERT_NOT_REACHED;
- return kCGLineCapButt;
+ case CAIRO_LINE_CAP_BUTT:
+ return kCGLineCapButt;
+
+ case CAIRO_LINE_CAP_ROUND:
+ return kCGLineCapRound;
+
+ case CAIRO_LINE_CAP_SQUARE:
+ return kCGLineCapSquare;
+ }
}
static inline CGLineJoin
_cairo_quartz_cairo_line_join_to_quartz (cairo_line_join_t cjoin)
{
switch (cjoin) {
- case CAIRO_LINE_JOIN_MITER: return kCGLineJoinMiter; break;
- case CAIRO_LINE_JOIN_ROUND: return kCGLineJoinRound; break;
- case CAIRO_LINE_JOIN_BEVEL: return kCGLineJoinBevel; break;
- }
+ default:
+ ASSERT_NOT_REACHED;
- return kCGLineJoinMiter;
+ case CAIRO_LINE_JOIN_MITER:
+ return kCGLineJoinMiter;
+
+ case CAIRO_LINE_JOIN_ROUND:
+ return kCGLineJoinRound;
+
+ case CAIRO_LINE_JOIN_BEVEL:
+ return kCGLineJoinBevel;
+ }
}
static inline CGInterpolationQuality
_cairo_quartz_filter_to_quartz (cairo_filter_t filter)
{
switch (filter) {
- case CAIRO_FILTER_NEAREST:
- return kCGInterpolationNone;
+ case CAIRO_FILTER_NEAREST:
+ case CAIRO_FILTER_FAST:
+ return kCGInterpolationNone;
- case CAIRO_FILTER_FAST:
- return kCGInterpolationLow;
+ case CAIRO_FILTER_BEST:
+ case CAIRO_FILTER_GOOD:
+ case CAIRO_FILTER_BILINEAR:
+ case CAIRO_FILTER_GAUSSIAN:
+ return kCGInterpolationDefault;
- case CAIRO_FILTER_BEST:
- case CAIRO_FILTER_GOOD:
- case CAIRO_FILTER_BILINEAR:
- case CAIRO_FILTER_GAUSSIAN:
- return kCGInterpolationDefault;
+ default:
+ ASSERT_NOT_REACHED;
+ return kCGInterpolationDefault;
}
-
- return kCGInterpolationDefault;
}
static inline void
@@ -590,152 +629,6 @@ _cairo_quartz_cairo_matrix_to_quartz (const cairo_matrix_t *src,
dst->ty = src->y0;
}
-typedef struct {
- bool isClipping;
- CGGlyph *cg_glyphs;
- CGSize *cg_advances;
- size_t nglyphs;
- CGAffineTransform textTransform;
- CGFontRef font;
- CGPoint origin;
-} unbounded_show_glyphs_t;
-
-typedef struct {
- CGPathRef cgPath;
- cairo_fill_rule_t fill_rule;
-} unbounded_stroke_fill_t;
-
-typedef struct {
- CGImageRef mask;
- CGAffineTransform maskTransform;
-} unbounded_mask_t;
-
-typedef enum {
- UNBOUNDED_STROKE_FILL,
- UNBOUNDED_SHOW_GLYPHS,
- UNBOUNDED_MASK
-} unbounded_op_t;
-
-typedef struct {
- unbounded_op_t op;
- union {
- unbounded_stroke_fill_t stroke_fill;
- unbounded_show_glyphs_t show_glyphs;
- unbounded_mask_t mask;
- } u;
-} unbounded_op_data_t;
-
-static void
-_cairo_quartz_fixup_unbounded_operation (cairo_quartz_surface_t *surface,
- unbounded_op_data_t *op,
- cairo_antialias_t antialias)
-{
- CGRect clipBox, clipBoxRound;
- CGContextRef cgc;
- CGImageRef maskImage;
-
- /* TODO: handle failure */
- if (!CGContextClipToMaskPtr)
- return;
-
- clipBox = CGContextGetClipBoundingBox (surface->cgContext);
- clipBoxRound = CGRectIntegral (clipBox);
-
- cgc = CGBitmapContextCreate (NULL,
- clipBoxRound.size.width,
- clipBoxRound.size.height,
- 8,
- (((size_t) clipBoxRound.size.width) + 15) & (~15),
- NULL,
- kCGImageAlphaOnly);
-
- if (!cgc)
- return;
-
- CGContextSetCompositeOperation (cgc, kPrivateCGCompositeCopy);
- /* We want to mask out whatever we just rendered, so we fill the
- * surface opaque, and then we'll render transparent.
- */
- CGContextSetAlpha (cgc, 1.0f);
- CGContextFillRect (cgc, CGRectMake (0, 0, clipBoxRound.size.width, clipBoxRound.size.height));
-
- CGContextSetCompositeOperation (cgc, kPrivateCGCompositeClear);
- CGContextSetShouldAntialias (cgc, (antialias != CAIRO_ANTIALIAS_NONE));
-
- CGContextTranslateCTM (cgc, -clipBoxRound.origin.x, -clipBoxRound.origin.y);
-
- /* We need to either render the path that was given to us, or the glyph op */
- if (op->op == UNBOUNDED_STROKE_FILL) {
- CGContextBeginPath (cgc);
- CGContextAddPath (cgc, op->u.stroke_fill.cgPath);
-
- if (op->u.stroke_fill.fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextFillPath (cgc);
- 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
- * cg_advances we have were already transformed, so we
- * don't have to do anything. */
- CGContextSetTextDrawingMode (cgc, kCGTextClip);
- CGContextSaveGState (cgc);
- }
-
- 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);
- }
- } else if (op->op == UNBOUNDED_MASK) {
- CGAffineTransform ctm = CGContextGetCTM (cgc);
- CGContextSaveGState (cgc);
- CGContextConcatCTM (cgc, op->u.mask.maskTransform);
- CGContextClipToMask (cgc, CGRectMake (0.0f, 0.0f,
- CGImageGetWidth(op->u.mask.mask), CGImageGetHeight(op->u.mask.mask)),
- op->u.mask.mask);
- CGContextSetCTM (cgc, ctm);
- CGContextClearRect (cgc, clipBoxRound);
- CGContextRestoreGState (cgc);
- }
-
- /* Also mask out the portion of the clipbox that we rounded out, if any */
- if (!CGRectEqualToRect (clipBox, clipBoxRound)) {
- CGContextBeginPath (cgc);
- CGContextAddRect (cgc, clipBoxRound);
- CGContextAddRect (cgc, clipBox);
- CGContextEOFillPath (cgc);
- }
-
- maskImage = CGBitmapContextCreateImage (cgc);
- CGContextRelease (cgc);
-
- if (!maskImage)
- return;
-
- /* Then render with the mask */
- CGContextSaveGState (surface->cgContext);
-
- CGContextSetCompositeOperation (surface->cgContext, kPrivateCGCompositeCopy);
- CGContextClipToMaskPtr (surface->cgContext, clipBoxRound, maskImage);
- CGImageRelease (maskImage);
-
- /* Finally, clear out the entire clipping region through our mask */
- CGContextClearRect (surface->cgContext, clipBoxRound);
-
- CGContextRestoreGState (surface->cgContext);
-}
/*
* Source -> Quartz setup and finish functions
@@ -754,18 +647,16 @@ ComputeGradientValue (void *info,
* REPEAT/REFLECT
*/
if (grad->base.extend == CAIRO_EXTEND_REPEAT) {
- fdist = fdist - floor(fdist);
+ fdist = fdist - floor (fdist);
} else if (grad->base.extend == CAIRO_EXTEND_REFLECT) {
- fdist = fmod(fabs(fdist), 2.0);
- if (fdist > 1.0) {
+ fdist = fmod (fabs (fdist), 2.0);
+ if (fdist > 1.0)
fdist = 2.0 - fdist;
- }
}
- for (i = 0; i < grad->n_stops; i++) {
+ for (i = 0; i < grad->n_stops; i++)
if (grad->stops[i].offset > fdist)
break;
- }
if (i == 0 || i == grad->n_stops) {
if (i == grad->n_stops)
@@ -801,134 +692,62 @@ static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
static const CGFunctionCallbacks gradient_callbacks = {
0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
};
-/* Quartz will clamp input values to the input range.
-
- Our stops are all in the range 0.0 to 1.0. However, the color before the
- beginning of the gradient line is obtained by Quartz computing a negative
- position on the gradient line, clamping it to the input range we specified
- for our color function, and then calling our color function (actually it
- pre-samples the color function into an array, but that doesn't matter just
- here). Therefore if we set the lower bound to 0.0, a negative position
- on the gradient line will pass 0.0 to ComputeGradientValue, which will
- select the last color stop with position 0, although it should select
- the first color stop (this matters when there are multiple color stops with
- position 0).
-
- Therefore we pass a small negative number as the lower bound of the input
- range, so this value gets passed into ComputeGradientValue, which will
- return the color of the first stop. The number should be small because
- as far as I can tell, Quartz pre-samples the entire input range of the color
- function into an array of fixed size, so if the input range is larger
- than needed, the resolution of the gradient will be unnecessarily low.
-*/
-static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
+
+/* Quartz computes a small number of samples of the gradient color
+ * function. On MacOS X 10.5 it apparently computes only 1024
+ * samples. */
+#define MAX_GRADIENT_RANGE 1024
static CGFunctionRef
-CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
+_cairo_quartz_create_gradient_function (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents,
+ cairo_circle_double_t *start,
+ cairo_circle_double_t *end)
{
cairo_pattern_t *pat;
+ cairo_quartz_float_t input_value_range[2];
- if (_cairo_pattern_create_copy (&pat, &gpat->base))
- /* quartz doesn't deal very well with malloc failing, so there's
- * not much point in us trying either */
- return NULL;
-
- return CGFunctionCreate (pat,
- 1,
- nonrepeating_gradient_input_value_range,
- 4,
- gradient_output_value_ranges,
- &gradient_callbacks);
-}
+ if (gradient->base.extend != CAIRO_EXTEND_NONE) {
+ double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+ double t[2], tolerance;
+
+ tolerance = fabs (_cairo_matrix_compute_determinant (&gradient->base.matrix));
+ tolerance /= _cairo_matrix_transformed_circle_major_axis (&gradient->base.matrix, 1);
+
+ bounds_x1 = extents->x;
+ bounds_y1 = extents->y;
+ bounds_x2 = extents->x + extents->width;
+ bounds_y2 = extents->y + extents->height;
+ _cairo_matrix_transform_bounding_box (&gradient->base.matrix,
+ &bounds_x1, &bounds_y1,
+ &bounds_x2, &bounds_y2,
+ NULL);
+
+ _cairo_gradient_pattern_box_to_parameter (gradient,
+ bounds_x1, bounds_y1,
+ bounds_x2, bounds_y2,
+ tolerance,
+ t);
+
+ if (gradient->base.extend == CAIRO_EXTEND_PAD) {
+ t[0] = MAX (t[0], -0.5);
+ t[1] = MIN (t[1], 1.5);
+ } else if (t[1] - t[0] > MAX_GRADIENT_RANGE)
+ return NULL;
-static void
-UpdateLinearParametersToIncludePoint(double *min_t, double *max_t, CGPoint *start,
- double dx, double dy,
- double x, double y)
-{
- /* Compute a parameter t such that a line perpendicular to the (dx,dy)
- vector, passing through (start->x + dx*t, start->y + dy*t), also
- passes through (x,y).
-
- Let px = x - start->x, py = y - start->y.
- t is given by
- (px - dx*t)*dx + (py - dy*t)*dy = 0
-
- Solving for t we get
- numerator = dx*px + dy*py
- denominator = dx^2 + dy^2
- t = numerator/denominator
-
- In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
- is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
- */
- double px = x - start->x;
- double py = y - start->y;
- double numerator = dx*px + dy*py;
- double denominator = dx*dx + dy*dy;
- double t = numerator/denominator;
-
- if (*min_t > t) {
- *min_t = t;
- }
- if (*max_t < t) {
- *max_t = t;
+ /* set the input range for the function -- the function knows how
+ to map values outside of 0.0 .. 1.0 to the correct color */
+ input_value_range[0] = t[0];
+ input_value_range[1] = t[1];
+ } else {
+ input_value_range[0] = 0;
+ input_value_range[1] = 1;
}
-}
-static CGFunctionRef
-CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
- const cairo_gradient_pattern_t *gpat,
- CGPoint *start, CGPoint *end,
- cairo_rectangle_int_t *extents)
-{
- cairo_pattern_t *pat;
- cairo_quartz_float_t input_value_range[2];
- double t_min = 0.;
- double t_max = 0.;
- double dx = end->x - start->x;
- double dy = end->y - start->y;
- double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
-
- if (!extents) {
- extents = &surface->extents;
- }
- bounds_x1 = extents->x;
- bounds_y1 = extents->y;
- bounds_x2 = extents->x + extents->width;
- bounds_y2 = extents->y + extents->height;
- _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
- &bounds_x1, &bounds_y1,
- &bounds_x2, &bounds_y2,
- NULL);
-
- UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
- bounds_x1, bounds_y1);
- UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
- bounds_x2, bounds_y1);
- UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
- bounds_x2, bounds_y2);
- UpdateLinearParametersToIncludePoint(&t_min, &t_max, start, dx, dy,
- bounds_x1, bounds_y2);
-
- /* Move t_min and t_max to the nearest usable integer to try to avoid
- subtle variations due to numerical instability, especially accidentally
- cutting off a pixel. Extending the gradient repetitions is always safe. */
- t_min = floor (t_min);
- t_max = ceil (t_max);
- end->x = start->x + dx*t_max;
- end->y = start->y + dy*t_max;
- start->x = start->x + dx*t_min;
- start->y = start->y + dy*t_min;
-
- // set the input range for the function -- the function knows how to
- // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
- input_value_range[0] = t_min;
- input_value_range[1] = t_max;
-
- if (_cairo_pattern_create_copy (&pat, &gpat->base))
- /* quartz doesn't deal very well with malloc failing, so there's
- * not much point in us trying either */
+ _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
+ _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
+
+ if (_cairo_pattern_create_copy (&pat, &gradient->base))
return NULL;
return CGFunctionCreate (pat,
@@ -939,151 +758,6 @@ CreateRepeatingLinearGradientFunction (cairo_quartz_surface_t *surface,
&gradient_callbacks);
}
-static void
-UpdateRadialParameterToIncludePoint(double *max_t, CGPoint *center,
- double dr, double dx, double dy,
- double x, double y)
-{
- /* Compute a parameter t such that a circle centered at
- (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
- point (x,y).
-
- Let px = x - center->x, py = y - center->y.
- Parameter values for which t is on the circle are given by
- (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
-
- Solving for t using the quadratic formula, and simplifying, we get
- numerator = dx*px + dy*py +-
- sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
- denominator = dx^2 + dy^2 - dr^2
- t = numerator/denominator
-
- In CreateRepeatingRadialGradientFunction we know the outer circle
- contains the inner circle. Therefore the distance between the circle
- centers plus the radius of the inner circle is less than the radius of
- the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
- Therefore
- dx^2 + dy^2 < dr^2
- So the denominator is negative and the larger solution for t is given by
- numerator = dx*px + dy*py -
- sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
- denominator = dx^2 + dy^2 - dr^2
- t = numerator/denominator
- dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
- */
- double px = x - center->x;
- double py = y - center->y;
- double dx_py_minus_dy_px = dx*py - dy*px;
- double numerator = dx*px + dy*py -
- sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
- double denominator = dx*dx + dy*dy - dr*dr;
- double t = numerator/denominator;
-
- if (*max_t < t) {
- *max_t = t;
- }
-}
-
-/* This must only be called when one of the circles properly contains the other */
-static CGFunctionRef
-CreateRepeatingRadialGradientFunction (cairo_quartz_surface_t *surface,
- const cairo_gradient_pattern_t *gpat,
- CGPoint *start, double *start_radius,
- CGPoint *end, double *end_radius,
- cairo_rectangle_int_t *extents)
-{
- cairo_pattern_t *pat;
- cairo_quartz_float_t input_value_range[2];
- CGPoint *inner;
- double *inner_radius;
- CGPoint *outer;
- double *outer_radius;
- /* minimum and maximum t-parameter values that will make our gradient
- cover the clipBox */
- double t_min, t_max, t_temp;
- /* outer minus inner */
- double dr, dx, dy;
- double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
-
- if (!extents) {
- extents = &surface->extents;
- }
- bounds_x1 = extents->x;
- bounds_y1 = extents->y;
- bounds_x2 = extents->x + extents->width;
- bounds_y2 = extents->y + extents->height;
- _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
- &bounds_x1, &bounds_y1,
- &bounds_x2, &bounds_y2,
- NULL);
-
- if (*start_radius < *end_radius) {
- /* end circle contains start circle */
- inner = start;
- outer = end;
- inner_radius = start_radius;
- outer_radius = end_radius;
- } else {
- /* start circle contains end circle */
- inner = end;
- outer = start;
- inner_radius = end_radius;
- outer_radius = start_radius;
- }
-
- dr = *outer_radius - *inner_radius;
- dx = outer->x - inner->x;
- dy = outer->y - inner->y;
-
- /* We can't round or fudge t_min here, it has to be as accurate as possible. */
- t_min = -(*inner_radius/dr);
- inner->x += t_min*dx;
- inner->y += t_min*dy;
- *inner_radius = 0.;
-
- t_temp = 0.;
- UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
- bounds_x1, bounds_y1);
- UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
- bounds_x2, bounds_y1);
- UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
- bounds_x2, bounds_y2);
- UpdateRadialParameterToIncludePoint(&t_temp, inner, dr, dx, dy,
- bounds_x1, bounds_y2);
- /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
- But for the parameter values we use with Quartz, t_min means radius 0.
- Since the circles are alway expanding and contain the earlier circles,
- it's safe to extend t_max/t_temp as much as we want, so round t_temp up
- to the nearest integer. This may help us give stable results. */
- t_temp = ceil (t_temp);
- t_max = t_min + t_temp;
- outer->x = inner->x + t_temp*dx;
- outer->y = inner->y + t_temp*dy;
- *outer_radius = t_temp*dr;
-
- /* set the input range for the function -- the function knows how to
- map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
- if (*start_radius < *end_radius) {
- input_value_range[0] = t_min;
- input_value_range[1] = t_max;
- } else {
- input_value_range[0] = 1 - t_max;
- input_value_range[1] = 1 - t_min;
- }
-
- if (_cairo_pattern_create_copy (&pat, &gpat->base))
- /* quartz doesn't deal very well with malloc failing, so there's
- * not much point in us trying either */
- return NULL;
-
- return CGFunctionCreate (pat,
- 1,
- input_value_range,
- 4,
- gradient_output_value_ranges,
- &gradient_callbacks);
-}
-
/* Obtain a CGImageRef from a #cairo_surface_t * */
typedef struct {
@@ -1101,8 +775,7 @@ DataProviderReleaseCallback (void *info, const void *data, size_t size)
}
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;
@@ -1116,7 +789,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *target,
if (_cairo_surface_is_quartz (source)) {
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) source;
- if (IS_EMPTY(surface)) {
+ if (IS_EMPTY (surface)) {
*image_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
@@ -1129,13 +802,13 @@ _cairo_surface_to_cgimage (cairo_surface_t *target,
}
source_img = malloc (sizeof (quartz_source_image_t));
- if (source_img == NULL)
+ if (unlikely (source_img == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
source_img->surface = source;
status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra);
- if (status) {
+ if (unlikely (status)) {
free (source_img);
return status;
}
@@ -1157,7 +830,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *target,
source_img);
/* TODO: differentiate memory error and unsupported surface type */
- if (*image_out == NULL)
+ if (unlikely (*image_out == NULL))
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
@@ -1237,8 +910,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
cairo_matrix_t m;
/* SURFACE is the only type we'll handle here */
- if (apattern->type != CAIRO_PATTERN_TYPE_SURFACE)
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ assert (apattern->type == CAIRO_PATTERN_TYPE_SURFACE);
spattern = (cairo_surface_pattern_t *) apattern;
pat_surf = spattern->surface;
@@ -1246,14 +918,14 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
- status = _cairo_surface_to_cgimage ((cairo_surface_t*) dest, pat_surf, &image);
- if (status)
+ status = _cairo_surface_to_cgimage (pat_surf, &image);
+ if (unlikely (status))
return status;
- if (image == NULL)
+ if (unlikely (image == NULL))
return CAIRO_INT_STATUS_NOTHING_TO_DO;
- info = malloc(sizeof(SurfacePatternDrawInfo));
- if (!info)
+ info = malloc (sizeof (SurfacePatternDrawInfo));
+ if (unlikely (!info))
return CAIRO_STATUS_NO_MEMORY;
/* XXX -- if we're printing, we may need to call CGImageCreateCopy to make sure
@@ -1284,7 +956,7 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
rh = pbounds.size.height;
m = spattern->base.matrix;
- cairo_matrix_invert(&m);
+ cairo_matrix_invert (&m);
_cairo_quartz_cairo_matrix_to_quartz (&m, &stransform);
/* The pattern matrix is relative to the bottom left, again; the
@@ -1292,13 +964,13 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
* So we take the pattern matrix and the original context matrix,
* which gives us the correct base translation/y flip.
*/
- ptransform = CGAffineTransformConcat(stransform, dest->cgContextBaseCTM);
+ ptransform = CGAffineTransformConcat (stransform, dest->cgContextBaseCTM);
#ifdef QUARTZ_DEBUG
- ND((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
- ND((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
- CGAffineTransform xform = CGContextGetCTM(dest->cgContext);
- ND((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
+ ND ((stderr, " pbounds: %f %f %f %f\n", pbounds.origin.x, pbounds.origin.y, pbounds.size.width, pbounds.size.height));
+ ND ((stderr, " pattern xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", ptransform.tx, ptransform.ty, ptransform.a, ptransform.b, ptransform.c, ptransform.d));
+ CGAffineTransform xform = CGContextGetCTM (dest->cgContext);
+ ND ((stderr, " context xform: t: %f %f xx: %f xy: %f yx: %f yy: %f\n", xform.tx, xform.ty, xform.a, xform.b, xform.c, xform.d));
#endif
*cgpat = CGPatternCreate (info,
@@ -1312,86 +984,36 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t
return CAIRO_STATUS_SUCCESS;
}
-typedef enum {
- DO_SOLID,
- DO_SHADING,
- DO_PATTERN,
- DO_IMAGE,
- DO_UNSUPPORTED,
- DO_NOTHING,
- DO_TILED_IMAGE
-} cairo_quartz_action_t;
-
-static cairo_quartz_action_t
-_cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source)
-{
- CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext);
- double x0, y0, w, h;
-
- cairo_surface_t *fallback;
- CGImageRef img;
-
- cairo_status_t status;
-
- if (clipBox.size.width == 0.0f ||
- clipBox.size.height == 0.0f)
- return DO_NOTHING;
-
- x0 = floor(clipBox.origin.x);
- y0 = floor(clipBox.origin.y);
- w = ceil(clipBox.origin.x + clipBox.size.width) - x0;
- h = ceil(clipBox.origin.y + clipBox.size.height) - y0;
-
- /* Create a temporary the size of the clip surface, and position
- * it so that the device origin coincides with the original surface */
- fallback = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (int) w, (int) h);
- cairo_surface_set_device_offset (fallback, -x0, -y0);
+/* State used during a drawing operation. */
+typedef struct {
+ /* The destination of the mask */
+ CGContextRef cgMaskContext;
-#if 0
- {
- cairo_t *fallback_cr;
- cairo_pattern_t *source_copy;
+ /* The destination of the drawing of the source */
+ CGContextRef cgDrawContext;
- /* Paint the source onto our temporary */
- fallback_cr = cairo_create (fallback);
- cairo_set_operator (fallback_cr, CAIRO_OPERATOR_SOURCE);
+ /* The filter to be used when drawing the source */
+ CGInterpolationQuality filter;
- /* Use a copy of the pattern because it is const and could be allocated
- * on the stack */
- status = _cairo_pattern_create_copy (&source_copy, source);
- cairo_set_source (fallback_cr, source_copy);
- cairo_pattern_destroy (source_copy);
+ /* Action type */
+ cairo_quartz_action_t action;
- cairo_paint (fallback_cr);
- cairo_destroy (fallback_cr);
- }
-#else
- {
- cairo_pattern_union_t pattern;
+ /* Destination rect */
+ CGRect rect;
- _cairo_pattern_init_static_copy (&pattern.base, source);
- _cairo_pattern_transform (&pattern.base,
- &fallback->device_transform_inverse);
- status = _cairo_surface_paint (fallback,
- CAIRO_OPERATOR_SOURCE,
- &pattern.base, NULL);
- }
-#endif
+ /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */
+ CGAffineTransform transform;
- status = _cairo_surface_to_cgimage (&surface->base, fallback, &img);
- if (status)
- return DO_UNSUPPORTED;
- if (img == NULL)
- return DO_NOTHING;
+ /* Used with DO_IMAGE and DO_TILED_IMAGE */
+ CGImageRef image;
- surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h);
- surface->sourceImage = img;
- surface->sourceImageSurface = fallback;
- surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0);
+ /* Used with DO_SHADING */
+ CGShadingRef shading;
- return DO_IMAGE;
-}
+ /* Temporary destination for unbounded operations */
+ CGLayerRef layer;
+ CGRect clipRect;
+} cairo_quartz_drawing_state_t;
/*
Quartz does not support repeating radients. We handle repeating gradients
@@ -1400,181 +1022,170 @@ minimize the number of repetitions since Quartz seems to sample our color
function across the entire range, even if part of that range is not needed
for the visible area of the gradient, and it samples with some fixed resolution,
so if the gradient range is too large it samples with very low resolution and
-the gradient is very coarse. CreateRepeatingLinearGradientFunction and
-CreateRepeatingRadialGradientFunction compute the number of repetitions needed
-based on the extents of the object (the clip region cannot be used here since
-we don't want the rasterization of the entire gradient to depend on the
-clip region).
+the gradient is very coarse. _cairo_quartz_create_gradient_function computes
+the number of repetitions needed based on the extents.
*/
-static cairo_quartz_action_t
-_cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface,
- const cairo_linear_pattern_t *lpat,
- cairo_rectangle_int_t *extents)
+static cairo_int_status_t
+_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
+ const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents)
{
- const cairo_pattern_t *abspat = &lpat->base.base;
cairo_matrix_t mat;
- CGPoint start, end;
+ cairo_circle_double_t start, end;
CGFunctionRef gradFunc;
CGColorSpaceRef rgb;
- bool extend = abspat->extend == CAIRO_EXTEND_PAD;
+ bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
- if (lpat->base.n_stops == 0) {
- CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.);
- CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.);
- return DO_SOLID;
- }
+ assert (gradient->n_stops > 0);
- if (lpat->p1.x == lpat->p2.x &&
- lpat->p1.y == lpat->p2.y) {
- /* Quartz handles cases where the vector has no length very
- * differently from pixman.
- * 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);
- }
-
- mat = abspat->matrix;
+ mat = gradient->base.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();
+ gradFunc = _cairo_quartz_create_gradient_function (gradient,
+ extents,
+ &start,
+ &end);
- start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
- _cairo_fixed_to_double (lpat->p1.y));
- end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
- _cairo_fixed_to_double (lpat->p2.y));
+ if (unlikely (gradFunc == NULL))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
- if (abspat->extend == CAIRO_EXTEND_NONE ||
- abspat->extend == CAIRO_EXTEND_PAD)
- {
- gradFunc = CreateGradientFunction (&lpat->base);
+ rgb = CGColorSpaceCreateDeviceRGB ();
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ state->shading = CGShadingCreateAxial (rgb,
+ CGPointMake (start.center.x,
+ start.center.y),
+ CGPointMake (end.center.x,
+ end.center.y),
+ gradFunc,
+ extend, extend);
} else {
- gradFunc = CreateRepeatingLinearGradientFunction (surface,
- &lpat->base,
- &start, &end,
- extents);
+ state->shading = CGShadingCreateRadial (rgb,
+ CGPointMake (start.center.x,
+ start.center.y),
+ MAX (start.radius, 0),
+ CGPointMake (end.center.x,
+ end.center.y),
+ MAX (end.radius, 0),
+ gradFunc,
+ extend, extend);
}
- surface->sourceShading = CGShadingCreateAxial (rgb,
- start, end,
- gradFunc,
- extend, extend);
-
- CGColorSpaceRelease(rgb);
- CGFunctionRelease(gradFunc);
+ CGColorSpaceRelease (rgb);
+ CGFunctionRelease (gradFunc);
- return DO_SHADING;
+ state->action = DO_SHADING;
+ return CAIRO_STATUS_SUCCESS;
}
-static cairo_quartz_action_t
-_cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface,
- const cairo_radial_pattern_t *rpat,
- cairo_rectangle_int_t *extents)
+static cairo_int_status_t
+_cairo_quartz_setup_state (cairo_quartz_drawing_state_t *state,
+ cairo_quartz_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_clip_t *clip)
{
- const cairo_pattern_t *abspat = &rpat->base.base;
- cairo_matrix_t mat;
- CGPoint start, end;
- CGFunctionRef gradFunc;
- CGColorSpaceRef rgb;
- bool extend = abspat->extend == CAIRO_EXTEND_PAD;
- double c1x = _cairo_fixed_to_double (rpat->c1.x);
- double c1y = _cairo_fixed_to_double (rpat->c1.y);
- double c2x = _cairo_fixed_to_double (rpat->c2.x);
- double c2y = _cairo_fixed_to_double (rpat->c2.y);
- double r1 = _cairo_fixed_to_double (rpat->r1);
- double r2 = _cairo_fixed_to_double (rpat->r2);
- double dx = c1x - c2x;
- double dy = c1y - c2y;
- 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;
- }
-
- if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */
- r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */
- /* Quartz handles cases where neither circle contains the other very
- * differently from pixman.
- * Whatever the correct behaviour is, let's at least have only pixman's
- * implementation to worry about.
- * Note that this also catches the cases where r1 == r2.
- */
- return _cairo_quartz_setup_fallback_source (surface, abspat);
- }
+ cairo_bool_t needs_temp;
+ cairo_status_t status;
- mat = abspat->matrix;
- cairo_matrix_invert (&mat);
- _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform);
+ state->layer = NULL;
+ state->image = NULL;
+ state->shading = NULL;
+ state->cgDrawContext = NULL;
+ state->cgMaskContext = NULL;
- rgb = CGColorSpaceCreateDeviceRGB();
+ status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ if (unlikely (status))
+ return status;
- start = CGPointMake (c1x, c1y);
- end = CGPointMake (c2x, c2y);
+ status = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ if (unlikely (status))
+ return status;
- if (abspat->extend == CAIRO_EXTEND_NONE ||
- abspat->extend == CAIRO_EXTEND_PAD)
- {
- gradFunc = CreateGradientFunction (&rpat->base);
- } else {
- gradFunc = CreateRepeatingRadialGradientFunction (surface,
- &rpat->base,
- &start, &r1,
- &end, &r2,
- extents);
- }
+ /* 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)
+ */
- surface->sourceShading = CGShadingCreateRadial (rgb,
- start,
- r1,
- end,
- r2,
- gradFunc,
- extend, extend);
+ CGContextSaveGState (surface->cgContext);
+ state->clipRect = CGContextGetClipBoundingBox (surface->cgContext);
+ state->clipRect = CGRectIntegral (state->clipRect);
+ state->rect = state->clipRect;
- CGColorSpaceRelease(rgb);
- CGFunctionRelease(gradFunc);
+ state->cgMaskContext = surface->cgContext;
+ state->cgDrawContext = state->cgMaskContext;
- return DO_SHADING;
-}
+ state->filter = _cairo_quartz_filter_to_quartz (source->filter);
-static cairo_quartz_action_t
-_cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source,
- cairo_rectangle_int_t *extents)
-{
- assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern));
+ if (op == CAIRO_OPERATOR_CLEAR) {
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
- surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext);
- CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter));
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ /*
+ * To implement mask unbounded operations Quartz needs a temporary
+ * surface which will be composited entirely (ignoring the mask).
+ * To implement source unbounded operations Quartz needs a
+ * temporary surface which allows extending the source to a size
+ * covering the whole mask, but there are some optimization
+ * opportunities:
+ *
+ * - CLEAR completely ignores the source, thus we can just use a
+ * solid color fill.
+ *
+ * - SOURCE can be implemented by drawing the source and clearing
+ * outside of the source as long as the two regions have no
+ * intersection. This happens when the source is a pixel-aligned
+ * rectangle. If the source is at least as big as the
+ * intersection between the clip rectangle and the mask
+ * rectangle, no clear operation is needed.
+ */
+ needs_temp = ! _cairo_operator_bounded_by_mask (op);
+
+ if (needs_temp) {
+ state->layer = CGLayerCreateWithContext (surface->cgContext,
+ state->clipRect.size,
+ NULL);
+ state->cgDrawContext = CGLayerGetContext (state->layer);
+ state->cgMaskContext = state->cgDrawContext;
+ CGContextTranslateCTM (state->cgDrawContext,
+ -state->clipRect.origin.x,
+ -state->clipRect.origin.y);
+ }
if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source;
- CGContextSetRGBStrokeColor (surface->cgContext,
+ CGContextSetRGBStrokeColor (state->cgDrawContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
- CGContextSetRGBFillColor (surface->cgContext,
+ CGContextSetRGBFillColor (state->cgDrawContext,
solid->color.red,
solid->color.green,
solid->color.blue,
solid->color.alpha);
- return DO_SOLID;
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
}
- 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_LINEAR ||
+ source->type == CAIRO_PATTERN_TYPE_RADIAL)
+ {
+ const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
+ cairo_rectangle_int_t extents;
+
+ extents = surface->virtual_extents;
+ extents.x -= surface->base.device_transform.x0;
+ extents.y -= surface->base.device_transform.y0;
+ _cairo_rectangle_union (&extents, &surface->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);
+ return _cairo_quartz_setup_gradient_source (state, gpat, &extents);
}
if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
@@ -1585,33 +1196,57 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *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;
- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
- if (status)
- return DO_UNSUPPORTED;
- if (img == NULL)
- return DO_NOTHING;
+ status = _cairo_surface_to_cgimage (pat_surf, &img);
+ if (unlikely (status))
+ return status;
+ if (unlikely (img == NULL))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
- CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1);
+ state->image = img;
- surface->sourceImage = img;
+ if (state->filter == kCGInterpolationNone && _cairo_matrix_is_translation (&m)) {
+ m.x0 = -ceil (m.x0 - 0.5);
+ m.y0 = -ceil (m.y0 - 0.5);
+ } else {
+ cairo_matrix_invert (&m);
+ }
- cairo_matrix_invert(&m);
- _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform);
+ _cairo_quartz_cairo_matrix_to_quartz (&m, &state->transform);
is_bounded = _cairo_surface_get_extents (pat_surf, &extents);
assert (is_bounded);
+ srcRect = CGRectMake (0, 0, extents.width, extents.height);
+
if (source->extend == CAIRO_EXTEND_NONE) {
- surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height);
- return DO_IMAGE;
+ int x, y;
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ (pat_surf->content == CAIRO_CONTENT_ALPHA ||
+ ! _cairo_matrix_is_integer_translation (&m, &x, &y)))
+ {
+ state->layer = CGLayerCreateWithContext (surface->cgContext,
+ state->clipRect.size,
+ NULL);
+ state->cgDrawContext = CGLayerGetContext (state->layer);
+ CGContextTranslateCTM (state->cgDrawContext,
+ -state->clipRect.origin.x,
+ -state->clipRect.origin.y);
+ }
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
+ state->rect = srcRect;
+ state->action = DO_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
}
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1);
+
/* Quartz seems to tile images at pixel-aligned regions only -- this
* leads to seams if the image doesn't end up scaling to fill the
* space exactly. The CGPattern tiling approach doesn't have this
@@ -1619,10 +1254,9 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
* epsilon), and if not, fall back to the CGPattern type.
*/
- xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext),
- surface->sourceTransform);
+ xform = CGAffineTransformConcat (CGContextGetCTM (state->cgDrawContext),
+ state->transform);
- srcRect = CGRectMake (0, 0, extents.width, extents.height);
srcRect = CGRectApplyAffineTransform (srcRect, xform);
fw = _cairo_fixed_from_double (srcRect.size.width);
@@ -1634,16 +1268,16 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
/* We're good to use DrawTiledImage, but ensure that
* the math works out */
- srcRect.size.width = round(srcRect.size.width);
- srcRect.size.height = round(srcRect.size.height);
+ srcRect.size.width = round (srcRect.size.width);
+ srcRect.size.height = round (srcRect.size.height);
xform = CGAffineTransformInvert (xform);
srcRect = CGRectApplyAffineTransform (srcRect, xform);
- surface->sourceImageRect = srcRect;
-
- return DO_TILED_IMAGE;
+ state->rect = srcRect;
+ state->action = DO_TILED_IMAGE;
+ return CAIRO_STATUS_SUCCESS;
}
/* Fall through to generic SURFACE case */
@@ -1652,90 +1286,100 @@ _cairo_quartz_setup_source (cairo_quartz_surface_t *surface,
if (source->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_quartz_float_t patternAlpha = 1.0f;
CGColorSpaceRef patternSpace;
- CGPatternRef pattern;
+ CGPatternRef pattern = NULL;
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);
+ if (unlikely (status))
+ return status;
+
+ patternSpace = CGColorSpaceCreatePattern (NULL);
+ CGContextSetFillColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetFillPattern (state->cgDrawContext, pattern, &patternAlpha);
+ CGContextSetStrokeColorSpace (state->cgDrawContext, patternSpace);
+ CGContextSetStrokePattern (state->cgDrawContext, 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));
+ CGContextSetPatternPhase (state->cgDrawContext, CGSizeMake (0, 0));
- surface->sourcePattern = pattern;
+ CGPatternRelease (pattern);
- return DO_PATTERN;
+ state->action = DO_DIRECT;
+ return CAIRO_STATUS_SUCCESS;
}
- return DO_UNSUPPORTED;
+ return CAIRO_INT_STATUS_UNSUPPORTED;
}
static void
-_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface,
- const cairo_pattern_t *source)
+_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state,
+ cairo_quartz_surface_t *surface)
{
- CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality);
+ if (state->layer) {
+ CGContextDrawLayerInRect (surface->cgContext,
+ state->clipRect,
+ state->layer);
+ CGContextRelease (state->cgDrawContext);
+ CGLayerRelease (state->layer);
+ }
- if (surface->sourceImage) {
- CGImageRelease(surface->sourceImage);
- surface->sourceImage = NULL;
+ if (state->cgMaskContext)
+ CGContextRestoreGState (surface->cgContext);
- cairo_surface_destroy(surface->sourceImageSurface);
- surface->sourceImageSurface = NULL;
- }
+ if (state->image)
+ CGImageRelease (state->image);
- if (surface->sourceShading) {
- CGShadingRelease(surface->sourceShading);
- surface->sourceShading = NULL;
+ if (state->shading)
+ CGShadingRelease (state->shading);
+}
+
+static void
+_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state,
+ cairo_operator_t op)
+{
+ CGContextSetShouldAntialias (state->cgDrawContext, state->filter != kCGInterpolationNone);
+ CGContextSetInterpolationQuality(state->cgDrawContext, state->filter);
+
+ if (state->action == DO_DIRECT) {
+ CGContextFillRect (state->cgDrawContext, state->rect);
+ return;
}
- if (surface->sourcePattern) {
- CGPatternRelease(surface->sourcePattern);
- // To tear down the pattern and colorspace
- CGContextRestoreGState(surface->cgContext);
+ CGContextConcatCTM (state->cgDrawContext, state->transform);
- surface->sourcePattern = NULL;
+ if (state->action == DO_SHADING) {
+ CGContextDrawShading (state->cgDrawContext, state->shading);
+ return;
}
-}
+ CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+ CGContextScaleCTM (state->cgDrawContext, 1, -1);
-static void
-_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action)
-{
- assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_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->action == DO_IMAGE) {
+ CGContextDrawImage (state->cgDrawContext, state->rect, state->image);
+ if (op == CAIRO_OPERATOR_SOURCE &&
+ state->cgDrawContext == state->cgMaskContext)
+ {
+ CGContextBeginPath (state->cgDrawContext);
+ CGContextAddRect (state->cgDrawContext, state->rect);
+
+ CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height);
+ CGContextScaleCTM (state->cgDrawContext, 1, -1);
+ CGContextConcatCTM (state->cgDrawContext,
+ CGAffineTransformInvert (state->transform));
+
+ CGContextAddRect (state->cgDrawContext, state->clipRect);
+
+ CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 0);
+ CGContextEOFillPath (state->cgDrawContext);
}
- } else
- CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage);
+ } else {
+ CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image);
+ }
}
@@ -1751,37 +1395,33 @@ _cairo_quartz_get_image (cairo_quartz_surface_t *surface,
unsigned char *imageData;
cairo_image_surface_t *isurf;
- if (IS_EMPTY(surface)) {
+ if (IS_EMPTY (surface)) {
*image_out = (cairo_image_surface_t*) cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0, 0);
return CAIRO_STATUS_SUCCESS;
}
if (surface->imageSurfaceEquiv) {
- *image_out = (cairo_image_surface_t*) cairo_surface_reference(surface->imageSurfaceEquiv);
+ *image_out = (cairo_image_surface_t*) cairo_surface_reference (surface->imageSurfaceEquiv);
return CAIRO_STATUS_SUCCESS;
}
- if (_cairo_quartz_is_cgcontext_bitmap_context(surface->cgContext)) {
+ if (_cairo_quartz_is_cgcontext_bitmap_context (surface->cgContext)) {
unsigned int stride;
unsigned int bitinfo;
unsigned int bpc, bpp;
CGColorSpaceRef colorspace;
unsigned int color_comps;
- imageData = (unsigned char *) CGBitmapContextGetData(surface->cgContext);
+ imageData = (unsigned char *) CGBitmapContextGetData (surface->cgContext);
-#ifdef USE_10_3_WORKAROUNDS
- bitinfo = CGBitmapContextGetAlphaInfo (surface->cgContext);
-#else
bitinfo = CGBitmapContextGetBitmapInfo (surface->cgContext);
-#endif
stride = CGBitmapContextGetBytesPerRow (surface->cgContext);
bpp = CGBitmapContextGetBitsPerPixel (surface->cgContext);
bpc = CGBitmapContextGetBitsPerComponent (surface->cgContext);
// let's hope they don't add YUV under us
colorspace = CGBitmapContextGetColorSpace (surface->cgContext);
- color_comps = CGColorSpaceGetNumberOfComponents(colorspace);
+ color_comps = CGColorSpaceGetNumberOfComponents (colorspace);
// XXX TODO: We can handle all of these by converting to
// pixman masks, including non-native-endian masks
@@ -1842,9 +1482,9 @@ _cairo_quartz_surface_finish (void *abstract_surface)
{
cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- ND((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
+ ND ((stderr, "_cairo_quartz_surface_finish[%p] cgc: %p\n", surface, surface->cgContext));
- if (IS_EMPTY(surface))
+ if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
/* Restore our saved gstate that we use to reset clipping */
@@ -1876,10 +1516,10 @@ _cairo_quartz_surface_acquire_source_image (void *abstract_surface,
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));
+ //ND ((stderr, "%p _cairo_quartz_surface_acquire_source_image\n", surface));
status = _cairo_quartz_get_image (surface, image_out);
- if (status)
+ if (unlikely (status))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
*image_extra = NULL;
@@ -1906,10 +1546,10 @@ _cairo_quartz_surface_snapshot (void *abstract_surface)
static void
_cairo_quartz_surface_release_source_image (void *abstract_surface,
- cairo_image_surface_t *image,
- void *image_extra)
+ cairo_image_surface_t *image,
+ void *image_extra)
{
- cairo_surface_destroy ((cairo_surface_t *) image);
+ cairo_surface_destroy (&image->base);
}
@@ -1923,10 +1563,10 @@ _cairo_quartz_surface_acquire_dest_image (void *abstract_surface,
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));
+ ND ((stderr, "%p _cairo_quartz_surface_acquire_dest_image\n", surface));
status = _cairo_quartz_get_image (surface, image_out);
- if (status)
+ if (unlikely (status))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
*image_rect = surface->extents;
@@ -1944,19 +1584,19 @@ _cairo_quartz_surface_release_dest_image (void *abstract_surface,
{
//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);
+ cairo_surface_destroy (&image->base);
}
static cairo_surface_t *
_cairo_quartz_surface_create_similar (void *abstract_surface,
- cairo_content_t content,
- int width,
- int height)
+ cairo_content_t content,
+ int width,
+ int height)
{
- /*cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;*/
-
+ cairo_quartz_surface_t *surface, *similar_quartz;
+ cairo_surface_t *similar;
cairo_format_t format;
if (content == CAIRO_CONTENT_COLOR_ALPHA)
@@ -1969,12 +1609,20 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
return NULL;
// verify width and height of surface
- if (!_cairo_quartz_verify_surface_size(width, height)) {
+ if (!_cairo_quartz_verify_surface_size (width, height)) {
return _cairo_surface_create_in_error (_cairo_error
(CAIRO_STATUS_INVALID_SIZE));
}
- return cairo_quartz_surface_create (format, width, height);
+ similar = cairo_quartz_surface_create (format, width, height);
+ if (unlikely (similar->status))
+ return similar;
+
+ surface = (cairo_quartz_surface_t *) abstract_surface;
+ similar_quartz = (cairo_quartz_surface_t *) similar;
+ similar_quartz->virtual_extents = surface->virtual_extents;
+
+ return similar;
}
static cairo_status_t
@@ -1996,14 +1644,12 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
*clone_out = NULL;
// verify width and height of surface
- if (!_cairo_quartz_verify_surface_size(width, height)) {
+ if (!_cairo_quartz_verify_surface_size (width, height))
return CAIRO_INT_STATUS_UNSUPPORTED;
- }
if (width == 0 || height == 0) {
- *clone_out = (cairo_surface_t*)
- _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
- width, height);
+ *clone_out = &_cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
+ width, height)->base;
*clone_offset_x = 0;
*clone_offset_y = 0;
return CAIRO_STATUS_SUCCESS;
@@ -2012,24 +1658,24 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
if (_cairo_surface_is_quartz (src)) {
cairo_quartz_surface_t *qsurf = (cairo_quartz_surface_t *) src;
- if (IS_EMPTY(qsurf)) {
- *clone_out = (cairo_surface_t*)
- _cairo_quartz_surface_create_internal (NULL, CAIRO_CONTENT_COLOR_ALPHA,
- qsurf->extents.width, qsurf->extents.height);
+ if (IS_EMPTY (qsurf)) {
+ *clone_out = &_cairo_quartz_surface_create_internal (NULL,
+ CAIRO_CONTENT_COLOR_ALPHA,
+ qsurf->extents.width,
+ qsurf->extents.height)->base;
*clone_offset_x = 0;
*clone_offset_y = 0;
return CAIRO_STATUS_SUCCESS;
}
}
- status = _cairo_surface_to_cgimage ((cairo_surface_t*) abstract_surface, src, &quartz_image);
- if (status)
+ status = _cairo_surface_to_cgimage (src, &quartz_image);
+ if (unlikely (status))
return CAIRO_INT_STATUS_UNSUPPORTED;
new_format = CAIRO_FORMAT_ARGB32; /* assumed */
- if (_cairo_surface_is_image (src)) {
+ if (_cairo_surface_is_image (src))
new_format = ((cairo_image_surface_t *) src)->format;
- }
new_surface = (cairo_quartz_surface_t *)
cairo_quartz_surface_create (new_format, width, height);
@@ -2044,12 +1690,11 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
CGContextSaveGState (new_surface->cgContext);
- CGContextSetCompositeOperation (new_surface->cgContext,
- kPrivateCGCompositeCopy);
+ _cairo_quartz_surface_set_cairo_operator (new_surface, CAIRO_OPERATOR_SOURCE);
CGContextTranslateCTM (new_surface->cgContext, -src_x, -src_y);
CGContextDrawImage (new_surface->cgContext,
- CGRectMake (0, 0, CGImageGetWidth(quartz_image), CGImageGetHeight(quartz_image)),
+ CGRectMake (0, 0, CGImageGetWidth (quartz_image), CGImageGetHeight (quartz_image)),
quartz_image);
CGContextRestoreGState (new_surface->cgContext);
@@ -2059,7 +1704,7 @@ _cairo_quartz_surface_clone_similar (void *abstract_surface,
FINISH:
*clone_offset_x = src_x;
*clone_offset_y = src_y;
- *clone_out = (cairo_surface_t*) new_surface;
+ *clone_out = &new_surface->base;
return CAIRO_STATUS_SUCCESS;
}
@@ -2075,73 +1720,32 @@ _cairo_quartz_surface_get_extents (void *abstract_surface,
}
static cairo_int_status_t
-_cairo_quartz_surface_paint_cg (void *abstract_surface,
+_cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_clip_t *clip)
{
- cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
- ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
+ ND ((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type));
- if (IS_EMPTY(surface))
- return CAIRO_STATUS_SUCCESS;
-
- rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
- if (unlikely (rv))
- return rv;
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ rv = _cairo_quartz_setup_state (&state, surface, op, source, clip);
if (unlikely (rv))
- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+ goto BAIL;
- action = _cairo_quartz_setup_source (surface, source, NULL);
+ _cairo_quartz_draw_source (&state, op);
- 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) {
- rv = CAIRO_INT_STATUS_UNSUPPORTED;
- }
-
- _cairo_quartz_teardown_source (surface, source);
+BAIL:
+ _cairo_quartz_teardown_state (&state, surface);
- ND((stderr, "-- paint\n"));
+ ND ((stderr, "-- paint\n"));
return rv;
}
-static cairo_bool_t
-_cairo_quartz_source_needs_extents (const cairo_pattern_t *source)
-{
- /* For repeating gradients we need to manually extend the gradient and
- repeat stops, since Quartz doesn't support repeating gradients natively.
- We need to minimze the number of repeated stops, and since rasterization
- depends on the number of repetitions we use (even if some of the
- repetitions go beyond the extents of the object or outside the clip
- region), it's important to use the same number of repetitions when
- rendering an object no matter what the clip region is. So the
- computation of the repetition count cannot depended on the clip region,
- and should only depend on the object extents, so we need to compute
- the object extents for repeating gradients. */
- return (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
- source->type == CAIRO_PATTERN_TYPE_RADIAL) &&
- (source->extend == CAIRO_EXTEND_REPEAT ||
- source->extend == CAIRO_EXTEND_REFLECT);
-}
-
static cairo_int_status_t
_cairo_quartz_surface_paint (void *abstract_surface,
cairo_operator_t op,
@@ -2152,7 +1756,7 @@ _cairo_quartz_surface_paint (void *abstract_surface,
cairo_int_status_t rv;
cairo_image_surface_t *image;
- rv = _cairo_quartz_surface_paint_cg (abstract_surface,
+ rv = _cairo_quartz_surface_paint_cg (surface,
op,
source,
clip);
@@ -2161,7 +1765,7 @@ _cairo_quartz_surface_paint (void *abstract_surface,
return rv;
rv = _cairo_quartz_get_image (surface, &image);
- if (rv == CAIRO_STATUS_SUCCESS) {
+ if (likely (rv == CAIRO_STATUS_SUCCESS)) {
rv = _cairo_surface_paint (&image->base, op, source, clip);
cairo_surface_destroy (&image->base);
}
@@ -2170,7 +1774,7 @@ _cairo_quartz_surface_paint (void *abstract_surface,
}
static cairo_int_status_t
-_cairo_quartz_surface_fill_cg (void *abstract_surface,
+_cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
@@ -2179,87 +1783,41 @@ _cairo_quartz_surface_fill_cg (void *abstract_surface,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
- cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
- CGPathRef path_for_unbounded = NULL;
-
- ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
- if (IS_EMPTY(surface))
- return CAIRO_STATUS_SUCCESS;
+ ND ((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type));
- rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
- if (unlikely (rv))
- return rv;
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ rv = _cairo_quartz_setup_state (&state, surface, op, source, clip);
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
- compute the number of gradient reptitions needed to cover the
- 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);
- } else {
- action = _cairo_quartz_setup_source (surface, source, NULL);
- }
-
- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
+ goto BAIL;
- if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr)
- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+ CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
- if (action == DO_SOLID || action == DO_PATTERN) {
- if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextFillPath (surface->cgContext);
- else
- CGContextEOFillPath (surface->cgContext);
- } else if (action == DO_SHADING) {
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
- // we have to clip and then paint the shading; we can't fill
- // with the shading
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextClip (surface->cgContext);
+ CGContextFillPath (state.cgMaskContext);
else
- CGContextEOClip (surface->cgContext);
-
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
- } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) {
+ CGContextEOFillPath (state.cgMaskContext);
+ } else {
if (fill_rule == CAIRO_FILL_RULE_WINDING)
- CGContextClip (surface->cgContext);
+ CGContextClip (state.cgMaskContext);
else
- CGContextEOClip (surface->cgContext);
+ CGContextEOClip (state.cgMaskContext);
- _cairo_quartz_draw_image (surface, op, action);
- } else if (action != DO_NOTHING) {
- rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ _cairo_quartz_draw_source (&state, op);
}
- _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.cgPath = path_for_unbounded;
- ub.u.stroke_fill.fill_rule = fill_rule;
-
- _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
- CGPathRelease (path_for_unbounded);
- }
+BAIL:
+ _cairo_quartz_teardown_state (&state, surface);
- ND((stderr, "-- fill\n"));
+ ND ((stderr, "-- fill\n"));
return rv;
}
@@ -2277,7 +1835,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
cairo_int_status_t rv;
cairo_image_surface_t *image;
- rv = _cairo_quartz_surface_fill_cg (abstract_surface,
+ rv = _cairo_quartz_surface_fill_cg (surface,
op,
source,
path,
@@ -2290,7 +1848,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
return rv;
rv = _cairo_quartz_get_image (surface, &image);
- if (rv == CAIRO_STATUS_SUCCESS) {
+ if (likely (rv == CAIRO_STATUS_SUCCESS)) {
rv = _cairo_surface_fill (&image->base, op, source,
path, fill_rule, tolerance, antialias,
clip);
@@ -2301,7 +1859,7 @@ _cairo_quartz_surface_fill (void *abstract_surface,
}
static cairo_int_status_t
-_cairo_quartz_surface_stroke_cg (void *abstract_surface,
+_cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_path_fixed_t *path,
@@ -2312,124 +1870,74 @@ _cairo_quartz_surface_stroke_cg (void *abstract_surface,
cairo_antialias_t antialias,
cairo_clip_t *clip)
{
- cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
- CGAffineTransform origCTM, strokeTransform;
- CGPathRef path_for_unbounded = NULL;
-
- ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
+ CGAffineTransform strokeTransform, invStrokeTransform;
- if (IS_EMPTY(surface))
- return CAIRO_STATUS_SUCCESS;
+ ND ((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type));
- rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
- if (unlikely (rv))
- return rv;
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
- rv = _cairo_quartz_surface_set_cairo_operator (surface, op);
+ rv = _cairo_quartz_setup_state (&state, surface, op, source, clip);
if (unlikely (rv))
- return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv;
+ goto BAIL;
// 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);
-
- origCTM = CGContextGetCTM (surface->cgContext);
+ CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE));
+ CGContextSetLineWidth (state.cgMaskContext, style->line_width);
+ CGContextSetLineCap (state.cgMaskContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap));
+ CGContextSetLineJoin (state.cgMaskContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join));
+ CGContextSetMiterLimit (state.cgMaskContext, style->miter_limit);
if (style->dash && style->num_dashes) {
-#define STATIC_DASH 32
- cairo_quartz_float_t sdash[STATIC_DASH];
+ cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)];
cairo_quartz_float_t *fdash = sdash;
unsigned int max_dashes = style->num_dashes;
unsigned int k;
if (style->num_dashes%2)
max_dashes *= 2;
- if (max_dashes > STATIC_DASH)
+ if (max_dashes > ARRAY_LENGTH (sdash))
fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t));
- if (fdash == NULL)
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ if (unlikely (fdash == NULL)) {
+ rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ goto BAIL;
+ }
for (k = 0; k < max_dashes; k++)
fdash[k] = (cairo_quartz_float_t) style->dash[k % style->num_dashes];
- CGContextSetLineDash (surface->cgContext, style->dash_offset, fdash, max_dashes);
+ CGContextSetLineDash (state.cgMaskContext, style->dash_offset, fdash, max_dashes);
if (fdash != sdash)
free (fdash);
} else
- CGContextSetLineDash (surface->cgContext, 0, NULL, 0);
-
- CGContextSaveGState (surface->cgContext);
-
-
- 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);
- }
+ CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0);
- _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext);
-
- if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr)
- path_for_unbounded = CGContextCopyPathPtr (surface->cgContext);
+ _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext);
_cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform);
- CGContextConcatCTM (surface->cgContext, strokeTransform);
-
- 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);
+ CGContextConcatCTM (state.cgMaskContext, strokeTransform);
- CGContextSetCTM (surface->cgContext, origCTM);
- _cairo_quartz_draw_image (surface, op, action);
- } else if (action == DO_SHADING) {
- CGContextReplacePathWithStrokedPath (surface->cgContext);
- CGContextClip (surface->cgContext);
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
+ CGContextStrokePath (state.cgMaskContext);
+ } else {
+ CGContextReplacePathWithStrokedPath (state.cgMaskContext);
+ CGContextClip (state.cgMaskContext);
- CGContextSetCTM (surface->cgContext, origCTM);
+ _cairo_quartz_cairo_matrix_to_quartz (ctm_inverse, &invStrokeTransform);
+ CGContextConcatCTM (state.cgMaskContext, invStrokeTransform);
- CGContextConcatCTM (surface->cgContext, surface->sourceTransform);
- CGContextDrawShading (surface->cgContext, surface->sourceShading);
- } else if (action != DO_NOTHING) {
- rv = CAIRO_INT_STATUS_UNSUPPORTED;
+ _cairo_quartz_draw_source (&state, op);
}
- _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);
- CGPathRelease (path_for_unbounded);
-
- CGContextSaveGState (surface->cgContext);
- CGContextConcatCTM (surface->cgContext, strokeTransform);
- CGContextReplacePathWithStrokedPath (surface->cgContext);
- CGContextRestoreGState (surface->cgContext);
-
- ub.u.stroke_fill.cgPath = CGContextCopyPathPtr (surface->cgContext);
-
- _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias);
- CGPathRelease (ub.u.stroke_fill.cgPath);
- }
+BAIL:
+ _cairo_quartz_teardown_state (&state, surface);
- ND((stderr, "-- stroke\n"));
+ ND ((stderr, "-- stroke\n"));
return rv;
}
@@ -2449,7 +1957,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
cairo_int_status_t rv;
cairo_image_surface_t *image;
- rv = _cairo_quartz_surface_stroke_cg (abstract_surface, op, source,
+ rv = _cairo_quartz_surface_stroke_cg (surface, op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias,
clip);
@@ -2458,7 +1966,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
return rv;
rv = _cairo_quartz_get_image (surface, &image);
- if (rv == CAIRO_STATUS_SUCCESS) {
+ if (likely (rv == CAIRO_STATUS_SUCCESS)) {
rv = _cairo_surface_stroke (&image->base, op, source,
path, style, ctm, ctm_inverse,
tolerance, antialias,
@@ -2471,7 +1979,7 @@ _cairo_quartz_surface_stroke (void *abstract_surface,
#if CAIRO_HAS_QUARTZ_FONT
static cairo_int_status_t
-_cairo_quartz_surface_show_glyphs_cg (void *abstract_surface,
+_cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
cairo_glyph_t *glyphs,
@@ -2480,113 +1988,93 @@ _cairo_quartz_surface_show_glyphs_cg (void *abstract_surface,
cairo_clip_t *clip,
int *remaining_glyphs)
{
- CGAffineTransform textTransform, ctm, invTextTransform;
-#define STATIC_BUF_SIZE 64
- CGGlyph glyphs_static[STATIC_BUF_SIZE];
- CGSize cg_advances_static[STATIC_BUF_SIZE];
+ CGAffineTransform textTransform, invTextTransform;
+ CGGlyph glyphs_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
+ CGSize cg_advances_static[CAIRO_STACK_ARRAY_LENGTH (CGSize)];
CGGlyph *cg_glyphs = &glyphs_static[0];
CGSize *cg_advances = &cg_advances_static[0];
+ COMPILE_TIME_ASSERT (sizeof (CGGlyph) <= sizeof (CGSize));
- cairo_rectangle_int_t glyph_extents;
- cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
+ cairo_quartz_drawing_state_t state;
cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
- cairo_quartz_action_t action;
cairo_quartz_float_t xprev, yprev;
int i;
CGFontRef cgfref = NULL;
- cairo_bool_t isClipping = FALSE;
cairo_bool_t didForceFontSmoothing = FALSE;
- if (IS_EMPTY(surface))
- return CAIRO_STATUS_SUCCESS;
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
if (num_glyphs <= 0)
- return CAIRO_STATUS_SUCCESS;
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ)
return CAIRO_INT_STATUS_UNSUPPORTED;
- rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+ rv = _cairo_quartz_setup_state (&state, surface, op, source, clip);
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);
+ goto BAIL;
- 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);
+ if (state.action == DO_DIRECT) {
+ assert (state.cgDrawContext == state.cgMaskContext);
+ CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill);
} else {
- action = _cairo_quartz_setup_source (surface, source, 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);
- isClipping = TRUE;
- } else {
- if (action != DO_NOTHING)
- rv = CAIRO_INT_STATUS_UNSUPPORTED;
- goto BAIL;
+ CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextClip);
}
/* 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.cgMaskContext, cgfref);
+ CGContextSetFontSize (state.cgMaskContext, 1.0);
switch (scaled_font->options.antialias) {
case CAIRO_ANTIALIAS_SUBPIXEL:
- CGContextSetShouldAntialias (surface->cgContext, TRUE);
- CGContextSetShouldSmoothFonts (surface->cgContext, TRUE);
+ CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+ CGContextSetShouldSmoothFonts (state.cgMaskContext, TRUE);
if (CGContextSetAllowsFontSmoothingPtr &&
- !CGContextGetAllowsFontSmoothingPtr (surface->cgContext))
+ !CGContextGetAllowsFontSmoothingPtr (state.cgMaskContext))
{
didForceFontSmoothing = TRUE;
- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE);
+ CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, TRUE);
}
break;
case CAIRO_ANTIALIAS_NONE:
- CGContextSetShouldAntialias (surface->cgContext, FALSE);
+ CGContextSetShouldAntialias (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_GRAY:
- CGContextSetShouldAntialias (surface->cgContext, TRUE);
- CGContextSetShouldSmoothFonts (surface->cgContext, FALSE);
+ CGContextSetShouldAntialias (state.cgMaskContext, TRUE);
+ CGContextSetShouldSmoothFonts (state.cgMaskContext, FALSE);
break;
case CAIRO_ANTIALIAS_DEFAULT:
/* Don't do anything */
break;
}
- if (num_glyphs > STATIC_BUF_SIZE) {
- cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph));
- if (cg_glyphs == NULL) {
+ if (num_glyphs > ARRAY_LENGTH (glyphs_static)) {
+ cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof (CGGlyph) + sizeof (CGSize));
+ if (unlikely (cg_glyphs == NULL)) {
rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
goto BAIL;
}
- cg_advances = (CGSize*) _cairo_malloc_ab (num_glyphs, sizeof(CGSize));
- if (cg_advances == NULL) {
- rv = _cairo_error (CAIRO_STATUS_NO_MEMORY);
- goto BAIL;
- }
+ cg_advances = (CGSize*) (cg_glyphs + num_glyphs);
}
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);
+ scaled_font->scale.xy,
+ scaled_font->scale.yy,
+ 0.0, 0.0);
- CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity);
+ 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);
+
+ CGContextSetTextPosition (state.cgMaskContext, 0.0, 0.0);
+ CGContextSetTextMatrix (state.cgMaskContext, CGAffineTransformIdentity);
/* Convert our glyph positions to glyph advances. We need n-1 advances,
* since the advance at index 0 is applied after glyph 0. */
@@ -2599,64 +2087,36 @@ _cairo_quartz_surface_show_glyphs_cg (void *abstract_surface,
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);
+ cg_advances[i - 1] = CGSizeApplyAffineTransform (CGSizeMake (xf - xprev, yf - yprev), invTextTransform);
xprev = xf;
yprev = yf;
}
/* 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);
+ CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y);
+ CGContextConcatCTM (state.cgMaskContext, textTransform);
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
- CGContextShowGlyphsWithAdvances (surface->cgContext,
+ CGContextShowGlyphsWithAdvances (state.cgMaskContext,
cg_glyphs,
cg_advances,
num_glyphs);
- CGContextSetCTM (surface->cgContext, ctm);
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+ CGContextConcatCTM (state.cgMaskContext, invTextTransform);
+ CGContextTranslateCTM (state.cgMaskContext, -glyphs[0].x, -glyphs[0].y);
- 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);
- }
+ if (state.action != DO_DIRECT)
+ _cairo_quartz_draw_source (&state, op);
BAIL:
- _cairo_quartz_teardown_source (surface, source);
-
if (didForceFontSmoothing)
- CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE);
-
- CGContextRestoreGState (surface->cgContext);
-
- if (rv == CAIRO_STATUS_SUCCESS &&
- cgfref &&
- !_cairo_operator_bounded_by_mask (op))
- {
- unbounded_op_data_t ub;
- ub.op = UNBOUNDED_SHOW_GLYPHS;
-
- ub.u.show_glyphs.isClipping = isClipping;
- ub.u.show_glyphs.cg_glyphs = cg_glyphs;
- ub.u.show_glyphs.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.origin = CGPointMake (glyphs[0].x, glyphs[0].y);
-
- _cairo_quartz_fixup_unbounded_operation (surface, &ub, scaled_font->options.antialias);
- }
+ CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE);
+ _cairo_quartz_teardown_state (&state, surface);
- if (cg_advances != &cg_advances_static[0]) {
- free (cg_advances);
- }
-
- if (cg_glyphs != &glyphs_static[0]) {
+ if (cg_glyphs != glyphs_static)
free (cg_glyphs);
- }
return rv;
}
@@ -2677,7 +2137,7 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
cairo_image_surface_t *image;
#if CAIRO_HAS_QUARTZ_FONT
- rv = _cairo_quartz_surface_show_glyphs_cg (abstract_surface, op, source,
+ rv = _cairo_quartz_surface_show_glyphs_cg (surface, op, source,
glyphs, num_glyphs,
scaled_font, clip, remaining_glyphs);
@@ -2687,7 +2147,7 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
#endif
rv = _cairo_quartz_get_image (surface, &image);
- if (rv == CAIRO_STATUS_SUCCESS) {
+ if (likely (rv == CAIRO_STATUS_SUCCESS)) {
rv = _cairo_surface_show_text_glyphs (&image->base, op, source,
NULL, 0,
glyphs, num_glyphs,
@@ -2701,148 +2161,186 @@ _cairo_quartz_surface_show_glyphs (void *abstract_surface,
static cairo_int_status_t
_cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_surface_pattern_t *mask,
- cairo_clip_t *clip)
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ cairo_surface_t *mask_surf,
+ const cairo_matrix_t *mask_mat,
+ CGInterpolationQuality filter,
+ cairo_clip_t *clip)
{
CGRect rect;
CGImageRef img;
- cairo_surface_t *pat_surf = mask->surface;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
- CGAffineTransform ctm, mask_matrix;
+ CGAffineTransform mask_matrix;
+ cairo_quartz_drawing_state_t state;
- status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img);
- if (status)
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+ status = _cairo_surface_to_cgimage (mask_surf, &img);
+ if (unlikely (status))
return status;
- if (img == NULL) {
- if (!_cairo_operator_bounded_by_mask (op))
- CGContextClearRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext));
- return CAIRO_STATUS_SUCCESS;
- }
- rect = CGRectMake (0.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img));
+ status = _cairo_quartz_setup_state (&state, surface, op, source, clip);
+ if (unlikely (status))
+ goto BAIL;
- CGContextSaveGState (surface->cgContext);
+ if (unlikely (img == NULL))
+ goto BAIL;
+
+ rect = CGRectMake (0.0, 0.0, CGImageGetWidth (img), CGImageGetHeight (img));
+ _cairo_quartz_cairo_matrix_to_quartz (mask_mat, &mask_matrix);
/* ClipToMask is essentially drawing an image, so we need to flip the CTM
* to get the image to appear oriented the right way */
- ctm = CGContextGetCTM (surface->cgContext);
+ CGContextConcatCTM (state.cgMaskContext, CGAffineTransformInvert (mask_matrix));
+ CGContextTranslateCTM (state.cgMaskContext, 0.0, rect.size.height);
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
- _cairo_quartz_cairo_matrix_to_quartz (&mask->base.matrix, &mask_matrix);
- mask_matrix = CGAffineTransformInvert(mask_matrix);
- mask_matrix = CGAffineTransformTranslate (mask_matrix, 0.0, CGImageGetHeight (img));
- mask_matrix = CGAffineTransformScale (mask_matrix, 1.0, -1.0);
+ state.filter = filter;
- CGContextConcatCTM (surface->cgContext, mask_matrix);
- CGContextClipToMaskPtr (surface->cgContext, rect, img);
+ CGContextSetInterpolationQuality (state.cgMaskContext, filter);
+ CGContextSetShouldAntialias (state.cgMaskContext, filter != kCGInterpolationNone);
- CGContextSetCTM (surface->cgContext, ctm);
+ CGContextClipToMask (state.cgMaskContext, rect, img);
- status = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
+ CGContextScaleCTM (state.cgMaskContext, 1.0, -1.0);
+ CGContextTranslateCTM (state.cgMaskContext, 0.0, -rect.size.height);
+ CGContextConcatCTM (state.cgMaskContext, mask_matrix);
- CGContextRestoreGState (surface->cgContext);
+ _cairo_quartz_draw_source (&state, op);
- if (!_cairo_operator_bounded_by_mask (op)) {
- unbounded_op_data_t ub;
- ub.op = UNBOUNDED_MASK;
- ub.u.mask.mask = img;
- ub.u.mask.maskTransform = mask_matrix;
- _cairo_quartz_fixup_unbounded_operation (surface, &ub, CAIRO_ANTIALIAS_NONE);
- }
+BAIL:
+ _cairo_quartz_teardown_state (&state, surface);
CGImageRelease (img);
return status;
}
-/* This is somewhat less than ideal, but it gets the job done;
- * it would be better to avoid calling back into cairo. This
- * creates a temporary surface to use as the mask.
- */
static cairo_int_status_t
-_cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface,
- cairo_operator_t op,
- const cairo_pattern_t *source,
- const cairo_pattern_t *mask,
- cairo_clip_t *clip)
+_cairo_quartz_surface_mask_with_solid (cairo_quartz_surface_t *surface,
+ cairo_operator_t op,
+ const cairo_pattern_t *source,
+ double alpha,
+ cairo_clip_t *clip)
{
- int width = surface->extents.width;
- int height = surface->extents.height;
-
- cairo_surface_t *gradient_surf = NULL;
- cairo_surface_pattern_t surface_pattern;
- cairo_int_status_t status;
-
- /* Render the gradient to a surface */
- gradient_surf = cairo_quartz_surface_create (CAIRO_FORMAT_A8,
- width,
- height);
+ cairo_quartz_drawing_state_t state;
+ cairo_status_t status;
- status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL);
- if (status)
+ status = _cairo_quartz_setup_state (&state, surface, op, source, clip);
+ if (unlikely (status))
goto BAIL;
- _cairo_pattern_init_for_surface (&surface_pattern, gradient_surf);
+ CGContextSetAlpha (surface->cgContext, alpha);
+ _cairo_quartz_draw_source (&state, op);
- status = _cairo_quartz_surface_mask_with_surface (surface, op, source, &surface_pattern, clip);
-
- _cairo_pattern_fini (&surface_pattern.base);
-
- BAIL:
- if (gradient_surf)
- cairo_surface_destroy (gradient_surf);
+BAIL:
+ _cairo_quartz_teardown_state (&state, surface);
return status;
}
static cairo_int_status_t
-_cairo_quartz_surface_mask_cg (void *abstract_surface,
+_cairo_quartz_surface_mask_cg (cairo_quartz_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
cairo_clip_t *clip)
{
- cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface;
- cairo_int_status_t rv = CAIRO_STATUS_SUCCESS;
-
- ND((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
+ cairo_surface_t *mask_surf;
+ cairo_matrix_t matrix;
+ cairo_status_t status;
+ cairo_bool_t need_temp;
+ CGInterpolationQuality filter;
- if (IS_EMPTY(surface))
- return CAIRO_STATUS_SUCCESS;
+ ND ((stderr, "%p _cairo_quartz_surface_mask op %d source->type %d mask->type %d\n", surface, op, source->type, mask->type));
- rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
- if (unlikely (rv))
- return rv;
+ if (IS_EMPTY (surface))
+ return CAIRO_INT_STATUS_NOTHING_TO_DO;
if (mask->type == CAIRO_PATTERN_TYPE_SOLID) {
- /* This is easy; we just need to paint with the alpha. */
- cairo_solid_pattern_t *solid_mask = (cairo_solid_pattern_t *) mask;
+ const cairo_solid_pattern_t *mask_solid;
- CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha);
- rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip);
- CGContextSetAlpha (surface->cgContext, 1.0);
+ mask_solid = (const cairo_solid_pattern_t *) mask;
+ return _cairo_quartz_surface_mask_with_solid (surface, op, source,
+ mask_solid->color.alpha,
+ clip);
+ }
- return rv;
+ need_temp = (mask->type != CAIRO_PATTERN_TYPE_SURFACE ||
+ mask->extend != CAIRO_EXTEND_NONE);
+
+ filter = _cairo_quartz_filter_to_quartz (source->filter);
+
+ if (! need_temp) {
+ mask_surf = ((const cairo_surface_pattern_t *) mask)->surface;
+
+ /* When an opaque surface used as a mask in Quartz, its
+ * luminosity is used as the alpha value, so we con only use
+ * surfaces with alpha without creating a temporary mask. */
+ need_temp = ! (mask_surf->content & CAIRO_CONTENT_ALPHA);
}
- /* 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)
- return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip);
+ if (! need_temp) {
+ CGInterpolationQuality mask_filter;
+ cairo_bool_t simple_transform;
+
+ matrix = mask->matrix;
+
+ mask_filter = _cairo_quartz_filter_to_quartz (mask->filter);
+ if (mask_filter == kCGInterpolationNone) {
+ simple_transform = _cairo_matrix_is_translation (&matrix);
+ if (simple_transform) {
+ matrix.x0 = ceil (matrix.x0 - 0.5);
+ matrix.y0 = ceil (matrix.y0 - 0.5);
+ }
+ } else {
+ simple_transform = _cairo_matrix_is_integer_translation (&matrix,
+ NULL,
+ NULL);
+ }
- return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip);
+ /* Quartz only allows one interpolation to be set for mask and
+ * source, so we can skip the temp surface only if the source
+ * filtering makes the mask look correct. */
+ if (source->type == CAIRO_PATTERN_TYPE_SURFACE)
+ need_temp = ! (simple_transform || filter == mask_filter);
+ else
+ filter = mask_filter;
}
- /* So, CGContextClipToMask is not present in 10.3.9, so we're
- * doomed; if we have imageData, we can do fallback, otherwise
- * just pretend success.
- */
- if (surface->imageData)
- return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (need_temp) {
+ /* Render the mask to a surface */
+ mask_surf = _cairo_quartz_surface_create_similar (surface,
+ CAIRO_CONTENT_ALPHA,
+ surface->extents.width,
+ surface->extents.height);
+ status = mask_surf->status;
+ if (unlikely (status))
+ goto BAIL;
- return CAIRO_STATUS_SUCCESS;
+ /* mask_surf is clear, so use OVER instead of SOURCE to avoid a
+ * temporary layer or fallback to cairo-image. */
+ status = _cairo_surface_paint (mask_surf, CAIRO_OPERATOR_OVER, mask, NULL);
+ if (unlikely (status))
+ goto BAIL;
+
+ cairo_matrix_init_identity (&matrix);
+ }
+
+ status = _cairo_quartz_surface_mask_with_surface (surface, op, source,
+ mask_surf,
+ &matrix,
+ filter,
+ clip);
+
+BAIL:
+
+ if (need_temp)
+ cairo_surface_destroy (mask_surf);
+
+ return status;
}
static cairo_int_status_t
@@ -2856,7 +2354,7 @@ _cairo_quartz_surface_mask (void *abstract_surface,
cairo_int_status_t rv;
cairo_image_surface_t *image;
- rv = _cairo_quartz_surface_mask_cg (abstract_surface,
+ rv = _cairo_quartz_surface_mask_cg (surface,
op,
source,
mask,
@@ -2866,7 +2364,7 @@ _cairo_quartz_surface_mask (void *abstract_surface,
return rv;
rv = _cairo_quartz_get_image (surface, &image);
- if (rv == CAIRO_STATUS_SUCCESS) {
+ if (likely (rv == CAIRO_STATUS_SUCCESS)) {
rv = _cairo_surface_mask (&image->base, op, source, mask, clip);
cairo_surface_destroy (&image->base);
}
@@ -2884,9 +2382,9 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
cairo_quartz_surface_t *surface =
cairo_container_of (clipper, cairo_quartz_surface_t, clipper);
- ND((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
+ ND ((stderr, "%p _cairo_quartz_surface_intersect_clip_path path: %p\n", surface, path));
- if (IS_EMPTY(surface))
+ if (IS_EMPTY (surface))
return CAIRO_STATUS_SUCCESS;
if (path == NULL) {
@@ -2910,7 +2408,7 @@ _cairo_quartz_surface_clipper_intersect_clip_path (cairo_surface_clipper_t *clip
CGContextEOClip (surface->cgContext);
}
- ND((stderr, "-- intersect_clip_path\n"));
+ ND ((stderr, "-- intersect_clip_path\n"));
return CAIRO_STATUS_SUCCESS;
}
@@ -2954,20 +2452,20 @@ static const struct _cairo_surface_backend cairo_quartz_surface_backend = {
cairo_quartz_surface_t *
_cairo_quartz_surface_create_internal (CGContextRef cgContext,
- cairo_content_t content,
- unsigned int width,
- unsigned int height)
+ cairo_content_t content,
+ unsigned int width,
+ unsigned int height)
{
cairo_quartz_surface_t *surface;
- quartz_ensure_symbols();
+ quartz_ensure_symbols ();
/* Init the base surface */
- surface = malloc(sizeof(cairo_quartz_surface_t));
- if (surface == NULL)
+ surface = malloc (sizeof (cairo_quartz_surface_t));
+ if (unlikely (surface == NULL))
return (cairo_quartz_surface_t*) _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
- memset(surface, 0, sizeof(cairo_quartz_surface_t));
+ memset (surface, 0, sizeof (cairo_quartz_surface_t));
_cairo_surface_init (&surface->base,
&cairo_quartz_surface_backend,
@@ -2981,8 +2479,9 @@ _cairo_quartz_surface_create_internal (CGContextRef cgContext,
surface->extents.x = surface->extents.y = 0;
surface->extents.width = width;
surface->extents.height = height;
+ surface->virtual_extents = surface->extents;
- if (IS_EMPTY(surface)) {
+ if (IS_EMPTY (surface)) {
surface->cgContext = NULL;
surface->cgContextBaseCTM = CGAffineTransformIdentity;
surface->imageData = NULL;
@@ -3037,17 +2536,12 @@ cairo_quartz_surface_create_for_cg_context (CGContextRef cgContext,
{
cairo_quartz_surface_t *surf;
- CGContextRetain (cgContext);
-
surf = _cairo_quartz_surface_create_internal (cgContext, CAIRO_CONTENT_COLOR_ALPHA,
width, height);
- if (surf->base.status) {
- CGContextRelease (cgContext);
- // create_internal will have set an error
- return (cairo_surface_t*) surf;
- }
+ if (likely (!surf->base.status))
+ CGContextRetain (cgContext);
- return (cairo_surface_t *) surf;
+ return &surf->base;
}
/**
@@ -3079,18 +2573,18 @@ cairo_quartz_surface_create (cairo_format_t format,
int bitsPerComponent;
// verify width and height of surface
- if (!_cairo_quartz_verify_surface_size(width, height))
+ 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);
+ return &_cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
+ width, height)->base;
}
if (format == CAIRO_FORMAT_ARGB32 ||
format == CAIRO_FORMAT_RGB24)
{
- cgColorspace = CGColorSpaceCreateDeviceRGB();
+ cgColorspace = CGColorSpaceCreateDeviceRGB ();
bitinfo = kCGBitmapByteOrder32Host;
if (format == CAIRO_FORMAT_ARGB32)
bitinfo |= kCGImageAlphaPremultipliedFirst;
@@ -3120,7 +2614,7 @@ cairo_quartz_surface_create (cairo_format_t format,
stride = (stride + 15) & ~15;
imageData = _cairo_malloc_ab (height, stride);
- if (!imageData) {
+ if (unlikely (!imageData)) {
CGColorSpaceRelease (cgColorspace);
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
}
@@ -3152,13 +2646,13 @@ cairo_quartz_surface_create (cairo_format_t format,
CGContextRelease (cgc);
free (imageData);
// create_internal will have set an error
- return (cairo_surface_t*) surf;
+ return &surf->base;
}
surf->imageData = imageData;
surf->imageSurfaceEquiv = cairo_image_surface_create_for_data (imageData, format, width, height, stride);
- return (cairo_surface_t *) surf;
+ return &surf->base;
}
/**
@@ -3168,6 +2662,13 @@ cairo_quartz_surface_create (cairo_format_t format,
* Returns the CGContextRef that the given Quartz surface is backed
* by.
*
+ * A call to cairo_surface_flush() is required before using the
+ * CGContextRef to ensure that all pending drawing operations are
+ * finished and to restore any temporary modification cairo has made
+ * to its state. A call to cairo_surface_mark_dirty() is required
+ * after the state or the content of the CGContextRef has been
+ * modified.
+ *
* Return value: the CGContextRef for the given surface.
*
* Since: 1.4
@@ -3194,11 +2695,11 @@ _cairo_surface_is_quartz (const cairo_surface_t *surface)
#include <Movies.h>
-void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
+void ExportCGImageToPNGFile (CGImageRef inImageRef, char* dest)
{
Handle dataRef = NULL;
OSType dataRefType;
- CFStringRef inPath = CFStringCreateWithCString(NULL, dest, kCFStringEncodingASCII);
+ CFStringRef inPath = CFStringCreateWithCString (NULL, dest, kCFStringEncodingASCII);
GraphicsExportComponent grex = 0;
unsigned long sizeWritten;
@@ -3206,35 +2707,35 @@ void ExportCGImageToPNGFile(CGImageRef inImageRef, char* dest)
ComponentResult result;
// create the data reference
- result = QTNewDataReferenceFromFullPathCFString(inPath, kQTNativeDefaultPathStyle,
- 0, &dataRef, &dataRefType);
+ result = QTNewDataReferenceFromFullPathCFString (inPath, kQTNativeDefaultPathStyle,
+ 0, &dataRef, &dataRefType);
if (NULL != dataRef && noErr == result) {
// get the PNG exporter
- result = OpenADefaultComponent(GraphicsExporterComponentType, kQTFileTypePNG,
- &grex);
+ result = OpenADefaultComponent (GraphicsExporterComponentType, kQTFileTypePNG,
+ &grex);
if (grex) {
// tell the exporter where to find its source image
- result = GraphicsExportSetInputCGImage(grex, inImageRef);
+ result = GraphicsExportSetInputCGImage (grex, inImageRef);
if (noErr == result) {
// tell the exporter where to save the exporter image
- result = GraphicsExportSetOutputDataReference(grex, dataRef,
- dataRefType);
+ result = GraphicsExportSetOutputDataReference (grex, dataRef,
+ dataRefType);
if (noErr == result) {
// write the PNG file
- result = GraphicsExportDoExport(grex, &sizeWritten);
+ result = GraphicsExportDoExport (grex, &sizeWritten);
}
}
// remember to close the component
- CloseComponent(grex);
+ CloseComponent (grex);
}
// remember to dispose of the data reference handle
- DisposeHandle(dataRef);
+ DisposeHandle (dataRef);
}
}
@@ -3251,7 +2752,7 @@ quartz_image_to_png (CGImageRef imgref, char *dest)
dest = sptr;
}
- ExportCGImageToPNGFile(imgref, dest);
+ ExportCGImageToPNGFile (imgref, dest);
}
void
@@ -3278,9 +2779,9 @@ quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest)
return;
}
- ExportCGImageToPNGFile(imgref, dest);
+ ExportCGImageToPNGFile (imgref, dest);
- CGImageRelease(imgref);
+ CGImageRelease (imgref);
}
#endif /* QUARTZ_DEBUG */