Improve dash pattern approximation

Dash pattern approximation was taking the caps style into account
only for coverage computation, but not when computing the new
pattern, thus the computed approximation had a higher coverage if
the caps style was SQUARE or ROUND.

Reviewed-by: M. Joonas Pihlaja <jpihlaja@cc.helsinki.fi>
This commit is contained in:
Andrea Canciani 2010-01-15 10:44:58 +01:00
parent 8d7841048b
commit 45c795a182

View file

@ -251,8 +251,60 @@ _cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
*num_dashes = 2;
dashes[0] = scale * coverage;
dashes[1] = scale * (1.0 - coverage);
/*
* We want to create a new dash pattern with the same relative coverage,
* but composed of just 2 elements with total length equal to scale.
* Based on the formula in _cairo_stroke_style_dash_stroked:
* scale * coverage = dashes[0] + cap_scale * MIN (dashes[1], line_width)
* = MIN (dashes[0] + cap_scale * (scale - dashes[0]),
* dashes[0] + cap_scale * line_width) =
* = MIN (dashes[0] * (1 - cap_scale) + cap_scale * scale,
* dashes[0] + cap_scale * line_width)
*
* Solving both cases we get:
* dashes[0] = scale * (coverage - cap_scale) / (1 - cap_scale)
* when scale - dashes[0] <= line_width
* dashes[0] = scale * coverage - cap_scale * line_width
* when scale - dashes[0] > line_width.
*
* Comparing the two cases we get:
* second > first
* second > scale * (coverage - cap_scale) / (1 - cap_scale)
* second - cap_scale * second - scale * coverage + scale * cap_scale > 0
* (scale * coverage - cap_scale * line_width) - cap_scale * second - scale * coverage + scale * cap_scale > 0
* - line_width - second + scale > 0
* scale - second > line_width
* which is the condition for the second solution to be the valid one.
* So when second > first, the second solution is the correct one (i.e.
* the solution is always MAX (first, second).
*/
switch (style->line_cap) {
default:
ASSERT_NOT_REACHED;
dashes[0] = 0.0;
break;
case CAIRO_LINE_CAP_BUTT:
/* Simplified formula (substituting 0 for cap_scale): */
dashes[0] = scale * coverage;
break;
case CAIRO_LINE_CAP_ROUND:
dashes[0] = MAX(scale * (coverage - ROUND_MINSQ_APPROXIMATION) / (1.0 - ROUND_MINSQ_APPROXIMATION),
scale * coverage - ROUND_MINSQ_APPROXIMATION * style->line_width);
break;
case CAIRO_LINE_CAP_SQUARE:
/*
* Special attention is needed to handle the case cap_scale == 1 (since the first solution
* is either indeterminate or -inf in this case). Since dash lengths are always >=0, using
* 0 as first solution always leads to the correct solution.
*/
dashes[0] = MAX(0.0, scale * coverage - style->line_width);
break;
}
dashes[1] = scale - dashes[0];
*dash_offset = on ? 0.0 : dashes[0];
}