[path] Evaluate is_box && is_region during construction

Whilst constructing the path, if the operations continue to be
axis-aligned lines, allow the is_box and is_region flags to persist. These
are set to false as soon as a curve-to is added, a diagonal or in the case
of is_region a non-integer point.
This commit is contained in:
Chris Wilson 2009-07-05 09:09:42 +01:00
parent 4bb157672e
commit 1645352bfb
4 changed files with 116 additions and 185 deletions

View file

@ -73,6 +73,8 @@ struct _cairo_path_fixed {
cairo_point_t current_point;
unsigned int has_current_point : 1;
unsigned int has_curve_to : 1;
unsigned int is_box : 1;
unsigned int is_region : 1;
cairo_path_buf_t *buf_tail;
cairo_path_buf_fixed_t buf_head;
@ -105,4 +107,10 @@ _cairo_path_fixed_iter_is_fill_box (cairo_path_fixed_iter_t *_iter,
cairo_private cairo_bool_t
_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter);
static inline cairo_bool_t
_cairo_path_fixed_is_region (cairo_path_fixed_t *path)
{
return path->is_region;
}
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */

View file

@ -42,10 +42,10 @@
/* private functions */
static cairo_status_t
_cairo_path_fixed_add (cairo_path_fixed_t *path,
cairo_path_op_t op,
cairo_point_t *points,
int num_points);
_cairo_path_fixed_add (cairo_path_fixed_t *path,
cairo_path_op_t op,
const cairo_point_t *points,
int num_points);
static void
_cairo_path_fixed_add_buf (cairo_path_fixed_t *path,
@ -62,9 +62,9 @@ _cairo_path_buf_add_op (cairo_path_buf_t *buf,
cairo_path_op_t op);
static void
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
cairo_point_t *points,
int num_points);
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
const cairo_point_t *points,
int num_points);
void
_cairo_path_fixed_init (cairo_path_fixed_t *path)
@ -85,6 +85,8 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path)
path->current_point.y = 0;
path->has_current_point = FALSE;
path->has_curve_to = FALSE;
path->is_region = TRUE;
path->is_box = TRUE;
path->last_move_point = path->current_point;
}
@ -99,8 +101,10 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
path->current_point = other->current_point;
path->has_current_point = other->has_current_point;
path->has_curve_to = other->has_curve_to;
path->last_move_point = other->last_move_point;
path->has_curve_to = other->has_curve_to;
path->is_box = other->is_box;
path->is_region = other->is_region;
path->buf_head.base.num_ops = other->buf_head.base.num_ops;
path->buf_head.base.num_points = other->buf_head.base.num_points;
@ -370,6 +374,17 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path,
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
if (unlikely (status))
return status;
if (path->has_current_point && path->is_box) {
/* a move-to is first an implicit close */
path->is_box = path->current_point.x == path->last_move_point.x ||
path->current_point.y == path->last_move_point.y;
path->is_region &= path->is_box;
}
if (path->is_region) {
path->is_region = _cairo_fixed_is_integer (x) &&
_cairo_fixed_is_integer (y);
}
}
path->current_point = point;
@ -390,15 +405,13 @@ _cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
cairo_fixed_t dx,
cairo_fixed_t dy)
{
cairo_fixed_t x, y;
if (! path->has_current_point)
if (unlikely (! path->has_current_point))
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
x = path->current_point.x + dx;
y = path->current_point.y + dy;
return _cairo_path_fixed_move_to (path,
path->current_point.x + dx,
path->current_point.y + dy);
return _cairo_path_fixed_move_to (path, x, y);
}
cairo_status_t
@ -417,18 +430,25 @@ _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)
if (! path->has_current_point) {
status = _cairo_path_fixed_move_to (path, point.x, point.y);
else
} else {
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
if (unlikely (status))
return status;
if (path->is_box) {
path->is_box = path->current_point.x == x ||
path->current_point.y == y;
path->is_region &= path->is_box;
}
if (path->is_region) {
path->is_region = _cairo_fixed_is_integer (x) &&
_cairo_fixed_is_integer (y);
}
}
path->current_point = point;
path->has_current_point = TRUE;
return CAIRO_STATUS_SUCCESS;
return status;
}
cairo_status_t
@ -436,15 +456,12 @@ _cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
cairo_fixed_t dx,
cairo_fixed_t dy)
{
cairo_fixed_t x, y;
if (! path->has_current_point)
if (unlikely (! path->has_current_point))
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
x = path->current_point.x + dx;
y = path->current_point.y + dy;
return _cairo_path_fixed_line_to (path, x, y);
return _cairo_path_fixed_line_to (path,
path->current_point.x + dx,
path->current_point.y + dy);
}
cairo_status_t
@ -456,17 +473,19 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
cairo_status_t status;
cairo_point_t point[3];
point[0].x = x0; point[0].y = y0;
point[1].x = x1; point[1].y = y1;
point[2].x = x2; point[2].y = y2;
/* make sure subpaths are started properly */
if (! path->has_current_point) {
status = _cairo_path_fixed_move_to (path, point[0].x, point[0].y);
status = _cairo_path_fixed_move_to (path, x0, y0);
if (unlikely (status))
return status;
}
if (x2 == path->current_point.x && y2 == path->current_point.y)
return _cairo_path_fixed_line_to (path, x2, y2);
point[0].x = x0; point[0].y = y0;
point[1].x = x1; point[1].y = y1;
point[2].x = x2; point[2].y = y2;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
if (unlikely (status))
return status;
@ -474,6 +493,8 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
path->current_point = point[2];
path->has_current_point = TRUE;
path->has_curve_to = TRUE;
path->is_box = FALSE;
path->is_region = FALSE;
return CAIRO_STATUS_SUCCESS;
}
@ -484,26 +505,24 @@ _cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
cairo_fixed_t dx1, cairo_fixed_t dy1,
cairo_fixed_t dx2, cairo_fixed_t dy2)
{
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
if (! path->has_current_point)
if (unlikely (! path->has_current_point))
return _cairo_error (CAIRO_STATUS_NO_CURRENT_POINT);
x0 = path->current_point.x + dx0;
y0 = path->current_point.y + dy0;
x1 = path->current_point.x + dx1;
y1 = path->current_point.y + dy1;
x2 = path->current_point.x + dx2;
y2 = path->current_point.y + dy2;
if (dx2 == 0 && dy2 == 0) {
return _cairo_path_fixed_line_to (path,
path->current_point.x,
path->current_point.y);
}
return _cairo_path_fixed_curve_to (path,
x0, y0,
x1, y1,
x2, y2);
path->current_point.x + dx0,
path->current_point.y + dy0,
path->current_point.x + dx1,
path->current_point.y + dy1,
path->current_point.x + dx2,
path->current_point.y + dy2);
}
cairo_status_t
@ -518,13 +537,9 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
if (unlikely (status))
return status;
status = _cairo_path_fixed_move_to (path,
path->last_move_point.x,
path->last_move_point.y);
if (unlikely (status))
return status;
return CAIRO_STATUS_SUCCESS;
return _cairo_path_fixed_move_to (path,
path->last_move_point.x,
path->last_move_point.y);
}
cairo_bool_t
@ -542,10 +557,10 @@ _cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
}
static cairo_status_t
_cairo_path_fixed_add (cairo_path_fixed_t *path,
cairo_path_op_t op,
cairo_point_t *points,
int num_points)
_cairo_path_fixed_add (cairo_path_fixed_t *path,
cairo_path_op_t op,
const cairo_point_t *points,
int num_points)
{
cairo_path_buf_t *buf = path->buf_tail;
@ -616,25 +631,16 @@ _cairo_path_buf_add_op (cairo_path_buf_t *buf,
}
static void
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
cairo_point_t *points,
int num_points)
_cairo_path_buf_add_points (cairo_path_buf_t *buf,
const cairo_point_t *points,
int num_points)
{
int i;
for (i=0; i < num_points; i++) {
buf->points[buf->num_points++] = points[i];
}
memcpy (buf->points + buf->num_points,
points,
sizeof (points[0]) * num_points);
buf->num_points += num_points;
}
static int const num_args[] =
{
1, /* cairo_path_move_to */
1, /* cairo_path_op_line_to */
3, /* cairo_path_op_curve_to */
0, /* cairo_path_op_close_path */
};
cairo_status_t
_cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
cairo_direction_t dir,
@ -644,9 +650,14 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
cairo_path_fixed_close_path_func_t *close_path,
void *closure)
{
const uint8_t num_args[] = {
1, /* cairo_path_move_to */
1, /* cairo_path_op_line_to */
3, /* cairo_path_op_curve_to */
0, /* cairo_path_op_close_path */
};
cairo_status_t status;
const cairo_path_buf_t *buf;
cairo_path_op_t op;
cairo_bool_t forward = (dir == CAIRO_DIRECTION_FORWARD);
int step = forward ? 1 : -1;
@ -666,12 +677,11 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
points = buf->points + buf->num_points;
}
for (i=start; i != stop; i += step) {
op = buf->op[i];
for (i = start; i != stop; i += step) {
cairo_path_op_t op = buf->op[i];
if (! forward) {
if (! forward)
points -= num_args[(int) op];
}
switch (op) {
case CAIRO_PATH_OP_MOVE_TO:
@ -683,18 +693,17 @@ _cairo_path_fixed_interpret (const cairo_path_fixed_t *path,
case CAIRO_PATH_OP_CURVE_TO:
status = (*curve_to) (closure, &points[0], &points[1], &points[2]);
break;
case CAIRO_PATH_OP_CLOSE_PATH:
default:
ASSERT_NOT_REACHED;
case CAIRO_PATH_OP_CLOSE_PATH:
status = (*close_path) (closure);
break;
}
if (unlikely (status))
return status;
if (forward) {
if (forward)
points += num_args[(int) op];
}
}
}
@ -705,16 +714,14 @@ static cairo_status_t
_append_move_to (void *closure,
const cairo_point_t *point)
{
cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure;
return _cairo_path_fixed_move_to (path, point->x, point->y);
return _cairo_path_fixed_move_to (closure, point->x, point->y);
}
static cairo_status_t
_append_line_to (void *closure,
const cairo_point_t *point)
{
cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure;
return _cairo_path_fixed_line_to (path, point->x, point->y);
return _cairo_path_fixed_line_to (closure, point->x, point->y);
}
static cairo_status_t
@ -723,15 +730,16 @@ _append_curve_to (void *closure,
const cairo_point_t *p1,
const cairo_point_t *p2)
{
cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure;
return _cairo_path_fixed_curve_to (path, p0->x, p0->y, p1->x, p1->y, p2->x, p2->y);
return _cairo_path_fixed_curve_to (closure,
p0->x, p0->y,
p1->x, p1->y,
p2->x, p2->y);
}
static cairo_status_t
_append_close_path (void *closure)
{
cairo_path_fixed_t *path = (cairo_path_fixed_t *) closure;
return _cairo_path_fixed_close_path (path);
return _cairo_path_fixed_close_path (closure);
}
cairo_status_t
@ -825,6 +833,8 @@ _cairo_path_fixed_is_equal (cairo_path_fixed_t *path,
path->current_point.y != other->current_point.y ||
path->has_current_point != other->has_current_point ||
path->has_curve_to != other->has_curve_to ||
path->is_box != other->is_box ||
path->is_region != other->is_region ||
path->last_move_point.x != other->last_move_point.x ||
path->last_move_point.y != other->last_move_point.y)
return FALSE;
@ -913,7 +923,6 @@ _cpf_close_path (void *closure)
return cpf->close_path (cpf->closure);
}
cairo_status_t
_cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
cairo_direction_t dir,
@ -925,7 +934,7 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
{
cpf_t flattener;
if (!path->has_curve_to) {
if (! path->has_curve_to) {
return _cairo_path_fixed_interpret (path, dir,
move_to,
line_to,
@ -965,6 +974,9 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path,
{
cairo_path_buf_t *buf = &path->buf_head.base;
if (! path->is_box)
return FALSE;
/* We can't have more than one buf for this check */
if (buf->next != NULL)
return FALSE;
@ -1174,91 +1186,3 @@ _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
return FALSE;
}
/* Closure for path region testing. Every move_to must be to integer
* coordinates, there must be no curves, and every line_to or
* close_path must represent an axis aligned line to an integer point.
* We're relying on the path interpreter always sending a single
* move_to at the start of any subpath, not receiving having any
* superfluous move_tos, and the path intepreter bailing with our
* first non-successful error. */
typedef struct cairo_path_region_tester {
cairo_point_t last_move_point;
cairo_point_t current_point;
} cprt_t;
static cairo_status_t
_cprt_line_to (void *closure,
const cairo_point_t *p2)
{
cprt_t *self = closure;
cairo_point_t *p1 = &self->current_point;
if (p2->x == p1->x) {
if (_cairo_fixed_is_integer (p2->y)) {
p1->y = p2->y;
return CAIRO_STATUS_SUCCESS;
}
} else if (p2->y == p1->y) {
if (_cairo_fixed_is_integer (p2->x)) {
p1->x = p2->x;
return CAIRO_STATUS_SUCCESS;
}
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
static cairo_status_t
_cprt_close_path (void *closure)
{
cprt_t *self = closure;
return _cprt_line_to (closure, &self->last_move_point);
}
static cairo_status_t
_cprt_move_to (void *closure,
const cairo_point_t *p)
{
cprt_t *self = closure;
cairo_status_t status;
status = _cprt_close_path (closure);
if (status)
return status;
if (_cairo_fixed_is_integer (p->x) && _cairo_fixed_is_integer (p->y)) {
self->current_point = *p;
self->last_move_point = *p;
return CAIRO_STATUS_SUCCESS;
}
return CAIRO_INT_STATUS_UNSUPPORTED;
}
/*
* Check whether the given path is representable as a region.
* That is, if the path contains only axis aligned lines between
* integer coordinates in device space.
*/
cairo_bool_t
_cairo_path_fixed_is_region (cairo_path_fixed_t *path)
{
cprt_t cprt;
if (path->has_curve_to)
return FALSE;
cprt.current_point.x = 0;
cprt.current_point.y = 0;
cprt.last_move_point.x = 0;
cprt.last_move_point.y = 0;
return _cairo_path_fixed_interpret (path,
CAIRO_DIRECTION_FORWARD,
_cprt_move_to,
_cprt_line_to,
NULL,
_cprt_close_path,
&cprt) == CAIRO_STATUS_SUCCESS;
}

View file

@ -56,6 +56,8 @@ static const cairo_t _cairo_nil = {
{ 0, 0 }, /* current point */
FALSE, /* has_current_point */
FALSE, /* has_curve_to */
FALSE, /* is_box */
FALSE, /* is_region */
NULL, {{NULL}} /* buf_tail, buf_head */
}}
};

View file

@ -1617,9 +1617,6 @@ cairo_private cairo_bool_t
_cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path,
cairo_box_t *box);
cairo_private cairo_bool_t
_cairo_path_fixed_is_region (cairo_path_fixed_t *path);
/* cairo-path-in-fill.c */
cairo_private void
_cairo_path_fixed_in_fill (cairo_path_fixed_t *path,