From 3a483c2896c28142a90bd0e282af3862e066adfe Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 13 Aug 2009 00:19:03 +0100 Subject: [PATCH] [gstate] Convert simple mask() into a paint() As using mask() prevents various optimisations in the backends (for example the use of geometric clips) and for some may trigger fallbacks, perform the simplifications usually done (too late) by the pattern layer in the generic gstate layer. This allows us on the odd occasion to transform a mask() into a paint() but perhaps more importantly removes the need for identical transformations in each backend. --- src/cairo-gstate.c | 101 +++++++++++++++++++++++++++++++++++++++++---- src/cairoint.h | 9 ++-- 2 files changed, 98 insertions(+), 12 deletions(-) diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 902de85b7..ed0e8779f 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -817,13 +817,71 @@ _cairo_gstate_path_extents (cairo_gstate_t *gstate, *y2 = py2; } +static void +_cairo_gstate_copy_pattern (cairo_pattern_t *pattern, + const cairo_pattern_t *original) +{ + /* First check if the we can replace the original with a much simpler + * pattern. For example, gradients that are uniform or just have a single + * stop can be replace with a solid. + */ + switch (original->type) { + case CAIRO_PATTERN_TYPE_SOLID: + case CAIRO_PATTERN_TYPE_SURFACE: + break; + + case CAIRO_PATTERN_TYPE_LINEAR: + case CAIRO_PATTERN_TYPE_RADIAL: + { + cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) original; + + /* fast path for gradients with less than 2 color stops */ + if (src->n_stops < 2) { + if (src->n_stops) { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + &src->stops->color, + CAIRO_CONTENT_COLOR_ALPHA); + } else { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + CAIRO_COLOR_TRANSPARENT, + CAIRO_CONTENT_ALPHA); + } + + return; + } else { + unsigned int i; + + /* Is the gradient a uniform colour? + * Happens more often than you would believe. + */ + for (i = 1; i < src->n_stops; i++) { + if (! _cairo_color_equal (&src->stops[0].color, + &src->stops[i].color)) + { + break; + } + } + if (i == src->n_stops) { + _cairo_pattern_init_solid ((cairo_solid_pattern_t *) pattern, + &src->stops->color, + CAIRO_CONTENT_COLOR_ALPHA); + + return; + } + } + } + } + + _cairo_pattern_init_static_copy (pattern, original); +} + static void _cairo_gstate_copy_transformed_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern, - cairo_pattern_t *original, - cairo_matrix_t *ctm_inverse) + const cairo_pattern_t *original, + const cairo_matrix_t *ctm_inverse) { - _cairo_pattern_init_static_copy (pattern, original); + _cairo_gstate_copy_pattern (pattern, original); /* apply device_transform first so that it is transformed by ctm_inverse */ if (original->type == CAIRO_PATTERN_TYPE_SURFACE) { @@ -934,14 +992,41 @@ _cairo_gstate_mask (cairo_gstate_t *gstate, if (_clipped (gstate)) return CAIRO_STATUS_SUCCESS; + if (_cairo_pattern_is_opaque (mask)) + return _cairo_gstate_paint (gstate); + _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base); _cairo_gstate_copy_transformed_mask (gstate, &mask_pattern.base, mask); - status = _cairo_surface_mask (gstate->target, - gstate->op, - &source_pattern.base, - &mask_pattern.base, - _gstate_get_clip (gstate, &clip)); + /* XXX: This optimization assumes that there is no color + * information in mask, so this will need to change if we + * support RENDER-style 4-channel masks. + */ + if (source_pattern.type == CAIRO_PATTERN_TYPE_SOLID && + mask_pattern.type == CAIRO_PATTERN_TYPE_SOLID) + { + cairo_color_t combined; + + combined = source_pattern.solid.color; + _cairo_color_multiply_alpha (&combined, mask_pattern.solid.color.alpha); + + _cairo_pattern_init_solid (&source_pattern.solid, &combined, + source_pattern.solid.content | + mask_pattern.solid.content); + + status = _cairo_surface_paint (gstate->target, + gstate->op, + &source_pattern.base, + _gstate_get_clip (gstate, &clip)); + } + else + { + status = _cairo_surface_mask (gstate->target, + gstate->op, + &source_pattern.base, + &mask_pattern.base, + _gstate_get_clip (gstate, &clip)); + } _cairo_clip_fini (&clip); return status; diff --git a/src/cairoint.h b/src/cairoint.h index c4b22f247..258498187 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -937,11 +937,12 @@ typedef union { } cairo_gradient_pattern_union_t; typedef union { - cairo_pattern_t base; + cairo_pattern_type_t type; + cairo_pattern_t base; - cairo_solid_pattern_t solid; - cairo_surface_pattern_t surface; - cairo_gradient_pattern_union_t gradient; + cairo_solid_pattern_t solid; + cairo_surface_pattern_t surface; + cairo_gradient_pattern_union_t gradient; } cairo_pattern_union_t; typedef struct _cairo_surface_attributes {