path: Misclassification of rectilinear after degenerate line-to

Malte Nuhn reported hitting an assertion:

  cairo-path-stroke.c:1816: _cairo_rectilinear_stroker_line_to: Assertion `a->x == b->x || a->y == b->y' failed.
  http://bugs.freedesktop.org/show_bug.cgi?id=24797

when stroking an apparently simple path:

  0 8.626485 m
  0 8.626485 l
  5.208333 2.5 l
  10.416667 2.5 l
  15.625 2.5 l
  20.833333 2.5 l
  26.041667 2.5 l
  31.25 2.5 l
  36.458333 2.5 l
  41.666667 2.5 l
  46.875 2.5 l
  52.083333 2.5 l
  57.291667 2.5 l
  62.5 2.5 l
  67.708333 2.5 l
  72.916667 2.5 l
  78.125 2.5 l
  83.333333 2.5 l
  88.541667 2.5 l
  93.75 2.5 l
  98.958333 2.5 l
  104.166667 2.5 l
  109.375 2.5 l
  114.583333 2.5 l
  119.791667 2.5 l
  125 2.5 l
  stroke

which upon reduction becomes:

  0.000000 8.625000 m 5.207031 2.500000 l 125.000000 2.500000 l stroke

The bug is that after spotting a repeated line-to we move the previous
end-point without reclassifying the path, hence we miss the
non-rectilinear step.
This commit is contained in:
Chris Wilson 2009-10-30 07:49:56 +00:00
parent 600dd83398
commit 23bcf91748

View file

@ -443,61 +443,64 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
* explicitly calling into _cairo_path_fixed_move_to to ensure
* that the last_move_point state is updated properly.
*/
if (! path->has_current_point) {
status = _cairo_path_fixed_move_to (path, point.x, point.y);
} else {
/* If the previous op was also a LINE_TO with the same gradient,
* then just change its end-point rather than adding a new op.
*/
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
if (x == path->current_point.x &&
y == path->current_point.y)
{
return CAIRO_STATUS_SUCCESS;
}
else
{
cairo_path_buf_t *buf;
cairo_point_t *p;
cairo_slope_t prev, self;
if (! path->has_current_point)
return _cairo_path_fixed_move_to (path, point.x, point.y);
buf = cairo_path_tail (path);
if (likely (buf->num_points >= 2)) {
p = &buf->points[buf->num_points-2];
} else {
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
}
/* If the previous op was also a LINE_TO with the same gradient,
* then just change its end-point rather than adding a new op.
*/
if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
cairo_path_buf_t *buf;
const cairo_point_t *p;
_cairo_slope_init (&prev, p, &path->current_point);
_cairo_slope_init (&self, &path->current_point, &point);
if (_cairo_slope_equal (&prev, &self)) {
buf->points[buf->num_points - 1] = point;
path->current_point = point;
return CAIRO_STATUS_SUCCESS;
}
if (x == path->current_point.x && y == path->current_point.y)
return CAIRO_STATUS_SUCCESS;
buf = cairo_path_tail (path);
if (likely (buf->num_points >= 2)) {
p = &buf->points[buf->num_points-2];
} else {
cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
p = &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
}
if (p->x == path->current_point.x && p->y == path->current_point.y) {
/* previous line element was degenerate, replace */
buf->points[buf->num_points - 1] = point;
goto FLAGS;
} else {
cairo_slope_t prev, self;
_cairo_slope_init (&prev, p, &path->current_point);
_cairo_slope_init (&self, &path->current_point, &point);
if (_cairo_slope_equal (&prev, &self)) {
buf->points[buf->num_points - 1] = point;
goto FLAGS;
}
}
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
if (path->is_rectilinear) {
path->is_rectilinear = path->current_point.x == x ||
path->current_point.y == y;
path->maybe_fill_region &= path->is_rectilinear;
}
if (path->maybe_fill_region) {
path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
_cairo_fixed_is_integer (y);
}
if (path->is_empty_fill) {
path->is_empty_fill = path->current_point.x == x &&
path->current_point.y == y;
}
path->current_point = point;
}
return status;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
if (unlikely (status))
return status;
FLAGS:
if (path->is_rectilinear) {
path->is_rectilinear = path->current_point.x == x ||
path->current_point.y == y;
path->maybe_fill_region &= path->is_rectilinear;
}
if (path->maybe_fill_region) {
path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
_cairo_fixed_is_integer (y);
}
if (path->is_empty_fill) {
path->is_empty_fill = path->current_point.x == x &&
path->current_point.y == y;
}
path->current_point = point;
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t