pattern: Simplify degenerate linear pattern to solid colors

Degenerate linear patterns are considered clear if they have
EXTEND_NONE, the average of the first and the last stop if they are
EXTEND_PAD, the weighted average of the stops (based on the size of
the interpolation range in which they are active, just like integrating
over the whole interpolation range and taking the average) if they are
EXTEND_REPEAT or EXTEND_REFLECT.

Fixes degenerate-linear-gradient
This commit is contained in:
Andrea Canciani 2010-08-04 14:22:23 +02:00
parent d54a5a9fc9
commit ae2b7b13cd
2 changed files with 117 additions and 2 deletions

View file

@ -1816,6 +1816,116 @@ _gradient_is_clear (const cairo_gradient_pattern_t *gradient,
return TRUE;
}
static void
_gradient_color_average (const cairo_gradient_pattern_t *gradient,
cairo_color_t *color)
{
double delta0, delta1;
double r, g, b, a;
unsigned int i, start = 1, end;
assert (gradient->n_stops > 0);
assert (gradient->base.extend != CAIRO_EXTEND_NONE);
if (gradient->n_stops == 1) {
_cairo_color_init_rgba (color,
gradient->stops[0].color.red,
gradient->stops[0].color.green,
gradient->stops[0].color.blue,
gradient->stops[0].color.alpha);
return;
}
end = gradient->n_stops - 1;
switch (gradient->base.extend) {
case CAIRO_EXTEND_REPEAT:
/*
* Sa, Sb and Sy, Sz are the first two and last two stops respectively.
* The weight of the first and last stop can be computed as the area of
* the following triangles (taken with height 1, since the whole [0-1]
* will have total weight 1 this way): b*h/2
*
* + +
* / |\ / | \
* / | \ / | \
* / | \ / | \
* ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~
* -1+Sz 0 Sa Sb Sy Sz 1 1+Sa
*
* For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2
* For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2
* Halving the result is done after summing up all the areas.
*/
delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset;
delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset;
break;
case CAIRO_EXTEND_REFLECT:
/*
* Sa, Sb and Sy, Sz are the first two and last two stops respectively.
* The weight of the first and last stop can be computed as the area of
* the following trapezoids (taken with height 1, since the whole [0-1]
* will have total weight 1 this way): (b+B)*h/2
*
* +-------+ +---+
* | |\ / | |
* | | \ / | |
* | | \ / | |
* +-------+---+~~~~~~~+-------+---+
* 0 Sa Sb Sy Sz 1
*
* For the first stop: (Sa+Sb)/2
* For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2
* Halving the result is done after summing up all the areas.
*/
delta0 = gradient->stops[0].offset + gradient->stops[1].offset;
delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset;
break;
case CAIRO_EXTEND_PAD:
/* PAD is computed as the average of the first and last stop:
* - take both of them with weight 1 (they will be halved
* after the whole sum has been computed).
* - avoid summing any of the inner stops.
*/
delta0 = delta1 = 1.0;
start = end;
break;
case CAIRO_EXTEND_NONE:
default:
ASSERT_NOT_REACHED;
_cairo_color_init_rgba (color, 0, 0, 0, 0);
return;
}
r = delta0 * gradient->stops[0].color.red;
g = delta0 * gradient->stops[0].color.green;
b = delta0 * gradient->stops[0].color.blue;
a = delta0 * gradient->stops[0].color.alpha;
for (i = start; i < end; ++i) {
/* Inner stops weight is the same as the area of the triangle they influence
* (which goes from the stop before to the stop after), again with height 1
* since the whole must sum up to 1: b*h/2
* Halving is done after the whole sum has been computed.
*/
double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset;
r += delta * gradient->stops[i].color.red;
g += delta * gradient->stops[i].color.green;
b += delta * gradient->stops[i].color.blue;
a += delta * gradient->stops[i].color.alpha;
}
r += delta1 * gradient->stops[end].color.red;
g += delta1 * gradient->stops[end].color.green;
b += delta1 * gradient->stops[end].color.blue;
a += delta1 * gradient->stops[end].color.alpha;
_cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5);
}
/**
* _cairo_gradient_pattern_is_solid
*
@ -1838,10 +1948,15 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
/* TODO: radial, degenerate linear */
/* TODO: radial */
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
if (_linear_pattern_is_degenerate (linear)) {
_gradient_color_average (gradient, color);
return TRUE;
}
if (gradient->base.extend == CAIRO_EXTEND_NONE) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
double t[2];
/* We already know that the pattern is not clear, thus if some

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 322 B