From ae2b7b13cd5fdeaee44496056bb99f497346e262 Mon Sep 17 00:00:00 2001 From: Andrea Canciani Date: Wed, 4 Aug 2010 14:22:23 +0200 Subject: [PATCH] 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 --- src/cairo-pattern.c | 119 +++++++++++++++++++++++- test/degenerate-linear-gradient.ref.png | Bin 232 -> 322 bytes 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c index 59309f8c1..1b79011be 100644 --- a/src/cairo-pattern.c +++ b/src/cairo-pattern.c @@ -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 diff --git a/test/degenerate-linear-gradient.ref.png b/test/degenerate-linear-gradient.ref.png index e44cc335ea08405ed26ff69ca5e5b31210f480e8..1de7ca67bd7c46a91594b264c925bb64b247a1bc 100644 GIT binary patch delta 277 zcmaFCc!+6&S^alU7srr@)?}N42Mo+w85tQ8>y&(WlsS8J?r#dnW7+;CoQ;*u}9Af%;n5394xR{yc94UEq!s{f^jSLxA VCb9L`@_8_TfTydU%Q~loCIEK?aHIeL delta 186 zcmV;r07d`80_XvdHh-N-L_t(oh3%M24uBvGM2qoW3^(Iu;>}zTiHf3vana!|3!$0N z4j&UjkUidet&u^P;8HubFEPbl0FLo|Dj*{B+B+7wnsd62TZio9T#=NKYj3&K8e~=R z*75SXDw7Iw%8r9F+ZL$E*gaV_U9DcY)SC8t^3T>O}Jx+j0|zD@pq oh~GvWQ1|EJs&+`&Dw#{|0)#AL?6=keUH||907*qoM6N<$g5XbEy#N3J