michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-private.h b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-private.h michael@0: @@ -50,30 +50,16 @@ typedef struct cairo_quartz_surface { michael@0: CGContextRef cgContext; michael@0: CGAffineTransform cgContextBaseCTM; michael@0: michael@0: void *imageData; michael@0: cairo_surface_t *imageSurfaceEquiv; michael@0: michael@0: cairo_surface_clipper_t clipper; michael@0: cairo_rectangle_int_t extents; michael@0: - michael@0: - /* These are stored while drawing operations are in place, set up michael@0: - * by quartz_setup_source() and quartz_finish_source() michael@0: - */ michael@0: - CGAffineTransform sourceTransform; michael@0: - michael@0: - CGImageRef sourceImage; michael@0: - cairo_surface_t *sourceImageSurface; michael@0: - CGRect sourceImageRect; michael@0: - michael@0: - CGShadingRef sourceShading; michael@0: - CGPatternRef sourcePattern; michael@0: - michael@0: - CGInterpolationQuality oldInterpolationQuality; michael@0: } cairo_quartz_surface_t; michael@0: michael@0: typedef struct cairo_quartz_image_surface { michael@0: cairo_surface_t base; michael@0: michael@0: cairo_rectangle_int_t extents; michael@0: michael@0: CGImageRef image; michael@0: diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c michael@0: @@ -1333,36 +1333,59 @@ _cairo_quartz_cairo_repeating_surface_pa michael@0: return CAIRO_STATUS_SUCCESS; michael@0: } michael@0: michael@0: typedef enum { michael@0: DO_SOLID, michael@0: DO_SHADING, michael@0: DO_PATTERN, michael@0: DO_IMAGE, michael@0: + DO_TILED_IMAGE, michael@0: DO_UNSUPPORTED, michael@0: - DO_NOTHING, michael@0: - DO_TILED_IMAGE michael@0: + DO_NOTHING michael@0: } cairo_quartz_action_t; michael@0: michael@0: -static cairo_quartz_action_t michael@0: +/* State used during a drawing operation. */ michael@0: +typedef struct { michael@0: + CGContextRef context; michael@0: + cairo_quartz_action_t action; michael@0: + michael@0: + // Used with DO_SHADING, DO_IMAGE and DO_TILED_IMAGE michael@0: + CGAffineTransform transform; michael@0: + michael@0: + // Used with DO_IMAGE and DO_TILED_IMAGE michael@0: + CGImageRef image; michael@0: + cairo_surface_t *imageSurface; michael@0: + CGRect imageRect; michael@0: + michael@0: + // Used with DO_SHADING michael@0: + CGShadingRef shading; michael@0: + michael@0: + // Used with DO_PATTERN michael@0: + CGPatternRef pattern; michael@0: +} cairo_quartz_drawing_state_t; michael@0: + michael@0: +static void michael@0: _cairo_quartz_setup_fallback_source (cairo_quartz_surface_t *surface, michael@0: - const cairo_pattern_t *source) michael@0: + const cairo_pattern_t *source, michael@0: + cairo_quartz_drawing_state_t *state) michael@0: { michael@0: - CGRect clipBox = CGContextGetClipBoundingBox (surface->cgContext); michael@0: + CGRect clipBox = CGContextGetClipBoundingBox (state->context); michael@0: double x0, y0, w, h; michael@0: michael@0: cairo_surface_t *fallback; michael@0: CGImageRef img; michael@0: michael@0: cairo_status_t status; michael@0: michael@0: if (clipBox.size.width == 0.0f || michael@0: - clipBox.size.height == 0.0f) michael@0: - return DO_NOTHING; michael@0: + clipBox.size.height == 0.0f) { michael@0: + state->action = DO_NOTHING; michael@0: + return; michael@0: + } michael@0: michael@0: x0 = floor(clipBox.origin.x); michael@0: y0 = floor(clipBox.origin.y); michael@0: w = ceil(clipBox.origin.x + clipBox.size.width) - x0; michael@0: h = ceil(clipBox.origin.y + clipBox.size.height) - y0; michael@0: michael@0: /* Create a temporary the size of the clip surface, and position michael@0: * it so that the device origin coincides with the original surface */ michael@0: @@ -1396,73 +1419,79 @@ _cairo_quartz_setup_fallback_source (cai michael@0: &fallback->device_transform_inverse); michael@0: status = _cairo_surface_paint (fallback, michael@0: CAIRO_OPERATOR_SOURCE, michael@0: &pattern.base, NULL); michael@0: } michael@0: #endif michael@0: michael@0: status = _cairo_surface_to_cgimage (&surface->base, fallback, &img); michael@0: - if (status) michael@0: - return DO_UNSUPPORTED; michael@0: - if (img == NULL) michael@0: - return DO_NOTHING; michael@0: - michael@0: - surface->sourceImageRect = CGRectMake (0.0, 0.0, w, h); michael@0: - surface->sourceImage = img; michael@0: - surface->sourceImageSurface = fallback; michael@0: - surface->sourceTransform = CGAffineTransformMakeTranslation (x0, y0); michael@0: - michael@0: - return DO_IMAGE; michael@0: + if (status) { michael@0: + state->action = DO_UNSUPPORTED; michael@0: + return; michael@0: + } michael@0: + if (img == NULL) { michael@0: + state->action = DO_NOTHING; michael@0: + return; michael@0: + } michael@0: + michael@0: + state->imageRect = CGRectMake (0.0, 0.0, w, h); michael@0: + state->image = img; michael@0: + state->imageSurface = fallback; michael@0: + state->transform = CGAffineTransformMakeTranslation (x0, y0); michael@0: + state->action = DO_IMAGE; michael@0: } michael@0: michael@0: /* michael@0: Quartz does not support repeating radients. We handle repeating gradients michael@0: by manually extending the gradient and repeating color stops. We need to michael@0: minimize the number of repetitions since Quartz seems to sample our color michael@0: function across the entire range, even if part of that range is not needed michael@0: for the visible area of the gradient, and it samples with some fixed resolution, michael@0: so if the gradient range is too large it samples with very low resolution and michael@0: the gradient is very coarse. CreateRepeatingLinearGradientFunction and michael@0: CreateRepeatingRadialGradientFunction compute the number of repetitions needed michael@0: based on the extents of the object (the clip region cannot be used here since michael@0: we don't want the rasterization of the entire gradient to depend on the michael@0: clip region). michael@0: */ michael@0: -static cairo_quartz_action_t michael@0: +static void michael@0: _cairo_quartz_setup_linear_source (cairo_quartz_surface_t *surface, michael@0: const cairo_linear_pattern_t *lpat, michael@0: - cairo_rectangle_int_t *extents) michael@0: + cairo_rectangle_int_t *extents, michael@0: + cairo_quartz_drawing_state_t *state) michael@0: { michael@0: const cairo_pattern_t *abspat = &lpat->base.base; michael@0: cairo_matrix_t mat; michael@0: CGPoint start, end; michael@0: CGFunctionRef gradFunc; michael@0: CGColorSpaceRef rgb; michael@0: bool extend = abspat->extend == CAIRO_EXTEND_PAD; michael@0: michael@0: if (lpat->base.n_stops == 0) { michael@0: - CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.); michael@0: - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); michael@0: - return DO_SOLID; michael@0: + CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); michael@0: + CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); michael@0: + state->action = DO_SOLID; michael@0: + return; michael@0: } michael@0: michael@0: if (lpat->p1.x == lpat->p2.x && michael@0: lpat->p1.y == lpat->p2.y) { michael@0: /* Quartz handles cases where the vector has no length very michael@0: * differently from pixman. michael@0: * Whatever the correct behaviour is, let's at least have only pixman's michael@0: * implementation to worry about. michael@0: */ michael@0: - return _cairo_quartz_setup_fallback_source (surface, abspat); michael@0: + _cairo_quartz_setup_fallback_source (surface, abspat, state); michael@0: + return; michael@0: } michael@0: michael@0: mat = abspat->matrix; michael@0: cairo_matrix_invert (&mat); michael@0: - _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform); michael@0: + _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); michael@0: michael@0: rgb = CGColorSpaceCreateDeviceRGB(); michael@0: michael@0: start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x), michael@0: _cairo_fixed_to_double (lpat->p1.y)); michael@0: end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x), michael@0: _cairo_fixed_to_double (lpat->p2.y)); michael@0: michael@0: @@ -1472,31 +1501,32 @@ _cairo_quartz_setup_linear_source (cairo michael@0: gradFunc = CreateGradientFunction (&lpat->base); michael@0: } else { michael@0: gradFunc = CreateRepeatingLinearGradientFunction (surface, michael@0: &lpat->base, michael@0: &start, &end, michael@0: extents); michael@0: } michael@0: michael@0: - surface->sourceShading = CGShadingCreateAxial (rgb, michael@0: - start, end, michael@0: - gradFunc, michael@0: - extend, extend); michael@0: + state->shading = CGShadingCreateAxial (rgb, michael@0: + start, end, michael@0: + gradFunc, michael@0: + extend, extend); michael@0: michael@0: CGColorSpaceRelease(rgb); michael@0: CGFunctionRelease(gradFunc); michael@0: michael@0: - return DO_SHADING; michael@0: + state->action = DO_SHADING; michael@0: } michael@0: michael@0: -static cairo_quartz_action_t michael@0: +static void michael@0: _cairo_quartz_setup_radial_source (cairo_quartz_surface_t *surface, michael@0: const cairo_radial_pattern_t *rpat, michael@0: - cairo_rectangle_int_t *extents) michael@0: + cairo_rectangle_int_t *extents, michael@0: + cairo_quartz_drawing_state_t *state) michael@0: { michael@0: const cairo_pattern_t *abspat = &rpat->base.base; michael@0: cairo_matrix_t mat; michael@0: CGPoint start, end; michael@0: CGFunctionRef gradFunc; michael@0: CGColorSpaceRef rgb; michael@0: bool extend = abspat->extend == CAIRO_EXTEND_PAD; michael@0: double c1x = _cairo_fixed_to_double (rpat->c1.x); michael@0: @@ -1505,35 +1535,37 @@ _cairo_quartz_setup_radial_source (cairo michael@0: double c2y = _cairo_fixed_to_double (rpat->c2.y); michael@0: double r1 = _cairo_fixed_to_double (rpat->r1); michael@0: double r2 = _cairo_fixed_to_double (rpat->r2); michael@0: double dx = c1x - c2x; michael@0: double dy = c1y - c2y; michael@0: double centerDistance = sqrt (dx*dx + dy*dy); michael@0: michael@0: if (rpat->base.n_stops == 0) { michael@0: - CGContextSetRGBStrokeColor (surface->cgContext, 0., 0., 0., 0.); michael@0: - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); michael@0: - return DO_SOLID; michael@0: + CGContextSetRGBStrokeColor (state->context, 0., 0., 0., 0.); michael@0: + CGContextSetRGBFillColor (state->context, 0., 0., 0., 0.); michael@0: + state->action = DO_SOLID; michael@0: + return; michael@0: } michael@0: michael@0: if (r2 <= centerDistance + r1 + 1e-6 && /* circle 2 doesn't contain circle 1 */ michael@0: r1 <= centerDistance + r2 + 1e-6) { /* circle 1 doesn't contain circle 2 */ michael@0: /* Quartz handles cases where neither circle contains the other very michael@0: * differently from pixman. michael@0: * Whatever the correct behaviour is, let's at least have only pixman's michael@0: * implementation to worry about. michael@0: * Note that this also catches the cases where r1 == r2. michael@0: */ michael@0: - return _cairo_quartz_setup_fallback_source (surface, abspat); michael@0: + _cairo_quartz_setup_fallback_source (surface, abspat, state); michael@0: + return; michael@0: } michael@0: michael@0: mat = abspat->matrix; michael@0: cairo_matrix_invert (&mat); michael@0: - _cairo_quartz_cairo_matrix_to_quartz (&mat, &surface->sourceTransform); michael@0: + _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform); michael@0: michael@0: rgb = CGColorSpaceCreateDeviceRGB(); michael@0: michael@0: start = CGPointMake (c1x, c1y); michael@0: end = CGPointMake (c2x, c2y); michael@0: michael@0: if (abspat->extend == CAIRO_EXTEND_NONE || michael@0: abspat->extend == CAIRO_EXTEND_PAD) michael@0: @@ -1542,111 +1574,146 @@ _cairo_quartz_setup_radial_source (cairo michael@0: } else { michael@0: gradFunc = CreateRepeatingRadialGradientFunction (surface, michael@0: &rpat->base, michael@0: &start, &r1, michael@0: &end, &r2, michael@0: extents); michael@0: } michael@0: michael@0: - surface->sourceShading = CGShadingCreateRadial (rgb, michael@0: - start, michael@0: - r1, michael@0: - end, michael@0: - r2, michael@0: - gradFunc, michael@0: - extend, extend); michael@0: + state->shading = CGShadingCreateRadial (rgb, michael@0: + start, michael@0: + r1, michael@0: + end, michael@0: + r2, michael@0: + gradFunc, michael@0: + extend, extend); michael@0: michael@0: CGColorSpaceRelease(rgb); michael@0: CGFunctionRelease(gradFunc); michael@0: michael@0: - return DO_SHADING; michael@0: + state->action = DO_SHADING; michael@0: } michael@0: michael@0: -static cairo_quartz_action_t michael@0: -_cairo_quartz_setup_source (cairo_quartz_surface_t *surface, michael@0: - const cairo_pattern_t *source, michael@0: - cairo_rectangle_int_t *extents) michael@0: +/** michael@0: + * Sets up internal state to be used to draw the source mask, stored in michael@0: + * cairo_quartz_state_t. Guarantees to call CGContextSaveGState on michael@0: + * surface->cgContext. michael@0: + */ michael@0: +static cairo_quartz_drawing_state_t michael@0: +_cairo_quartz_setup_state (cairo_quartz_surface_t *surface, michael@0: + const cairo_pattern_t *source, michael@0: + cairo_operator_t op, michael@0: + cairo_rectangle_int_t *extents) michael@0: { michael@0: - assert (!(surface->sourceImage || surface->sourceShading || surface->sourcePattern)); michael@0: - michael@0: - surface->oldInterpolationQuality = CGContextGetInterpolationQuality (surface->cgContext); michael@0: - CGContextSetInterpolationQuality (surface->cgContext, _cairo_quartz_filter_to_quartz (source->filter)); michael@0: + CGContextRef context = surface->cgContext; michael@0: + cairo_quartz_drawing_state_t state; michael@0: + cairo_status_t status; michael@0: + michael@0: + state.context = context; michael@0: + state.image = NULL; michael@0: + state.imageSurface = NULL; michael@0: + state.shading = NULL; michael@0: + state.pattern = NULL; michael@0: + michael@0: + // Save before we change the pattern, colorspace, etc. so that michael@0: + // we can restore and make sure that quartz releases our michael@0: + // pattern (which may be stack allocated) michael@0: + CGContextSaveGState(context); michael@0: + michael@0: + CGContextSetInterpolationQuality (context, _cairo_quartz_filter_to_quartz (source->filter)); michael@0: + michael@0: + status = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { michael@0: + state.action = DO_NOTHING; michael@0: + return state; michael@0: + } michael@0: + if (status) { michael@0: + state.action = DO_UNSUPPORTED; michael@0: + return state; michael@0: + } michael@0: michael@0: if (source->type == CAIRO_PATTERN_TYPE_SOLID) { michael@0: cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) source; michael@0: michael@0: - CGContextSetRGBStrokeColor (surface->cgContext, michael@0: + CGContextSetRGBStrokeColor (context, michael@0: solid->color.red, michael@0: solid->color.green, michael@0: solid->color.blue, michael@0: solid->color.alpha); michael@0: - CGContextSetRGBFillColor (surface->cgContext, michael@0: + CGContextSetRGBFillColor (context, michael@0: solid->color.red, michael@0: solid->color.green, michael@0: solid->color.blue, michael@0: solid->color.alpha); michael@0: michael@0: - return DO_SOLID; michael@0: + state.action = DO_SOLID; michael@0: + return state; michael@0: } michael@0: michael@0: if (source->type == CAIRO_PATTERN_TYPE_LINEAR) { michael@0: const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source; michael@0: - return _cairo_quartz_setup_linear_source (surface, lpat, extents); michael@0: + _cairo_quartz_setup_linear_source (surface, lpat, extents, &state); michael@0: + return state; michael@0: } michael@0: michael@0: if (source->type == CAIRO_PATTERN_TYPE_RADIAL) { michael@0: const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source; michael@0: - return _cairo_quartz_setup_radial_source (surface, rpat, extents); michael@0: + _cairo_quartz_setup_radial_source (surface, rpat, extents, &state); michael@0: + return state; michael@0: } michael@0: michael@0: if (source->type == CAIRO_PATTERN_TYPE_SURFACE && michael@0: (source->extend == CAIRO_EXTEND_NONE || (CGContextDrawTiledImagePtr && source->extend == CAIRO_EXTEND_REPEAT))) michael@0: { michael@0: const cairo_surface_pattern_t *spat = (const cairo_surface_pattern_t *) source; michael@0: cairo_surface_t *pat_surf = spat->surface; michael@0: CGImageRef img; michael@0: cairo_matrix_t m = spat->base.matrix; michael@0: cairo_rectangle_int_t extents; michael@0: - cairo_status_t status; michael@0: CGAffineTransform xform; michael@0: CGRect srcRect; michael@0: cairo_fixed_t fw, fh; michael@0: cairo_bool_t is_bounded; michael@0: michael@0: status = _cairo_surface_to_cgimage ((cairo_surface_t *) surface, pat_surf, &img); michael@0: - if (status) michael@0: - return DO_UNSUPPORTED; michael@0: - if (img == NULL) michael@0: - return DO_NOTHING; michael@0: + if (status) { michael@0: + state.action = DO_UNSUPPORTED; michael@0: + return state; michael@0: + } michael@0: + if (img == NULL) { michael@0: + state.action = DO_NOTHING; michael@0: + return state; michael@0: + } michael@0: michael@0: CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 1); michael@0: michael@0: - surface->sourceImage = img; michael@0: + state.image = img; michael@0: michael@0: cairo_matrix_invert(&m); michael@0: - _cairo_quartz_cairo_matrix_to_quartz (&m, &surface->sourceTransform); michael@0: + _cairo_quartz_cairo_matrix_to_quartz (&m, &state.transform); michael@0: michael@0: is_bounded = _cairo_surface_get_extents (pat_surf, &extents); michael@0: assert (is_bounded); michael@0: michael@0: if (source->extend == CAIRO_EXTEND_NONE) { michael@0: - surface->sourceImageRect = CGRectMake (0, 0, extents.width, extents.height); michael@0: - return DO_IMAGE; michael@0: + state.imageRect = CGRectMake (0, 0, extents.width, extents.height); michael@0: + state.action = DO_IMAGE; michael@0: + return state; michael@0: } michael@0: michael@0: /* Quartz seems to tile images at pixel-aligned regions only -- this michael@0: * leads to seams if the image doesn't end up scaling to fill the michael@0: * space exactly. The CGPattern tiling approach doesn't have this michael@0: * problem. Check if we're going to fill up the space (within some michael@0: * epsilon), and if not, fall back to the CGPattern type. michael@0: */ michael@0: michael@0: - xform = CGAffineTransformConcat (CGContextGetCTM (surface->cgContext), michael@0: - surface->sourceTransform); michael@0: + xform = CGAffineTransformConcat (CGContextGetCTM (context), michael@0: + state.transform); michael@0: michael@0: srcRect = CGRectMake (0, 0, extents.width, extents.height); michael@0: srcRect = CGRectApplyAffineTransform (srcRect, xform); michael@0: michael@0: fw = _cairo_fixed_from_double (srcRect.size.width); michael@0: fh = _cairo_fixed_from_double (srcRect.size.height); michael@0: michael@0: if ((fw & CAIRO_FIXED_FRAC_MASK) <= CAIRO_FIXED_EPSILON && michael@0: @@ -1657,111 +1724,109 @@ _cairo_quartz_setup_source (cairo_quartz michael@0: michael@0: srcRect.size.width = round(srcRect.size.width); michael@0: srcRect.size.height = round(srcRect.size.height); michael@0: michael@0: xform = CGAffineTransformInvert (xform); michael@0: michael@0: srcRect = CGRectApplyAffineTransform (srcRect, xform); michael@0: michael@0: - surface->sourceImageRect = srcRect; michael@0: - michael@0: - return DO_TILED_IMAGE; michael@0: + state.imageRect = srcRect; michael@0: + state.action = DO_TILED_IMAGE; michael@0: + return state; michael@0: } michael@0: michael@0: /* Fall through to generic SURFACE case */ michael@0: } michael@0: michael@0: if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { michael@0: CGFloat patternAlpha = 1.0f; michael@0: CGColorSpaceRef patternSpace; michael@0: CGPatternRef pattern; michael@0: cairo_int_status_t status; michael@0: michael@0: status = _cairo_quartz_cairo_repeating_surface_pattern_to_quartz (surface, source, &pattern); michael@0: - if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) michael@0: - return DO_NOTHING; michael@0: - if (status) michael@0: - return DO_UNSUPPORTED; michael@0: - michael@0: - // Save before we change the pattern, colorspace, etc. so that michael@0: - // we can restore and make sure that quartz releases our michael@0: - // pattern (which may be stack allocated) michael@0: - CGContextSaveGState(surface->cgContext); michael@0: - michael@0: - patternSpace = CGColorSpaceCreatePattern(NULL); michael@0: - CGContextSetFillColorSpace (surface->cgContext, patternSpace); michael@0: - CGContextSetFillPattern (surface->cgContext, pattern, &patternAlpha); michael@0: - CGContextSetStrokeColorSpace (surface->cgContext, patternSpace); michael@0: - CGContextSetStrokePattern (surface->cgContext, pattern, &patternAlpha); michael@0: + if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) { michael@0: + state.action = DO_NOTHING; michael@0: + return state; michael@0: + } michael@0: + if (status) { michael@0: + state.action = DO_UNSUPPORTED; michael@0: + return state; michael@0: + } michael@0: + michael@0: + patternSpace = CGColorSpaceCreatePattern (NULL); michael@0: + CGContextSetFillColorSpace (context, patternSpace); michael@0: + CGContextSetFillPattern (context, pattern, &patternAlpha); michael@0: + CGContextSetStrokeColorSpace (context, patternSpace); michael@0: + CGContextSetStrokePattern (context, pattern, &patternAlpha); michael@0: CGColorSpaceRelease (patternSpace); michael@0: michael@0: /* Quartz likes to munge the pattern phase (as yet unexplained michael@0: * why); force it to 0,0 as we've already baked in the correct michael@0: * pattern translation into the pattern matrix michael@0: */ michael@0: - CGContextSetPatternPhase (surface->cgContext, CGSizeMake(0,0)); michael@0: - michael@0: - surface->sourcePattern = pattern; michael@0: - michael@0: - return DO_PATTERN; michael@0: + CGContextSetPatternPhase (context, CGSizeMake(0,0)); michael@0: + michael@0: + state.pattern = pattern; michael@0: + state.action = DO_PATTERN; michael@0: + return state; michael@0: } michael@0: michael@0: - return DO_UNSUPPORTED; michael@0: + state.action = DO_UNSUPPORTED; michael@0: + return state; michael@0: } michael@0: michael@0: +/** michael@0: + * 1) Tears down internal state used to draw the source michael@0: + * 2) Does CGContextRestoreGState(state->context) michael@0: + */ michael@0: static void michael@0: -_cairo_quartz_teardown_source (cairo_quartz_surface_t *surface, michael@0: - const cairo_pattern_t *source) michael@0: +_cairo_quartz_teardown_state (cairo_quartz_drawing_state_t *state) michael@0: { michael@0: - CGContextSetInterpolationQuality (surface->cgContext, surface->oldInterpolationQuality); michael@0: - michael@0: - if (surface->sourceImage) { michael@0: - CGImageRelease(surface->sourceImage); michael@0: - surface->sourceImage = NULL; michael@0: - michael@0: - cairo_surface_destroy(surface->sourceImageSurface); michael@0: - surface->sourceImageSurface = NULL; michael@0: + if (state->image) { michael@0: + CGImageRelease(state->image); michael@0: } michael@0: michael@0: - if (surface->sourceShading) { michael@0: - CGShadingRelease(surface->sourceShading); michael@0: - surface->sourceShading = NULL; michael@0: + if (state->imageSurface) { michael@0: + cairo_surface_destroy(state->imageSurface); michael@0: } michael@0: michael@0: - if (surface->sourcePattern) { michael@0: - CGPatternRelease(surface->sourcePattern); michael@0: - // To tear down the pattern and colorspace michael@0: - CGContextRestoreGState(surface->cgContext); michael@0: - michael@0: - surface->sourcePattern = NULL; michael@0: + if (state->shading) { michael@0: + CGShadingRelease(state->shading); michael@0: } michael@0: + michael@0: + if (state->pattern) { michael@0: + CGPatternRelease(state->pattern); michael@0: + } michael@0: + michael@0: + CGContextRestoreGState(state->context); michael@0: } michael@0: michael@0: michael@0: static void michael@0: -_cairo_quartz_draw_image (cairo_quartz_surface_t *surface, cairo_operator_t op, cairo_quartz_action_t action) michael@0: +_cairo_quartz_draw_image (cairo_quartz_drawing_state_t *state, cairo_operator_t op) michael@0: { michael@0: - assert (surface && surface->sourceImage && (action == DO_IMAGE || action == DO_TILED_IMAGE)); michael@0: - michael@0: - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); michael@0: - CGContextTranslateCTM (surface->cgContext, 0, surface->sourceImageRect.size.height); michael@0: - CGContextScaleCTM (surface->cgContext, 1, -1); michael@0: - michael@0: - if (action == DO_IMAGE) { michael@0: - CGContextDrawImage (surface->cgContext, surface->sourceImageRect, surface->sourceImage); michael@0: - if (!_cairo_operator_bounded_by_source(op)) { michael@0: - CGContextBeginPath (surface->cgContext); michael@0: - CGContextAddRect (surface->cgContext, surface->sourceImageRect); michael@0: - CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); michael@0: - CGContextSetRGBFillColor (surface->cgContext, 0, 0, 0, 0); michael@0: - CGContextEOFillPath (surface->cgContext); michael@0: + assert (state && state->image && (state->action == DO_IMAGE || state->action == DO_TILED_IMAGE)); michael@0: + michael@0: + CGContextConcatCTM (state->context, state->transform); michael@0: + CGContextTranslateCTM (state->context, 0, state->imageRect.size.height); michael@0: + CGContextScaleCTM (state->context, 1, -1); michael@0: + michael@0: + if (state->action == DO_IMAGE) { michael@0: + CGContextDrawImage (state->context, state->imageRect, state->image); michael@0: + if (!_cairo_operator_bounded_by_source (op)) { michael@0: + CGContextBeginPath (state->context); michael@0: + CGContextAddRect (state->context, state->imageRect); michael@0: + CGContextAddRect (state->context, CGContextGetClipBoundingBox (state->context)); michael@0: + CGContextSetRGBFillColor (state->context, 0, 0, 0, 0); michael@0: + CGContextEOFillPath (state->context); michael@0: } michael@0: } else michael@0: - CGContextDrawTiledImagePtr (surface->cgContext, surface->sourceImageRect, surface->sourceImage); michael@0: + CGContextDrawTiledImagePtr (state->context, state->imageRect, state->image); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * get source/dest image implementation michael@0: */ michael@0: michael@0: /* Read the image from the surface's front buffer */ michael@0: @@ -2098,52 +2163,44 @@ _cairo_quartz_surface_get_extents (void michael@0: static cairo_int_status_t michael@0: _cairo_quartz_surface_paint (void *abstract_surface, michael@0: cairo_operator_t op, michael@0: const cairo_pattern_t *source, michael@0: cairo_clip_t *clip) michael@0: { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; michael@0: - cairo_quartz_action_t action; michael@0: + cairo_quartz_drawing_state_t state; michael@0: michael@0: ND((stderr, "%p _cairo_quartz_surface_paint op %d source->type %d\n", surface, op, source->type)); michael@0: michael@0: if (IS_EMPTY(surface)) michael@0: return CAIRO_STATUS_SUCCESS; michael@0: michael@0: rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); michael@0: if (unlikely (rv)) michael@0: return rv; michael@0: michael@0: - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: - if (unlikely (rv)) michael@0: - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; michael@0: - michael@0: - action = _cairo_quartz_setup_source (surface, source, NULL); michael@0: - michael@0: - if (action == DO_SOLID || action == DO_PATTERN) { michael@0: - CGContextFillRect (surface->cgContext, CGRectMake(surface->extents.x, michael@0: - surface->extents.y, michael@0: - surface->extents.width, michael@0: - surface->extents.height)); michael@0: - } else if (action == DO_SHADING) { michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); michael@0: - CGContextDrawShading (surface->cgContext, surface->sourceShading); michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - _cairo_quartz_draw_image (surface, op, action); michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: - } else if (action != DO_NOTHING) { michael@0: + state = _cairo_quartz_setup_state (surface, source, op, NULL); michael@0: + michael@0: + if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: + CGContextFillRect (state.context, CGRectMake(surface->extents.x, michael@0: + surface->extents.y, michael@0: + surface->extents.width, michael@0: + surface->extents.height)); michael@0: + } else if (state.action == DO_SHADING) { michael@0: + CGContextConcatCTM (state.context, state.transform); michael@0: + CGContextDrawShading (state.context, state.shading); michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + _cairo_quartz_draw_image (&state, op); michael@0: + } else if (state.action != DO_NOTHING) { michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: } michael@0: michael@0: - _cairo_quartz_teardown_source (surface, source); michael@0: + _cairo_quartz_teardown_state (&state); michael@0: michael@0: ND((stderr, "-- paint\n")); michael@0: return rv; michael@0: } michael@0: michael@0: static cairo_bool_t michael@0: _cairo_quartz_source_needs_extents (const cairo_pattern_t *source) michael@0: { michael@0: @@ -2170,91 +2227,83 @@ _cairo_quartz_surface_fill (void *abstra michael@0: cairo_path_fixed_t *path, michael@0: cairo_fill_rule_t fill_rule, michael@0: double tolerance, michael@0: cairo_antialias_t antialias, michael@0: cairo_clip_t *clip) michael@0: { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; michael@0: - cairo_quartz_action_t action; michael@0: + cairo_quartz_drawing_state_t state; michael@0: quartz_stroke_t stroke; michael@0: CGPathRef path_for_unbounded = NULL; michael@0: michael@0: ND((stderr, "%p _cairo_quartz_surface_fill op %d source->type %d\n", surface, op, source->type)); michael@0: michael@0: if (IS_EMPTY(surface)) michael@0: return CAIRO_STATUS_SUCCESS; michael@0: michael@0: rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); michael@0: if (unlikely (rv)) michael@0: return rv; michael@0: michael@0: - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: - if (unlikely (rv)) michael@0: - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; michael@0: - michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - michael@0: - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); michael@0: - michael@0: if (_cairo_quartz_source_needs_extents (source)) michael@0: { michael@0: /* We don't need precise extents since these are only used to michael@0: compute the number of gradient reptitions needed to cover the michael@0: object. */ michael@0: cairo_rectangle_int_t path_extents; michael@0: _cairo_path_fixed_approximate_fill_extents (path, &path_extents); michael@0: - action = _cairo_quartz_setup_source (surface, source, &path_extents); michael@0: + state = _cairo_quartz_setup_state (surface, source, op, &path_extents); michael@0: } else { michael@0: - action = _cairo_quartz_setup_source (surface, source, NULL); michael@0: + state = _cairo_quartz_setup_state (surface, source, op, NULL); michael@0: } michael@0: michael@0: - CGContextBeginPath (surface->cgContext); michael@0: - michael@0: - stroke.cgContext = surface->cgContext; michael@0: + CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); michael@0: + michael@0: + CGContextBeginPath (state.context); michael@0: + michael@0: + stroke.cgContext = state.context; michael@0: stroke.ctm_inverse = NULL; michael@0: rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke); michael@0: if (rv) michael@0: goto BAIL; michael@0: michael@0: if (!_cairo_operator_bounded_by_mask(op) && CGContextCopyPathPtr) michael@0: - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext); michael@0: - michael@0: - if (action == DO_SOLID || action == DO_PATTERN) { michael@0: + path_for_unbounded = CGContextCopyPathPtr (state.context); michael@0: + michael@0: + if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: if (fill_rule == CAIRO_FILL_RULE_WINDING) michael@0: - CGContextFillPath (surface->cgContext); michael@0: + CGContextFillPath (state.context); michael@0: else michael@0: - CGContextEOFillPath (surface->cgContext); michael@0: - } else if (action == DO_SHADING) { michael@0: + CGContextEOFillPath (state.context); michael@0: + } else if (state.action == DO_SHADING) { michael@0: michael@0: // we have to clip and then paint the shading; we can't fill michael@0: // with the shading michael@0: if (fill_rule == CAIRO_FILL_RULE_WINDING) michael@0: - CGContextClip (surface->cgContext); michael@0: + CGContextClip (state.context); michael@0: else michael@0: - CGContextEOClip (surface->cgContext); michael@0: - michael@0: - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); michael@0: - CGContextDrawShading (surface->cgContext, surface->sourceShading); michael@0: - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { michael@0: + CGContextEOClip (state.context); michael@0: + michael@0: + CGContextConcatCTM (state.context, state.transform); michael@0: + CGContextDrawShading (state.context, state.shading); michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: if (fill_rule == CAIRO_FILL_RULE_WINDING) michael@0: - CGContextClip (surface->cgContext); michael@0: + CGContextClip (state.context); michael@0: else michael@0: - CGContextEOClip (surface->cgContext); michael@0: - michael@0: - _cairo_quartz_draw_image (surface, op, action); michael@0: - } else if (action != DO_NOTHING) { michael@0: + CGContextEOClip (state.context); michael@0: + michael@0: + _cairo_quartz_draw_image (&state, op); michael@0: + } else if (state.action != DO_NOTHING) { michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: } michael@0: michael@0: BAIL: michael@0: - _cairo_quartz_teardown_source (surface, source); michael@0: - michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: + _cairo_quartz_teardown_state (&state); michael@0: michael@0: if (path_for_unbounded) { michael@0: unbounded_op_data_t ub; michael@0: ub.op = UNBOUNDED_STROKE_FILL; michael@0: ub.u.stroke_fill.cgPath = path_for_unbounded; michael@0: ub.u.stroke_fill.fill_rule = fill_rule; michael@0: michael@0: _cairo_quartz_fixup_unbounded_operation (surface, &ub, antialias); michael@0: @@ -2274,44 +2323,49 @@ _cairo_quartz_surface_stroke (void *abst michael@0: cairo_matrix_t *ctm, michael@0: cairo_matrix_t *ctm_inverse, michael@0: double tolerance, michael@0: cairo_antialias_t antialias, michael@0: cairo_clip_t *clip) michael@0: { michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; michael@0: - cairo_quartz_action_t action; michael@0: + cairo_quartz_drawing_state_t state; michael@0: quartz_stroke_t stroke; michael@0: CGAffineTransform origCTM, strokeTransform; michael@0: CGPathRef path_for_unbounded = NULL; michael@0: michael@0: ND((stderr, "%p _cairo_quartz_surface_stroke op %d source->type %d\n", surface, op, source->type)); michael@0: michael@0: if (IS_EMPTY(surface)) michael@0: return CAIRO_STATUS_SUCCESS; michael@0: michael@0: rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); michael@0: if (unlikely (rv)) michael@0: return rv; michael@0: michael@0: - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: - if (unlikely (rv)) michael@0: - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; michael@0: + if (_cairo_quartz_source_needs_extents (source)) michael@0: + { michael@0: + cairo_rectangle_int_t path_extents; michael@0: + _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents); michael@0: + state = _cairo_quartz_setup_state (surface, source, op, &path_extents); michael@0: + } else { michael@0: + state = _cairo_quartz_setup_state (surface, source, op, NULL); michael@0: + } michael@0: michael@0: // Turning antialiasing off used to cause misrendering with michael@0: // single-pixel lines (e.g. 20,10.5 -> 21,10.5 end up being rendered as 2 pixels). michael@0: // That's been since fixed in at least 10.5, and in the latest 10.4 dot releases. michael@0: - CGContextSetShouldAntialias (surface->cgContext, (antialias != CAIRO_ANTIALIAS_NONE)); michael@0: - CGContextSetLineWidth (surface->cgContext, style->line_width); michael@0: - CGContextSetLineCap (surface->cgContext, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); michael@0: - CGContextSetLineJoin (surface->cgContext, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); michael@0: - CGContextSetMiterLimit (surface->cgContext, style->miter_limit); michael@0: - michael@0: - origCTM = CGContextGetCTM (surface->cgContext); michael@0: + CGContextSetShouldAntialias (state.context, (antialias != CAIRO_ANTIALIAS_NONE)); michael@0: + CGContextSetLineWidth (state.context, style->line_width); michael@0: + CGContextSetLineCap (state.context, _cairo_quartz_cairo_line_cap_to_quartz (style->line_cap)); michael@0: + CGContextSetLineJoin (state.context, _cairo_quartz_cairo_line_join_to_quartz (style->line_join)); michael@0: + CGContextSetMiterLimit (state.context, style->miter_limit); michael@0: + michael@0: + origCTM = CGContextGetCTM (state.context); michael@0: michael@0: if (style->dash && style->num_dashes) { michael@0: #define STATIC_DASH 32 michael@0: CGFloat sdash[STATIC_DASH]; michael@0: CGFloat *fdash = sdash; michael@0: double offset = style->dash_offset; michael@0: unsigned int max_dashes = style->num_dashes; michael@0: unsigned int k; michael@0: @@ -2330,90 +2384,75 @@ _cairo_quartz_surface_stroke (void *abst michael@0: if (max_dashes > STATIC_DASH) michael@0: fdash = _cairo_malloc_ab (max_dashes, sizeof (CGFloat)); michael@0: if (fdash == NULL) michael@0: return _cairo_error (CAIRO_STATUS_NO_MEMORY); michael@0: michael@0: for (k = 0; k < max_dashes; k++) michael@0: fdash[k] = (CGFloat) style->dash[k % style->num_dashes]; michael@0: } michael@0: - CGContextSetLineDash (surface->cgContext, offset, fdash, max_dashes); michael@0: + CGContextSetLineDash (state.context, offset, fdash, max_dashes); michael@0: if (fdash != sdash) michael@0: free (fdash); michael@0: } else michael@0: - CGContextSetLineDash (surface->cgContext, 0, NULL, 0); michael@0: - michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - michael@0: - michael@0: - if (_cairo_quartz_source_needs_extents (source)) michael@0: - { michael@0: - cairo_rectangle_int_t path_extents; michael@0: - _cairo_path_fixed_approximate_stroke_extents (path, style, ctm, &path_extents); michael@0: - action = _cairo_quartz_setup_source (surface, source, &path_extents); michael@0: - } else { michael@0: - action = _cairo_quartz_setup_source (surface, source, NULL); michael@0: - } michael@0: + CGContextSetLineDash (state.context, 0, NULL, 0); michael@0: michael@0: _cairo_quartz_cairo_matrix_to_quartz (ctm, &strokeTransform); michael@0: - CGContextConcatCTM (surface->cgContext, strokeTransform); michael@0: - michael@0: - CGContextBeginPath (surface->cgContext); michael@0: - michael@0: - stroke.cgContext = surface->cgContext; michael@0: + CGContextConcatCTM (state.context, strokeTransform); michael@0: + michael@0: + CGContextBeginPath (state.context); michael@0: + michael@0: + stroke.cgContext = state.context; michael@0: stroke.ctm_inverse = ctm_inverse; michael@0: rv = _cairo_quartz_cairo_path_to_quartz_context (path, &stroke); michael@0: if (rv) michael@0: goto BAIL; michael@0: michael@0: if (!_cairo_operator_bounded_by_mask (op) && CGContextCopyPathPtr) michael@0: - path_for_unbounded = CGContextCopyPathPtr (surface->cgContext); michael@0: - michael@0: - if (action == DO_SOLID || action == DO_PATTERN) { michael@0: - CGContextStrokePath (surface->cgContext); michael@0: - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE) { michael@0: - CGContextReplacePathWithStrokedPath (surface->cgContext); michael@0: - CGContextClip (surface->cgContext); michael@0: - michael@0: - CGContextSetCTM (surface->cgContext, origCTM); michael@0: - _cairo_quartz_draw_image (surface, op, action); michael@0: - } else if (action == DO_SHADING) { michael@0: - CGContextReplacePathWithStrokedPath (surface->cgContext); michael@0: - CGContextClip (surface->cgContext); michael@0: - michael@0: - CGContextSetCTM (surface->cgContext, origCTM); michael@0: - michael@0: - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); michael@0: - CGContextDrawShading (surface->cgContext, surface->sourceShading); michael@0: - } else if (action != DO_NOTHING) { michael@0: + path_for_unbounded = CGContextCopyPathPtr (state.context); michael@0: + michael@0: + if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: + CGContextStrokePath (state.context); michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + CGContextReplacePathWithStrokedPath (state.context); michael@0: + CGContextClip (state.context); michael@0: + michael@0: + CGContextSetCTM (state.context, origCTM); michael@0: + _cairo_quartz_draw_image (&state, op); michael@0: + } else if (state.action == DO_SHADING) { michael@0: + CGContextReplacePathWithStrokedPath (state.context); michael@0: + CGContextClip (state.context); michael@0: + michael@0: + CGContextSetCTM (state.context, origCTM); michael@0: + michael@0: + CGContextConcatCTM (state.context, state.transform); michael@0: + CGContextDrawShading (state.context, state.shading); michael@0: + } else if (state.action != DO_NOTHING) { michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: + goto BAIL; michael@0: } michael@0: michael@0: + if (path_for_unbounded) { michael@0: + CGContextSetCTM (state.context, origCTM); michael@0: + CGContextConcatCTM (state.context, strokeTransform); michael@0: + michael@0: + CGContextBeginPath (state.context); michael@0: + CGContextAddPath (state.context, path_for_unbounded); michael@0: + CGPathRelease (path_for_unbounded); michael@0: + michael@0: + CGContextReplacePathWithStrokedPath (state.context); michael@0: + michael@0: + CGContextAddRect (state.context, CGContextGetClipBoundingBox (state.context)); michael@0: + michael@0: + CGContextSetRGBFillColor (state.context, 0., 0., 0., 0.); michael@0: + CGContextEOFillPath (state.context); michael@0: + } michael@0: + michael@0: BAIL: michael@0: - _cairo_quartz_teardown_source (surface, source); michael@0: - michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: - michael@0: - if (path_for_unbounded) { michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - CGContextConcatCTM (surface->cgContext, strokeTransform); michael@0: - michael@0: - CGContextBeginPath (surface->cgContext); michael@0: - CGContextAddPath (surface->cgContext, path_for_unbounded); michael@0: - CGPathRelease (path_for_unbounded); michael@0: - michael@0: - CGContextReplacePathWithStrokedPath (surface->cgContext); michael@0: - michael@0: - CGContextAddRect (surface->cgContext, CGContextGetClipBoundingBox (surface->cgContext)); michael@0: - michael@0: - CGContextSetRGBFillColor (surface->cgContext, 0., 0., 0., 0.); michael@0: - CGContextEOFillPath (surface->cgContext); michael@0: - michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: - } michael@0: + _cairo_quartz_teardown_state (&state); michael@0: michael@0: ND((stderr, "-- stroke\n")); michael@0: return rv; michael@0: } michael@0: michael@0: #if CAIRO_HAS_QUARTZ_FONT michael@0: static cairo_int_status_t michael@0: _cairo_quartz_surface_show_glyphs (void *abstract_surface, michael@0: @@ -2429,17 +2468,17 @@ _cairo_quartz_surface_show_glyphs (void michael@0: #define STATIC_BUF_SIZE 64 michael@0: CGGlyph glyphs_static[STATIC_BUF_SIZE]; michael@0: CGSize cg_advances_static[STATIC_BUF_SIZE]; michael@0: CGGlyph *cg_glyphs = &glyphs_static[0]; michael@0: CGSize *cg_advances = &cg_advances_static[0]; michael@0: michael@0: cairo_quartz_surface_t *surface = (cairo_quartz_surface_t *) abstract_surface; michael@0: cairo_int_status_t rv = CAIRO_STATUS_SUCCESS; michael@0: - cairo_quartz_action_t action; michael@0: + cairo_quartz_drawing_state_t state; michael@0: float xprev, yprev; michael@0: int i; michael@0: CGFontRef cgfref = NULL; michael@0: michael@0: cairo_bool_t isClipping = FALSE; michael@0: cairo_bool_t didForceFontSmoothing = FALSE; michael@0: michael@0: if (IS_EMPTY(surface)) michael@0: @@ -2450,65 +2489,59 @@ _cairo_quartz_surface_show_glyphs (void michael@0: michael@0: if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_QUARTZ) michael@0: return CAIRO_INT_STATUS_UNSUPPORTED; michael@0: michael@0: rv = _cairo_surface_clipper_set_clip (&surface->clipper, clip); michael@0: if (unlikely (rv)) michael@0: return rv; michael@0: michael@0: - rv = _cairo_quartz_surface_set_cairo_operator (surface, op); michael@0: - if (unlikely (rv)) michael@0: - return rv == CAIRO_INT_STATUS_NOTHING_TO_DO ? CAIRO_STATUS_SUCCESS : rv; michael@0: - michael@0: - CGContextSaveGState (surface->cgContext); michael@0: - michael@0: if (_cairo_quartz_source_needs_extents (source)) michael@0: { michael@0: cairo_rectangle_int_t glyph_extents; michael@0: _cairo_scaled_font_glyph_device_extents (scaled_font, glyphs, num_glyphs, michael@0: &glyph_extents, NULL); michael@0: - action = _cairo_quartz_setup_source (surface, source, &glyph_extents); michael@0: + state = _cairo_quartz_setup_state (surface, source, op, &glyph_extents); michael@0: } else { michael@0: - action = _cairo_quartz_setup_source (surface, source, NULL); michael@0: + state = _cairo_quartz_setup_state (surface, source, op, NULL); michael@0: } michael@0: michael@0: - if (action == DO_SOLID || action == DO_PATTERN) { michael@0: - CGContextSetTextDrawingMode (surface->cgContext, kCGTextFill); michael@0: - } else if (action == DO_IMAGE || action == DO_TILED_IMAGE || action == DO_SHADING) { michael@0: - CGContextSetTextDrawingMode (surface->cgContext, kCGTextClip); michael@0: + if (state.action == DO_SOLID || state.action == DO_PATTERN) { michael@0: + CGContextSetTextDrawingMode (state.context, kCGTextFill); michael@0: + } else if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE || state.action == DO_SHADING) { michael@0: + CGContextSetTextDrawingMode (state.context, kCGTextClip); michael@0: isClipping = TRUE; michael@0: } else { michael@0: - if (action != DO_NOTHING) michael@0: + if (state.action != DO_NOTHING) michael@0: rv = CAIRO_INT_STATUS_UNSUPPORTED; michael@0: goto BAIL; michael@0: } michael@0: michael@0: /* this doesn't addref */ michael@0: cgfref = _cairo_quartz_scaled_font_get_cg_font_ref (scaled_font); michael@0: - CGContextSetFont (surface->cgContext, cgfref); michael@0: - CGContextSetFontSize (surface->cgContext, 1.0); michael@0: + CGContextSetFont (state.context, cgfref); michael@0: + CGContextSetFontSize (state.context, 1.0); michael@0: michael@0: switch (scaled_font->options.antialias) { michael@0: case CAIRO_ANTIALIAS_SUBPIXEL: michael@0: - CGContextSetShouldAntialias (surface->cgContext, TRUE); michael@0: - CGContextSetShouldSmoothFonts (surface->cgContext, TRUE); michael@0: + CGContextSetShouldAntialias (state.context, TRUE); michael@0: + CGContextSetShouldSmoothFonts (state.context, TRUE); michael@0: if (CGContextSetAllowsFontSmoothingPtr && michael@0: - !CGContextGetAllowsFontSmoothingPtr (surface->cgContext)) michael@0: + !CGContextGetAllowsFontSmoothingPtr (state.context)) michael@0: { michael@0: didForceFontSmoothing = TRUE; michael@0: - CGContextSetAllowsFontSmoothingPtr (surface->cgContext, TRUE); michael@0: + CGContextSetAllowsFontSmoothingPtr (state.context, TRUE); michael@0: } michael@0: break; michael@0: case CAIRO_ANTIALIAS_NONE: michael@0: - CGContextSetShouldAntialias (surface->cgContext, FALSE); michael@0: + CGContextSetShouldAntialias (state.context, FALSE); michael@0: break; michael@0: case CAIRO_ANTIALIAS_GRAY: michael@0: - CGContextSetShouldAntialias (surface->cgContext, TRUE); michael@0: - CGContextSetShouldSmoothFonts (surface->cgContext, FALSE); michael@0: + CGContextSetShouldAntialias (state.context, TRUE); michael@0: + CGContextSetShouldSmoothFonts (state.context, FALSE); michael@0: break; michael@0: case CAIRO_ANTIALIAS_DEFAULT: michael@0: /* Don't do anything */ michael@0: break; michael@0: } michael@0: michael@0: if (num_glyphs > STATIC_BUF_SIZE) { michael@0: cg_glyphs = (CGGlyph*) _cairo_malloc_ab (num_glyphs, sizeof(CGGlyph)); michael@0: @@ -2532,17 +2565,17 @@ _cairo_quartz_surface_show_glyphs (void michael@0: textTransform = CGAffineTransformScale (textTransform, 1.0, -1.0); michael@0: textTransform = CGAffineTransformConcat (CGAffineTransformMake(scaled_font->ctm.xx, michael@0: -scaled_font->ctm.yx, michael@0: -scaled_font->ctm.xy, michael@0: scaled_font->ctm.yy, michael@0: 0., 0.), michael@0: textTransform); michael@0: michael@0: - CGContextSetTextMatrix (surface->cgContext, textTransform); michael@0: + CGContextSetTextMatrix (state.context, textTransform); michael@0: michael@0: /* Convert our glyph positions to glyph advances. We need n-1 advances, michael@0: * since the advance at index 0 is applied after glyph 0. */ michael@0: xprev = glyphs[0].x; michael@0: yprev = glyphs[0].y; michael@0: michael@0: cg_glyphs[0] = glyphs[0].index; michael@0: michael@0: @@ -2569,40 +2602,38 @@ _cairo_quartz_surface_show_glyphs (void michael@0: michael@0: #if 0 michael@0: for (i = 0; i < num_glyphs; i++) { michael@0: ND((stderr, "[%d: %d %f,%f]\n", i, cg_glyphs[i], cg_advances[i].width, cg_advances[i].height)); michael@0: } michael@0: #endif michael@0: michael@0: /* Translate to the first glyph's position before drawing */ michael@0: - ctm = CGContextGetCTM (surface->cgContext); michael@0: - CGContextTranslateCTM (surface->cgContext, glyphs[0].x, glyphs[0].y); michael@0: - michael@0: - CGContextShowGlyphsWithAdvances (surface->cgContext, michael@0: + ctm = CGContextGetCTM (state.context); michael@0: + CGContextTranslateCTM (state.context, glyphs[0].x, glyphs[0].y); michael@0: + michael@0: + CGContextShowGlyphsWithAdvances (state.context, michael@0: cg_glyphs, michael@0: cg_advances, michael@0: num_glyphs); michael@0: michael@0: - CGContextSetCTM (surface->cgContext, ctm); michael@0: - michael@0: - if (action == DO_IMAGE || action == DO_TILED_IMAGE) { michael@0: - _cairo_quartz_draw_image (surface, op, action); michael@0: - } else if (action == DO_SHADING) { michael@0: - CGContextConcatCTM (surface->cgContext, surface->sourceTransform); michael@0: - CGContextDrawShading (surface->cgContext, surface->sourceShading); michael@0: + CGContextSetCTM (state.context, ctm); michael@0: + michael@0: + if (state.action == DO_IMAGE || state.action == DO_TILED_IMAGE) { michael@0: + _cairo_quartz_draw_image (&state, op); michael@0: + } else if (state.action == DO_SHADING) { michael@0: + CGContextConcatCTM (state.context, state.transform); michael@0: + CGContextDrawShading (state.context, state.shading); michael@0: } michael@0: michael@0: BAIL: michael@0: - _cairo_quartz_teardown_source (surface, source); michael@0: - michael@0: if (didForceFontSmoothing) michael@0: - CGContextSetAllowsFontSmoothingPtr (surface->cgContext, FALSE); michael@0: - michael@0: - CGContextRestoreGState (surface->cgContext); michael@0: + CGContextSetAllowsFontSmoothingPtr (state.context, FALSE); michael@0: + michael@0: + _cairo_quartz_teardown_state (&state); michael@0: michael@0: if (rv == CAIRO_STATUS_SUCCESS && michael@0: cgfref && michael@0: !_cairo_operator_bounded_by_mask (op)) michael@0: { michael@0: unbounded_op_data_t ub; michael@0: ub.op = UNBOUNDED_SHOW_GLYPHS; michael@0: