diff --git a/src/cairo-debug.c b/src/cairo-debug.c index 9160728e0..e36406975 100644 --- a/src/cairo-debug.c +++ b/src/cairo-debug.c @@ -221,6 +221,12 @@ _cairo_debug_print_path (FILE *stream, cairo_path_fixed_t *path) { cairo_status_t status; + printf ("path: extents=(%f, %f), (%f, %f)\n", + _cairo_fixed_to_double (path->extents.p1.x), + _cairo_fixed_to_double (path->extents.p1.y), + _cairo_fixed_to_double (path->extents.p2.x), + _cairo_fixed_to_double (path->extents.p2.y)); + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _print_move_to, diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index a83444787..8bb7e71d1 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -806,14 +806,25 @@ _cairo_gstate_path_extents (cairo_gstate_t *gstate, double *x1, double *y1, double *x2, double *y2) { + cairo_box_t box; double px1, py1, px2, py2; - _cairo_path_fixed_bounds (path, - &px1, &py1, &px2, &py2); + if (_cairo_path_fixed_extents (path, &box)) { + px1 = _cairo_fixed_to_double (box.p1.x); + py1 = _cairo_fixed_to_double (box.p1.y); + px2 = _cairo_fixed_to_double (box.p2.x); + py2 = _cairo_fixed_to_double (box.p2.y); + + _cairo_gstate_backend_to_user_rectangle (gstate, + &px1, &py1, &px2, &py2, + NULL); + } else { + px1 = 0.0; + py1 = 0.0; + px2 = 0.0; + py2 = 0.0; + } - _cairo_gstate_backend_to_user_rectangle (gstate, - &px1, &py1, &px2, &py2, - NULL); if (x1) *x1 = px1; if (y1) diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c index 7b7ae6800..8f9bdc3f2 100644 --- a/src/cairo-path-bounds.c +++ b/src/cairo-path-bounds.c @@ -35,6 +35,7 @@ */ #include "cairoint.h" +#include "cairo-path-fixed-private.h" typedef struct cairo_path_bounder { cairo_point_t current_point; @@ -134,26 +135,6 @@ _cairo_path_bounder_curve_to (void *closure, } } -static cairo_status_t -_cairo_path_bounder_curve_to_cp (void *closure, - const cairo_point_t *b, - const cairo_point_t *c, - const cairo_point_t *d) -{ - cairo_path_bounder_t *bounder = closure; - - if (bounder->has_initial_point) { - _cairo_path_bounder_add_point (bounder, &bounder->current_point); - bounder->has_initial_point = FALSE; - } - - _cairo_path_bounder_add_point (bounder, b); - _cairo_path_bounder_add_point (bounder, c); - _cairo_path_bounder_add_point (bounder, d); - - return CAIRO_STATUS_SUCCESS; -} - static cairo_status_t _cairo_path_bounder_close_path (void *closure) { @@ -168,21 +149,8 @@ void _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents) { - cairo_path_bounder_t bounder; - cairo_status_t status; - - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to_cp, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); - - if (bounder.has_point) { - _cairo_box_round_to_rectangle (&bounder.extents, extents); + if (path->extents.p1.x < path->extents.p2.x) { + _cairo_box_round_to_rectangle (&path->extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; @@ -199,15 +167,20 @@ _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, cairo_path_bounder_t bounder; cairo_status_t status; - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); + if (! path->has_curve_to) { + bounder.extents = path->extents; + bounder.has_point = path->extents.p1.x < path->extents.p2.x; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + } if (bounder.has_point) { _cairo_box_round_to_rectangle (&bounder.extents, extents); @@ -226,14 +199,19 @@ _cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, cairo_path_bounder_t bounder; cairo_status_t status; - _cairo_path_bounder_init (&bounder); - - status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_close_path, - &bounder, tolerance); - assert (status == CAIRO_STATUS_SUCCESS); + if (! path->has_curve_to) { + bounder.extents = path->extents; + bounder.has_point = path->extents.p1.x < path->extents.p2.x; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret_flat (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_close_path, + &bounder, tolerance); + assert (status == CAIRO_STATUS_SUCCESS); + } if (bounder.has_point) { _cairo_box_round_to_rectangle (&bounder.extents, extents); @@ -247,21 +225,43 @@ _cairo_path_fixed_fill_extents (const cairo_path_fixed_t *path, void _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, const cairo_stroke_style_t *style, - const const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm, cairo_rectangle_int_t *extents) { cairo_path_bounder_t bounder; cairo_status_t status; - _cairo_path_bounder_init (&bounder); + if (! path->has_curve_to) { + bounder.extents = path->extents; - status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, - _cairo_path_bounder_move_to, - _cairo_path_bounder_line_to, - _cairo_path_bounder_curve_to, - _cairo_path_bounder_close_path, - &bounder); - assert (status == CAIRO_STATUS_SUCCESS); + /* include trailing move-to for degenerate segments */ + if (path->has_last_move_point) { + const cairo_point_t *point = &path->last_move_point; + + if (point->x < bounder.extents.p1.x) + bounder.extents.p1.x = point->x; + if (point->y < bounder.extents.p1.y) + bounder.extents.p1.y = point->y; + + if (point->x > bounder.extents.p2.x) + bounder.extents.p2.x = point->x; + if (point->y > bounder.extents.p2.y) + bounder.extents.p2.y = point->y; + } + + bounder.has_point = bounder.extents.p1.x <= bounder.extents.p2.x; + bounder.has_initial_point = FALSE; + } else { + _cairo_path_bounder_init (&bounder); + + status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, + _cairo_path_bounder_move_to, + _cairo_path_bounder_line_to, + _cairo_path_bounder_curve_to, + _cairo_path_bounder_close_path, + &bounder); + assert (status == CAIRO_STATUS_SUCCESS); + } if (bounder.has_point) { double dx, dy; @@ -294,12 +294,12 @@ _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path, } cairo_status_t -_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, - const cairo_stroke_style_t *stroke_style, - const cairo_matrix_t *ctm, - const cairo_matrix_t *ctm_inverse, - double tolerance, - cairo_rectangle_int_t *extents) +_cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, + const cairo_stroke_style_t *stroke_style, + const cairo_matrix_t *ctm, + const cairo_matrix_t *ctm_inverse, + double tolerance, + cairo_rectangle_int_t *extents) { cairo_traps_t traps; cairo_box_t bbox; @@ -322,14 +322,18 @@ _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, return status; } -void -_cairo_path_fixed_bounds (const cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2) +cairo_bool_t +_cairo_path_fixed_extents (const cairo_path_fixed_t *path, + cairo_box_t *box) { cairo_path_bounder_t bounder; cairo_status_t status; + if (! path->has_curve_to) { + *box = path->extents; + return path->extents.p1.x < path->extents.p2.x; + } + _cairo_path_bounder_init (&bounder); status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, @@ -340,15 +344,6 @@ _cairo_path_fixed_bounds (const cairo_path_fixed_t *path, &bounder); assert (status == CAIRO_STATUS_SUCCESS); - if (bounder.has_point) { - *x1 = _cairo_fixed_to_double (bounder.extents.p1.x); - *y1 = _cairo_fixed_to_double (bounder.extents.p1.y); - *x2 = _cairo_fixed_to_double (bounder.extents.p2.x); - *y2 = _cairo_fixed_to_double (bounder.extents.p2.y); - } else { - *x1 = 0.0; - *y1 = 0.0; - *x2 = 0.0; - *y2 = 0.0; - } + *box = bounder.extents; + return bounder.has_point; } diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h index 08b7a0623..601d21460 100644 --- a/src/cairo-path-fixed-private.h +++ b/src/cairo-path-fixed-private.h @@ -80,15 +80,17 @@ struct _cairo_path_fixed { cairo_point_t last_move_point; cairo_point_t current_point; unsigned int has_current_point : 1; + unsigned int has_last_move_point : 1; unsigned int has_curve_to : 1; unsigned int is_rectilinear : 1; unsigned int maybe_fill_region : 1; unsigned int is_empty_fill : 1; + cairo_box_t extents; + cairo_path_buf_fixed_t buf; }; - cairo_private void _cairo_path_fixed_translate (cairo_path_fixed_t *path, cairo_fixed_t offx, diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c index 958c85c09..be071d168 100644 --- a/src/cairo-path-fixed.c +++ b/src/cairo-path-fixed.c @@ -96,11 +96,15 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path) path->current_point.x = 0; path->current_point.y = 0; path->last_move_point = path->current_point; + path->has_last_move_point = FALSE; path->has_current_point = FALSE; path->has_curve_to = FALSE; path->is_rectilinear = TRUE; path->maybe_fill_region = TRUE; path->is_empty_fill = TRUE; + + path->extents.p1.x = path->extents.p1.y = INT_MAX; + path->extents.p2.x = path->extents.p2.y = INT_MIN; } cairo_status_t @@ -119,12 +123,15 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path, path->current_point = other->current_point; path->last_move_point = other->last_move_point; + path->has_last_move_point = other->has_last_move_point; path->has_current_point = other->has_current_point; path->has_curve_to = other->has_curve_to; path->is_rectilinear = other->is_rectilinear; path->maybe_fill_region = other->maybe_fill_region; path->is_empty_fill = other->is_empty_fill; + path->extents = other->extents; + path->buf.base.num_ops = other->buf.base.num_ops; path->buf.base.num_points = other->buf.base.num_points; path->buf.base.buf_size = other->buf.base.buf_size; @@ -176,6 +183,8 @@ _cairo_path_fixed_hash (const cairo_path_fixed_t *path) const cairo_path_buf_t *buf; int num_points, num_ops; + hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents)); + num_ops = num_points = 0; cairo_path_foreach_buf_start (buf, path) { hash = _cairo_hash_bytes (hash, buf->op, @@ -231,6 +240,14 @@ _cairo_path_fixed_equal (const cairo_path_fixed_t *a, return FALSE; } + if (a->extents.p1.x != b->extents.p1.x || + a->extents.p1.y != b->extents.p1.y || + a->extents.p2.x != b->extents.p2.x || + a->extents.p2.y != b->extents.p2.y) + { + return FALSE; + } + num_ops_a = num_points_a = 0; if (a != NULL) { cairo_path_foreach_buf_start (buf_a, a) { @@ -365,6 +382,21 @@ _cairo_path_last_op (cairo_path_fixed_t *path) return buf->op[buf->num_ops - 1]; } +static inline void +_cairo_path_fixed_extents_add (cairo_path_fixed_t *path, + const cairo_point_t *point) +{ + if (point->x < path->extents.p1.x) + path->extents.p1.x = point->x; + if (point->y < path->extents.p1.y) + path->extents.p1.y = point->y; + + if (point->x > path->extents.p2.x) + path->extents.p2.x = point->x; + if (point->y > path->extents.p2.y) + path->extents.p2.y = point->y; +} + cairo_status_t _cairo_path_fixed_move_to (cairo_path_fixed_t *path, cairo_fixed_t x, @@ -403,6 +435,7 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path, path->current_point = point; path->last_move_point = point; + path->has_last_move_point = TRUE; path->has_current_point = TRUE; return CAIRO_STATUS_SUCCESS; @@ -511,6 +544,11 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path, } path->current_point = point; + if (path->has_last_move_point) { + _cairo_path_fixed_extents_add (path, &path->last_move_point); + path->has_last_move_point = FALSE; + } + _cairo_path_fixed_extents_add (path, &point); return CAIRO_STATUS_SUCCESS; } @@ -557,6 +595,15 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path, path->is_rectilinear = FALSE; path->maybe_fill_region = FALSE; + /* coarse bounds */ + if (path->has_last_move_point) { + _cairo_path_fixed_extents_add (path, &path->last_move_point); + path->has_last_move_point = FALSE; + } + _cairo_path_fixed_extents_add (path, &point[0]); + _cairo_path_fixed_extents_add (path, &point[1]); + _cairo_path_fixed_extents_add (path, &point[2]); + return CAIRO_STATUS_SUCCESS; } @@ -907,6 +954,12 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path, buf->points[i].y += offy; } } cairo_path_foreach_buf_end (buf, path); + + path->extents.p1.x = _cairo_fixed_mul (scalex, path->extents.p1.x) + offx; + path->extents.p2.x = _cairo_fixed_mul (scalex, path->extents.p2.x) + offx; + + path->extents.p1.y = _cairo_fixed_mul (scaley, path->extents.p1.y) + offy; + path->extents.p2.y = _cairo_fixed_mul (scaley, path->extents.p2.y) + offy; } void @@ -927,9 +980,9 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path, } path->last_move_point.x += offx; - path->last_move_point.y += offx; + path->last_move_point.y += offy; path->current_point.x += offx; - path->current_point.y += offx; + path->current_point.y += offy; cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { @@ -937,6 +990,11 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path, buf->points[i].y += offy; } } cairo_path_foreach_buf_end (buf, path); + + path->extents.p1.x += offx; + path->extents.p1.y += offy; + path->extents.p2.x += offx; + path->extents.p2.y += offy; } /** @@ -960,14 +1018,22 @@ _cairo_path_fixed_transform (cairo_path_fixed_t *path, if (matrix->yx == 0.0 && matrix->xy == 0.0) { /* Fast path for the common case of scale+transform */ - _cairo_path_fixed_offset_and_scale (path, - _cairo_fixed_from_double (matrix->x0), - _cairo_fixed_from_double (matrix->y0), - _cairo_fixed_from_double (matrix->xx), - _cairo_fixed_from_double (matrix->yy)); + if (matrix->xx == 1. && matrix->yy == 1.) { + _cairo_path_fixed_translate (path, + _cairo_fixed_from_double (matrix->x0), + _cairo_fixed_from_double (matrix->y0)); + } else { + _cairo_path_fixed_offset_and_scale (path, + _cairo_fixed_from_double (matrix->x0), + _cairo_fixed_from_double (matrix->y0), + _cairo_fixed_from_double (matrix->xx), + _cairo_fixed_from_double (matrix->yy)); + } return; } + path->extents.p1.x = path->extents.p1.y = INT_MAX; + path->extents.p2.x = path->extents.p2.y = INT_MIN; path->maybe_fill_region = FALSE; cairo_path_foreach_buf_start (buf, path) { for (i = 0; i < buf->num_points; i++) { @@ -978,6 +1044,9 @@ _cairo_path_fixed_transform (cairo_path_fixed_t *path, buf->points[i].x = _cairo_fixed_from_double (dx); buf->points[i].y = _cairo_fixed_from_double (dy); + + /* XXX need to eliminate surplus move-to's? */ + _cairo_path_fixed_extents_add (path, &buf->points[i]); } } cairo_path_foreach_buf_end (buf, path); } diff --git a/src/cairo.c b/src/cairo.c index 4be3e4497..3cb9b3652 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -60,10 +60,12 @@ static const cairo_t _cairo_nil = { { 0, 0 }, /* last_move_point */ { 0, 0 }, /* current point */ FALSE, /* has_current_point */ + FALSE, /* has_last_move_point */ FALSE, /* has_curve_to */ FALSE, /* is_box */ FALSE, /* maybe_fill_region */ TRUE, /* is_empty_fill */ + { {0, 0}, {0, 0}}, /* extents */ {{{NULL,NULL}}} /* link */ }} }; diff --git a/src/cairoint.h b/src/cairoint.h index 364edf5ba..f4be2f52a 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1498,6 +1498,10 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, void *closure, double tolerance); +cairo_private cairo_bool_t +_cairo_path_fixed_extents (const cairo_path_fixed_t *path, + cairo_box_t *box); + cairo_private void _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path, cairo_rectangle_int_t *extents); @@ -1526,11 +1530,6 @@ _cairo_path_fixed_stroke_extents (const cairo_path_fixed_t *path, double tolerance, cairo_rectangle_int_t *extents); -cairo_private void -_cairo_path_fixed_bounds (const cairo_path_fixed_t *path, - double *x1, double *y1, - double *x2, double *y2); - cairo_private void _cairo_path_fixed_transform (cairo_path_fixed_t *path, const cairo_matrix_t *matrix);