[fill] Emit rectangles for GdkRegion

Scan the path for a series of consistently wound rectangles.
This commit is contained in:
Chris Wilson 2008-11-25 11:52:01 +00:00
parent 23df74e5ff
commit 4ac38f7c2b
3 changed files with 200 additions and 24 deletions

View file

@ -150,6 +150,7 @@ _cairo_filler_close_path (void *closure)
static cairo_int_status_t
_cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps);
cairo_status_t
@ -163,7 +164,7 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path,
/* Before we do anything else, we use a special-case filler for
* a device-axis aligned rectangle if possible. */
status = _cairo_path_fixed_fill_rectangle (path, traps);
status = _cairo_path_fixed_fill_rectangle (path, fill_rule, traps);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
return status;
@ -205,27 +206,77 @@ BAIL:
*/
static cairo_int_status_t
_cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps)
{
if (_cairo_path_fixed_is_box (path, NULL)) {
cairo_point_t *p = path->buf_head.base.points;
cairo_point_t *top_left, *bot_right;
cairo_box_t box;
top_left = &p[0];
bot_right = &p[2];
if (top_left->x > bot_right->x || top_left->y > bot_right->y) {
int n;
if (_cairo_path_fixed_is_box (path, &box)) {
if (box.p1.x > box.p2.x) {
cairo_fixed_t t;
/* not a simple cairo_rectangle() */
for (n = 0; n < 4; n++) {
if (p[n].x <= top_left->x && p[n].y <= top_left->y)
top_left = &p[n];
if (p[n].x >= bot_right->x && p[n].y >= bot_right->y)
bot_right = &p[n];
}
t = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = t;
}
return _cairo_traps_tessellate_rectangle (traps, top_left, bot_right);
if (box.p1.y > box.p2.y) {
cairo_fixed_t t;
t = box.p1.y;
box.p1.y = box.p2.y;
box.p2.y = t;
}
return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
} else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
cairo_path_fixed_iter_t iter;
int last_cw = -1;
/* Support a series of rectangles as can be expected to describe a
* GdkRegion clip region during exposes.
*/
_cairo_path_fixed_iter_init (&iter, path);
while (_cairo_path_fixed_iter_is_box (&iter, &box)) {
cairo_status_t status;
int cw = 0;
if (box.p1.x > box.p2.x) {
cairo_fixed_t t;
t = box.p1.x;
box.p1.x = box.p2.x;
box.p2.x = t;
cw = ! cw;
}
if (box.p1.y > box.p2.y) {
cairo_fixed_t t;
t = box.p1.y;
box.p1.y = box.p2.y;
box.p2.y = t;
cw = ! cw;
}
if (last_cw < 0) {
last_cw = cw;
} else if (last_cw != cw) {
_cairo_traps_clear (traps);
return CAIRO_INT_STATUS_UNSUPPORTED;
}
status = _cairo_traps_tessellate_rectangle (traps,
&box.p1, &box.p2);
if (unlikely (status))
return status;
}
if (_cairo_path_fixed_iter_at_end (&iter))
return CAIRO_STATUS_SUCCESS;
_cairo_traps_clear (traps);
}
return CAIRO_INT_STATUS_UNSUPPORTED;

View file

@ -88,4 +88,21 @@ cairo_private cairo_bool_t
_cairo_path_fixed_equal (const cairo_path_fixed_t *a,
const cairo_path_fixed_t *b);
typedef struct _cairo_path_fixed_iter {
cairo_path_buf_t *buf;
int n_op;
int n_point;
} cairo_path_fixed_iter_t;
cairo_private void
_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
cairo_path_fixed_t *path);
cairo_private cairo_bool_t
_cairo_path_fixed_iter_is_box (cairo_path_fixed_iter_t *_iter,
cairo_box_t *box);
cairo_private cairo_bool_t
_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter);
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */

View file

@ -1002,10 +1002,8 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path,
buf->points[2].y == buf->points[3].y &&
buf->points[3].x == buf->points[0].x)
{
if (box) {
box->p1 = buf->points[0];
box->p2 = buf->points[2];
}
box->p1 = buf->points[0];
box->p2 = buf->points[2];
return TRUE;
}
@ -1014,10 +1012,8 @@ _cairo_path_fixed_is_box (cairo_path_fixed_t *path,
buf->points[2].x == buf->points[3].x &&
buf->points[3].y == buf->points[0].y)
{
if (box) {
box->p1 = buf->points[0];
box->p2 = buf->points[2];
}
box->p1 = buf->points[0];
box->p2 = buf->points[2];
return TRUE;
}
@ -1049,3 +1045,115 @@ _cairo_path_fixed_is_rectangle (cairo_path_fixed_t *path,
return FALSE;
}
void
_cairo_path_fixed_iter_init (cairo_path_fixed_iter_t *iter,
cairo_path_fixed_t *path)
{
iter->buf = &path->buf_head.base;
iter->n_op = 0;
iter->n_point = 0;
}
static cairo_bool_t
_cairo_path_fixed_iter_next_op (cairo_path_fixed_iter_t *iter)
{
if (++iter->n_op == iter->buf->num_ops) {
iter->buf = iter->buf->next;
iter->n_op = 0;
iter->n_point = 0;
}
return iter->buf != NULL;
}
cairo_bool_t
_cairo_path_fixed_iter_is_box (cairo_path_fixed_iter_t *_iter,
cairo_box_t *box)
{
cairo_point_t points[5];
cairo_path_fixed_iter_t iter;
if (_iter->buf == NULL)
return FALSE;
iter = *_iter;
/* Check whether the ops are those that would be used for a rectangle */
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_MOVE_TO)
return FALSE;
points[0] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
return FALSE;
points[1] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
return FALSE;
points[2] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_LINE_TO)
return FALSE;
points[3] = iter.buf->points[iter.n_point++];
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
/* Now, there are choices. The rectangle might end with a LINE_TO
* (to the original point), but this isn't required. If it
* doesn't, then it must end with a CLOSE_PATH. */
if (iter.buf->op[iter.n_op] == CAIRO_PATH_OP_LINE_TO) {
points[4] = iter.buf->points[iter.n_point++];
if (points[4].x != points[0].x || points[4].y != points[0].y)
return FALSE;
} else if (iter.buf->op[iter.n_op] != CAIRO_PATH_OP_CLOSE_PATH) {
return FALSE;
}
if (! _cairo_path_fixed_iter_next_op (&iter))
return FALSE;
/* Ok, we may have a box, if the points line up */
if (points[0].y == points[1].y &&
points[1].x == points[2].x &&
points[2].y == points[3].y &&
points[3].x == points[0].x)
{
box->p1 = points[0];
box->p2 = points[2];
*_iter = iter;
return TRUE;
}
if (points[0].x == points[1].x &&
points[1].y == points[2].y &&
points[2].x == points[3].x &&
points[3].y == points[0].y)
{
box->p1 = points[0];
box->p2 = points[2];
*_iter = iter;
return TRUE;
}
return FALSE;
}
cairo_bool_t
_cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
{
if (iter->buf == NULL)
return TRUE;
if (iter->buf->op[iter->n_op] == CAIRO_PATH_OP_MOVE_TO &&
iter->buf->num_ops == iter->n_op + 1)
{
return TRUE;
}
return FALSE;
}