diff options
Diffstat (limited to 'src/cairo-quartz-surface.c')
-rw-r--r-- | src/cairo-quartz-surface.c | 663 |
1 files changed, 281 insertions, 382 deletions
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c index f2fdb63d..99095b10 100644 --- a/src/cairo-quartz-surface.c +++ b/src/cairo-quartz-surface.c @@ -532,20 +532,31 @@ _cairo_quartz_surface_set_cairo_operator (cairo_quartz_surface_t *surface, cairo { ND((stderr, "%p _cairo_quartz_surface_set_cairo_operator %d\n", surface, op)); + /* 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_OUT || + 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; + } + + 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_IN) - op = CAIRO_OPERATOR_DEST_ATOP; - else if (op == CAIRO_OPERATOR_DEST_ATOP) - op = CAIRO_OPERATOR_IN; - else if (op == CAIRO_OPERATOR_ATOP) - return CAIRO_INT_STATUS_NOTHING_TO_DO; /* op = CAIRO_OPERATOR_DEST_OVER */ - else if (op == CAIRO_OPERATOR_DEST_OVER) - op = CAIRO_OPERATOR_ATOP; + 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); @@ -621,152 +632,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; - - 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; - - _cairo_cgcontext_set_cairo_operator (cgc, CAIRO_OPERATOR_SOURCE); - - /* 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)); - - _cairo_cgcontext_set_cairo_operator (cgc, CAIRO_OPERATOR_CLEAR); - 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.0, - 0.0, - 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); - - _cairo_quartz_surface_set_cairo_operator (surface, CAIRO_OPERATOR_SOURCE); - CGContextClipToMask (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 @@ -1124,18 +989,30 @@ _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (cairo_quartz_surface_t /* State used during a drawing operation. */ typedef struct { + /* The destination of the mask */ + CGContextRef cgMaskContext; + + /* The destination of the drawing of the source */ + CGContextRef cgDrawContext; + + /* Action type */ cairo_quartz_action_t action; + /* Destination rect */ + CGRect rect; + /* Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE */ CGAffineTransform transform; /* Used with DO_IMAGE and DO_TILED_IMAGE */ CGImageRef image; - CGRect imageRect; /* Used with DO_SHADING */ CGShadingRef shading; + /* Temporary destination for unbounded operations */ + CGLayerRef layer; + CGRect clipRect; } cairo_quartz_drawing_state_t; /* @@ -1203,15 +1080,28 @@ _cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state, } static cairo_int_status_t -_cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, - cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source) +_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) { + cairo_bool_t needs_temp; cairo_status_t status; + state->layer = NULL; state->image = NULL; state->shading = NULL; + state->cgDrawContext = NULL; + state->cgMaskContext = NULL; + + status = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + if (unlikely (status)) + return status; + + status = _cairo_quartz_surface_set_cairo_operator (surface, op); + if (unlikely (status)) + return status; /* Save before we change the pattern, colorspace, etc. so that * we can restore and make sure that quartz releases our @@ -1219,22 +1109,62 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, */ CGContextSaveGState (surface->cgContext); + state->clipRect = CGContextGetClipBoundingBox (surface->cgContext); + state->clipRect = CGRectIntegral (state->clipRect); + state->rect = state->clipRect; - status = _cairo_quartz_surface_set_cairo_operator (surface, op); - if (unlikely (status)) - return status; + state->cgMaskContext = surface->cgContext; + state->cgDrawContext = state->cgMaskContext; + + if (op == CAIRO_OPERATOR_CLEAR) { + CGContextSetRGBFillColor (state->cgDrawContext, 0, 0, 0, 1); + + 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); - CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter)); + 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); + } + + CGContextSetInterpolationQuality (state->cgDrawContext, _cairo_quartz_filter_to_quartz (source->filter)); 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, @@ -1277,8 +1207,6 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, if (unlikely (img == NULL)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); - state->image = img; cairo_matrix_invert (&m); @@ -1287,12 +1215,32 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, 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) { - state->imageRect = CGRectMake (0, 0, extents.width, extents.height); + 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 @@ -1300,10 +1248,9 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, * epsilon), and if not, fall back to the CGPattern type. */ - xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext), + 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); @@ -1322,7 +1269,7 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, srcRect = CGRectApplyAffineTransform (srcRect, xform); - state->imageRect = srcRect; + state->rect = srcRect; state->action = DO_TILED_IMAGE; return CAIRO_STATUS_SUCCESS; } @@ -1341,17 +1288,17 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, return status; patternSpace = CGColorSpaceCreatePattern (NULL); - CGContextSetFillColorSpace (surface->cgContext, patternSpace); - CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha); - CGContextSetStrokeColorSpace (surface->cgContext, patternSpace); - CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha); + 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)); CGPatternRelease (pattern); @@ -1363,11 +1310,19 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state, } static void -_cairo_quartz_teardown_source (cairo_quartz_drawing_state_t *state, - cairo_quartz_surface_t *surface) - +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state, + cairo_quartz_surface_t *surface) { - CGContextRestoreGState (surface->cgContext); + if (state->layer) { + CGContextDrawLayerInRect (surface->cgContext, + state->clipRect, + state->layer); + CGContextRelease (state->cgDrawContext); + CGLayerRelease (state->layer); + } + + if (state->cgMaskContext) + CGContextRestoreGState (surface->cgContext); if (state->image) CGImageRelease (state->image); @@ -1376,45 +1331,46 @@ _cairo_quartz_teardown_source (cairo_quartz_drawing_state_t *state, CGShadingRelease (state->shading); } -static cairo_int_status_t -_cairo_quartz_setup_source_safe (cairo_quartz_drawing_state_t *state, - cairo_quartz_surface_t *surface, - cairo_operator_t op, - const cairo_pattern_t *source) -{ - cairo_int_status_t status; - - status = _cairo_quartz_setup_source (state, surface, op, source); - if (unlikely (status)) - _cairo_quartz_teardown_source (state, surface); - - return status; -} - static void -_cairo_quartz_draw_source (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_drawing_state_t *state) +_cairo_quartz_draw_source (cairo_quartz_drawing_state_t *state, + cairo_operator_t op) { - CGContextConcatCTM (surface->cgContext, state->transform); + if (state->action == DO_DIRECT) { + CGContextFillRect (state->cgDrawContext, state->rect); + return; + } + + CGContextConcatCTM (state->cgDrawContext, state->transform); if (state->action == DO_SHADING) { - CGContextDrawShading (surface->cgContext, state->shading); + CGContextDrawShading (state->cgDrawContext, state->shading); return; } - CGContextTranslateCTM (surface->cgContext, 0, state->imageRect.size.height); - CGContextScaleCTM (surface->cgContext, 1, -1); + CGContextTranslateCTM (state->cgDrawContext, 0, state->rect.size.height); + CGContextScaleCTM (state->cgDrawContext, 1, -1); if (state->action == DO_IMAGE) { - CGContextDrawImage (surface->cgContext, state->imageRect, state->image); - if (!_cairo_operator_bounded_by_source (op)) { - CGContextBeginPath (surface->cgContext); - CGContextAddRect (surface->cgContext, state->imageRect); - CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0); - CGContextEOFillPath (surface->cgContext); + 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, state->imageRect, state->image); + } else { + CGContextDrawTiledImagePtr (state->cgDrawContext, state->rect, state->image); + } } @@ -1768,24 +1724,14 @@ _cairo_quartz_surface_paint_cg (cairo_quartz_surface_t *surface, if (IS_EMPTY (surface)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - 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_setup_source_safe (&state, surface, op, source); - if (unlikely (rv)) - return rv; + goto BAIL; - if (state.action == DO_DIRECT) { - CGContextFillRect (surface->cgContext, CGRectMake (surface->extents.x, - surface->extents.y, - surface->extents.width, - surface->extents.height)); - } else { - _cairo_quartz_draw_source (surface, op, &state); - } + _cairo_quartz_draw_source (&state, op); - _cairo_quartz_teardown_source (&state, surface); +BAIL: + _cairo_quartz_teardown_state (&state, surface); ND ((stderr, "-- paint\n")); return rv; @@ -1830,53 +1776,37 @@ _cairo_quartz_surface_fill_cg (cairo_quartz_surface_t *surface, { cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; - 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_INT_STATUS_NOTHING_TO_DO; - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - - rv = _cairo_quartz_setup_source_safe (&state, surface, op, source); + rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); if (unlikely (rv)) - return rv; - - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); + goto BAIL; - _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext); + CGContextSetShouldAntialias (state.cgMaskContext, (antialias != CAIRO_ANTIALIAS_NONE)); - if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr) - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext); + _cairo_quartz_cairo_path_to_quartz_context (path, state.cgMaskContext); if (state.action == DO_DIRECT) { + assert (state.cgDrawContext == state.cgMaskContext); if (fill_rule == CAIRO_FILL_RULE_WINDING) - CGContextFillPath (surface->cgContext); + CGContextFillPath (state.cgMaskContext); else - CGContextEOFillPath (surface->cgContext); + 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_source (surface, op, &state); + _cairo_quartz_draw_source (&state, op); } - _cairo_quartz_teardown_source (&state, surface); - - 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")); return rv; @@ -1934,27 +1864,26 @@ _cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface, cairo_quartz_drawing_state_t state; cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; 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)); if (IS_EMPTY (surface)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); + rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); if (unlikely (rv)) - return 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); + 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); - origCTM = CGContextGetCTM (surface->cgContext); + origCTM = CGContextGetCTM (state.cgMaskContext); if (style->dash && style->num_dashes) { cairo_quartz_float_t sdash[CAIRO_STACK_ARRAY_LENGTH (cairo_quartz_float_t)]; @@ -1966,61 +1895,38 @@ _cairo_quartz_surface_stroke_cg (cairo_quartz_surface_t *surface, max_dashes *= 2; if (max_dashes > ARRAY_LENGTH (sdash)) fdash = _cairo_malloc_ab (max_dashes, sizeof (cairo_quartz_float_t)); - if (unlikely (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); - - rv = _cairo_quartz_setup_source_safe (&state, surface, op, source); - if (unlikely (rv)) - return rv; - - _cairo_quartz_cairo_path_to_quartz_context (path, surface->cgContext); + CGContextSetLineDash (state.cgMaskContext, 0, NULL, 0); - 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); + CGContextConcatCTM (state.cgMaskContext, strokeTransform); if (state.action == DO_DIRECT) { - CGContextStrokePath (surface->cgContext); + assert (state.cgDrawContext == state.cgMaskContext); + CGContextStrokePath (state.cgMaskContext); } else { - CGContextReplacePathWithStrokedPath (surface->cgContext); - CGContextClip (surface->cgContext); + CGContextReplacePathWithStrokedPath (state.cgMaskContext); + CGContextClip (state.cgMaskContext); - CGContextSetCTM (surface->cgContext, origCTM); - _cairo_quartz_draw_source (surface, op, &state); + CGContextSetCTM (state.cgMaskContext, origCTM); + _cairo_quartz_draw_source (&state, op); } - _cairo_quartz_teardown_source (&state, surface); - - 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")); return rv; @@ -2086,7 +1992,6 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, int i; CGFontRef cgfref = NULL; - cairo_bool_t isClipping = FALSE; cairo_bool_t didForceFontSmoothing = FALSE; if (IS_EMPTY (surface)) @@ -2098,43 +2003,39 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, 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); - if (unlikely (rv)) - return rv; - - rv = _cairo_quartz_setup_source_safe (&state, surface, op, source); + rv = _cairo_quartz_setup_state (&state, surface, op, source, clip); if (unlikely (rv)) - return rv; + goto BAIL; if (state.action == DO_DIRECT) { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill); + assert (state.cgDrawContext == state.cgMaskContext); + CGContextSetTextDrawingMode (state.cgMaskContext, kCGTextFill); } else { - CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip); - isClipping = TRUE; + 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 */ @@ -2158,7 +2059,7 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, 0, 0); _cairo_quartz_cairo_matrix_to_quartz (&scaled_font->scale_inverse, &invTextTransform); - CGContextSetTextMatrix (surface->cgContext, CGAffineTransformIdentity); + 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. */ @@ -2177,43 +2078,25 @@ _cairo_quartz_surface_show_glyphs_cg (cairo_quartz_surface_t *surface, } /* 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); + ctm = CGContextGetCTM (state.cgMaskContext); + CGContextTranslateCTM (state.cgMaskContext, glyphs[0].x, glyphs[0].y); + CGContextConcatCTM (state.cgMaskContext, textTransform); - CGContextShowGlyphsWithAdvances (surface->cgContext, + CGContextShowGlyphsWithAdvances (state.cgMaskContext, cg_glyphs, cg_advances, num_glyphs); - CGContextSetCTM (surface->cgContext, ctm); + CGContextSetCTM (state.cgMaskContext, ctm); if (state.action != DO_DIRECT) - _cairo_quartz_draw_source (surface, op, &state); + _cairo_quartz_draw_source (&state, op); BAIL: - _cairo_quartz_teardown_source (&state, surface); - if (didForceFontSmoothing) - CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE); - - if (rv == CAIRO_STATUS_SUCCESS && - cgfref && - !_cairo_operator_bounded_by_mask (op)) - { - unbounded_op_data_t ub; - ub.op = UNBOUNDED_SHOW_GLYPHS; + CGContextSetAllowsFontSmoothingPtr (state.cgMaskContext, FALSE); - 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); - } + _cairo_quartz_teardown_state (&state, surface); if (cg_glyphs != glyphs_static) free (cg_glyphs); @@ -2271,45 +2154,42 @@ _cairo_quartz_surface_mask_with_surface (cairo_quartz_surface_t *surface, cairo_surface_t *pat_surf = mask->surface; cairo_status_t status = CAIRO_STATUS_SUCCESS; CGAffineTransform ctm, mask_matrix; + cairo_quartz_drawing_state_t state; + + if (IS_EMPTY (surface)) + return CAIRO_INT_STATUS_NOTHING_TO_DO; status = _cairo_surface_to_cgimage (pat_surf, &img); if (unlikely (status)) return status; - if (unlikely (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.0f, 0.0f, CGImageGetWidth (img) , CGImageGetHeight (img)); /* 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); + ctm = CGContextGetCTM (state.cgMaskContext); _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); - CGContextConcatCTM (surface->cgContext, mask_matrix); - CGContextClipToMask (surface->cgContext, rect, img); + CGContextConcatCTM (state.cgMaskContext, mask_matrix); + CGContextClipToMask (state.cgMaskContext, rect, img); - CGContextSetCTM (surface->cgContext, ctm); + CGContextSetCTM (state.cgMaskContext, ctm); - status = _cairo_quartz_surface_paint_cg (surface, op, source, clip); + _cairo_quartz_draw_source (&state, op); - CGContextRestoreGState (surface->cgContext); - - 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); @@ -2340,7 +2220,11 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, if (unlikely (status)) goto BAIL; - status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_SOURCE, mask, NULL); + /* gradient_surf is clear, thus we can use OVER instead of SOURCE + * to make sure we won't have to create a temporary layer or + * fallback. + */ + status = _cairo_quartz_surface_paint (gradient_surf, CAIRO_OPERATOR_OVER, mask, NULL); if (unlikely (status)) goto BAIL; @@ -2355,32 +2239,47 @@ _cairo_quartz_surface_mask_with_generic (cairo_quartz_surface_t *surface, } static cairo_int_status_t +_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) +{ + cairo_quartz_drawing_state_t state; + cairo_status_t status; + + status = _cairo_quartz_setup_state (&state, surface, op, source, clip); + if (unlikely (status)) + goto BAIL; + + CGContextSetAlpha (surface->cgContext, alpha); + _cairo_quartz_draw_source (&state, op); + +BAIL: + _cairo_quartz_teardown_state (&state, surface); + + return status; +} + +static cairo_int_status_t _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_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)); if (IS_EMPTY (surface)) return CAIRO_INT_STATUS_NOTHING_TO_DO; - rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); - if (unlikely (rv)) - return rv; - 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); - - return rv; + mask_solid = (const cairo_solid_pattern_t *) mask; + return _cairo_quartz_surface_mask_with_solid (surface, op, source, + mask_solid->color.alpha, + clip); } /* For these, we can skip creating a temporary surface, since we already have one */ |