summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrea Canciani <ranma42@gmail.com>2010-06-01 19:39:02 +0200
committerAndrea Canciani <ranma42@gmail.com>2010-06-10 16:07:42 +0200
commit561625ee3bd2732457eaaf28937edf557ee7661d (patch)
tree523d897aedffaaab8ad16e8de0fcfeac815612bd /src
parentbaaf312e047a9bea6f54e63cd6534c2ed7448523 (diff)
pattern: improve clear/opaque check functions
_cairo_pattern_is_opaque was missing some checks about the extend type. Conversely _cairo_pattern_is_clear was being too strict about gradients.
Diffstat (limited to 'src')
-rw-r--r--src/cairo-pattern.c112
-rw-r--r--src/cairoint.h4
2 files changed, 108 insertions, 8 deletions
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 27a7a801..2aa80d5e 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1726,6 +1726,69 @@ _cairo_pattern_reset_solid_surface_cache (void)
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
}
+static void
+_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
+ const cairo_rectangle_int_t *extents,
+ double t[2])
+{
+ double t0, tdx, tdy;
+ double p1x, p1y, pdx, pdy, invsqnorm;
+
+ p1x = _cairo_fixed_to_double (linear->p1.x);
+ p1y = _cairo_fixed_to_double (linear->p1.y);
+ pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
+ pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
+ invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+ pdx *= invsqnorm;
+ pdy *= invsqnorm;
+
+ t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
+ tdx = extents->width * pdx;
+ tdy = extents->height * pdy;
+
+ t[0] = t[1] = t0;
+ if (tdx < 0)
+ t[0] += tdx;
+ else
+ t[1] += tdx;
+
+ if (tdy < 0)
+ t[0] += tdy;
+ else
+ t[1] += tdy;
+}
+
+static cairo_bool_t
+_gradient_is_clear (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents)
+{
+ unsigned int i;
+
+ /* Check if the extents intersect the drawn part of the pattern.
+ * TODO: radial gradients, other linear extend modes.
+ */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
+ return TRUE;
+
+ if (extents != NULL) {
+ double t[2];
+ _extents_to_linear_parameter (linear, extents, t);
+ if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0))
+ return TRUE;
+ }
+ }
+ }
+
+ for (i = 0; i < gradient->n_stops; i++)
+ if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color))
+ return FALSE;
+
+ return TRUE;
+}
+
/**
* _cairo_pattern_is_opaque_solid
*
@@ -1779,13 +1842,46 @@ _surface_is_opaque (const cairo_surface_pattern_t *pattern,
}
static cairo_bool_t
-_gradient_is_opaque (const cairo_gradient_pattern_t *gradient)
+_surface_is_clear (const cairo_surface_pattern_t *pattern)
+{
+ cairo_rectangle_int_t extents;
+
+ if (_cairo_surface_get_extents (pattern->surface, &extents) &&
+ (extents.width == 0 || extents.height == 0))
+ return TRUE;
+
+ return pattern->surface->is_clear &&
+ pattern->surface->content & CAIRO_CONTENT_ALPHA;
+}
+
+static cairo_bool_t
+_gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
+ const cairo_rectangle_int_t *extents)
{
unsigned int i;
if (gradient->n_stops == 0)
return FALSE;
+ /* TODO: radial gradients, degenerate linear gradients */
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
+ return FALSE;
+
+ if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ double t[2];
+
+ if (extents == NULL)
+ return FALSE;
+
+ _extents_to_linear_parameter (linear, extents, t);
+ if (t[0] < 0.0 || t[1] > 1.0)
+ return FALSE;
+ }
+ } else
+ return FALSE;
+
for (i = 0; i < gradient->n_stops; i++)
if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color))
return FALSE;
@@ -1819,7 +1915,7 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
return _surface_is_opaque (&pattern->surface, extents);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
- return _gradient_is_opaque (&pattern->gradient.base);
+ return _gradient_is_opaque (&pattern->gradient.base, extents);
}
ASSERT_NOT_REACHED;
@@ -1837,16 +1933,16 @@ _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
pattern = (cairo_pattern_union_t *) abstract_pattern;
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
- return pattern->solid.color.alpha_short == 0x0000;
+ return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color);
case CAIRO_PATTERN_TYPE_SURFACE:
- return pattern->surface.surface->is_clear &&
- pattern->surface.surface->content & CAIRO_CONTENT_ALPHA;
- default:
- ASSERT_NOT_REACHED;
+ return _surface_is_clear (&pattern->surface);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
- return pattern->gradient.base.n_stops == 0;
+ return _gradient_is_clear (&pattern->gradient.base, NULL);
}
+
+ ASSERT_NOT_REACHED;
+ return FALSE;
}
/**
diff --git a/src/cairoint.h b/src/cairoint.h
index 22ff16da..59af6844 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -145,10 +145,14 @@ do { \
#define COMPILE_TIME_ASSERT0(condition, line) COMPILE_TIME_ASSERT1(condition, line)
#define COMPILE_TIME_ASSERT(condition) COMPILE_TIME_ASSERT0(condition, __LINE__)
+#define CAIRO_ALPHA_IS_CLEAR(alpha) ((alpha) <= ((double)0x00ff / (double)0xffff))
+#define CAIRO_ALPHA_SHORT_IS_CLEAR(alpha) ((alpha) <= 0x00ff)
+
#define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff))
#define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00)
#define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0)
+#define CAIRO_COLOR_IS_CLEAR(color) CAIRO_ALPHA_SHORT_IS_CLEAR ((color)->alpha_short)
#define CAIRO_COLOR_IS_OPAQUE(color) CAIRO_ALPHA_SHORT_IS_OPAQUE ((color)->alpha_short)
/* Reverse the bits in a byte with 7 operations (no 64-bit):