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