mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-06-05 07:58:17 +02:00
Improve stroking of densely dashed styles
Add some auxiliary functions to cairo-stroke-style to compute properties of the dashed patterns (period, "on" coverage) and to generate approximations when the dash pattern is sub-tolerance. These functions are used in cairo-path-stroke to simplify dash patterns stroked by cairo. Fixes dash-infinite-loop See http://bugs.freedesktop.org/show_bug.cgi?id=24702
This commit is contained in:
parent
9c24288c82
commit
26e9f14906
3 changed files with 157 additions and 6 deletions
|
|
@ -51,6 +51,7 @@ typedef struct _cairo_stroker_dash {
|
|||
|
||||
double dash_offset;
|
||||
const double *dashes;
|
||||
double approximate_dashes[2];
|
||||
unsigned int num_dashes;
|
||||
} cairo_stroker_dash_t;
|
||||
|
||||
|
|
@ -137,15 +138,25 @@ _cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
|
|||
|
||||
static void
|
||||
_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
|
||||
const cairo_stroke_style_t *style)
|
||||
const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
double tolerance)
|
||||
{
|
||||
dash->dashed = style->dash != NULL;
|
||||
if (! dash->dashed)
|
||||
return;
|
||||
|
||||
dash->dashes = style->dash;
|
||||
dash->num_dashes = style->num_dashes;
|
||||
dash->dash_offset = style->dash_offset;
|
||||
if (_cairo_stroke_style_dash_can_approximate (style, ctm, tolerance)) {
|
||||
_cairo_stroke_style_dash_approximate (style, ctm, tolerance,
|
||||
&dash->dash_offset,
|
||||
dash->approximate_dashes,
|
||||
&dash->num_dashes);
|
||||
dash->dashes = dash->approximate_dashes;
|
||||
} else {
|
||||
dash->dashes = style->dash;
|
||||
dash->num_dashes = style->num_dashes;
|
||||
dash->dash_offset = style->dash_offset;
|
||||
}
|
||||
|
||||
_cairo_stroker_dash_start (dash);
|
||||
}
|
||||
|
|
@ -179,7 +190,7 @@ _cairo_stroker_init (cairo_stroker_t *stroker,
|
|||
stroker->has_first_face = FALSE;
|
||||
stroker->has_initial_sub_path = FALSE;
|
||||
|
||||
_cairo_stroker_dash_init (&stroker->dash, stroke_style);
|
||||
_cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, tolerance);
|
||||
|
||||
stroker->add_external_edge = NULL;
|
||||
|
||||
|
|
@ -1490,7 +1501,8 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
|
|||
stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
|
||||
stroker->num_segments = 0;
|
||||
|
||||
_cairo_stroker_dash_init (&stroker->dash, stroke_style);
|
||||
/* Assume 2*EPSILON tolerance */
|
||||
_cairo_stroker_dash_init (&stroker->dash, stroke_style, ctm, _cairo_fixed_to_double (2 * CAIRO_FIXED_EPSILON));
|
||||
|
||||
stroker->has_bounds = FALSE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,3 +120,122 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
|
|||
*dx = style_expansion * hypot (ctm->xx, ctm->xy);
|
||||
*dy = style_expansion * hypot (ctm->yy, ctm->yx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes the period of a dashed stroke style.
|
||||
* Returns 0 for non-dashed styles.
|
||||
*/
|
||||
double
|
||||
_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style)
|
||||
{
|
||||
double period;
|
||||
unsigned int i;
|
||||
|
||||
period = 0.0;
|
||||
for (i = 0; i < style->num_dashes; i++)
|
||||
period += style->dash[i];
|
||||
|
||||
if (style->num_dashes & 1)
|
||||
period *= 2.0;
|
||||
|
||||
return period;
|
||||
}
|
||||
|
||||
/*
|
||||
* Coefficient of the linear approximation (minimizing square difference)
|
||||
* of the surface covered by round caps
|
||||
*/
|
||||
#define ROUND_MINSQ_APPROXIMATION (9*M_PI/32)
|
||||
|
||||
/*
|
||||
* Computes the length of the "on" part of a dashed stroke style,
|
||||
* taking into account also line caps.
|
||||
* Returns 0 for non-dashed styles.
|
||||
*/
|
||||
double
|
||||
_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style)
|
||||
{
|
||||
double stroked, cap_scale;
|
||||
unsigned int i;
|
||||
|
||||
switch (style->line_cap) {
|
||||
default: ASSERT_NOT_REACHED;
|
||||
case CAIRO_LINE_CAP_BUTT: cap_scale = 0.0; break;
|
||||
case CAIRO_LINE_CAP_ROUND: cap_scale = ROUND_MINSQ_APPROXIMATION; break;
|
||||
case CAIRO_LINE_CAP_SQUARE: cap_scale = 1.0; break;
|
||||
}
|
||||
|
||||
stroked = 0.0;
|
||||
if (style->num_dashes & 1) {
|
||||
/* Each dash element is used both as on and as off. The order in which they are summed is
|
||||
* irrelevant, so sum the coverage of one dash element, taken both on and off at each iteration */
|
||||
for (i = 0; i < style->num_dashes; i++)
|
||||
stroked += style->dash[i] + cap_scale * MIN (style->dash[i], style->line_width);
|
||||
} else {
|
||||
/* Even (0, 2, ...) dashes are on and simply counted for the coverage, odd dashes are off, thus
|
||||
* their coverage is approximated based on the area covered by the caps of adjacent on dases. */
|
||||
for (i = 0; i < style->num_dashes; i+=2)
|
||||
stroked += style->dash[i] + cap_scale * MIN (style->dash[i+1], style->line_width);
|
||||
}
|
||||
|
||||
return stroked;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verifies if _cairo_stroke_style_dash_approximate should be used to generate
|
||||
* an approximation of the dash pattern in the specified style, when used for
|
||||
* stroking a path with the given CTM and tolerance.
|
||||
* Always FALSE for non-dashed styles.
|
||||
*/
|
||||
cairo_bool_t
|
||||
_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
double tolerance)
|
||||
{
|
||||
double period;
|
||||
|
||||
if (! style->num_dashes)
|
||||
return FALSE;
|
||||
|
||||
period = _cairo_stroke_style_dash_period (style);
|
||||
return _cairo_matrix_transformed_circle_major_axis (ctm, period) < tolerance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a 2-dashes approximation of a dashed style, by making the "on" and "off"
|
||||
* parts respect the original ratio.
|
||||
*/
|
||||
void
|
||||
_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
double tolerance,
|
||||
double *dash_offset,
|
||||
double *dashes,
|
||||
unsigned int *num_dashes)
|
||||
{
|
||||
double coverage, scale, offset;
|
||||
cairo_bool_t on = TRUE;
|
||||
unsigned int i = 0;
|
||||
|
||||
coverage = _cairo_stroke_style_dash_stroked (style) / _cairo_stroke_style_dash_period (style);
|
||||
coverage = MIN (coverage, 1.0);
|
||||
scale = tolerance / _cairo_matrix_transformed_circle_major_axis (ctm, 1.0);
|
||||
|
||||
/* We stop searching for a starting point as soon as the
|
||||
offset reaches zero. Otherwise when an initial dash
|
||||
segment shrinks to zero it will be skipped over. */
|
||||
offset = style->dash_offset;
|
||||
while (offset > 0.0 && offset >= style->dash[i]) {
|
||||
offset -= style->dash[i];
|
||||
on = !on;
|
||||
if (++i == style->num_dashes)
|
||||
i = 0;
|
||||
}
|
||||
|
||||
*num_dashes = 2;
|
||||
|
||||
dashes[0] = scale * coverage;
|
||||
dashes[1] = scale * (1.0 - coverage);
|
||||
|
||||
*dash_offset = on ? 0.0 : dashes[0];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1749,6 +1749,26 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
|
|||
const cairo_matrix_t *ctm,
|
||||
double *dx, double *dy);
|
||||
|
||||
cairo_private double
|
||||
_cairo_stroke_style_dash_period (const cairo_stroke_style_t *style);
|
||||
|
||||
cairo_private double
|
||||
_cairo_stroke_style_dash_stroked (const cairo_stroke_style_t *style);
|
||||
|
||||
cairo_private cairo_bool_t
|
||||
_cairo_stroke_style_dash_can_approximate (const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
double tolerance);
|
||||
|
||||
cairo_private void
|
||||
_cairo_stroke_style_dash_approximate (const cairo_stroke_style_t *style,
|
||||
const cairo_matrix_t *ctm,
|
||||
double tolerance,
|
||||
double *dash_offset,
|
||||
double *dashes,
|
||||
unsigned int *num_dashes);
|
||||
|
||||
|
||||
/* cairo-surface.c */
|
||||
|
||||
cairo_private cairo_surface_t *
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue