[cairo-commit] 30 commits - src/cairo-arc.c src/cairo-box-private.h src/cairo.c src/cairo-clip.c src/cairo-gstate.c src/cairo-image-surface.c src/cairoint.h src/cairo-path-bounds.c src/cairo-path-fill.c src/cairo-path-fixed.c src/cairo-path-fixed-private.h src/cairo-path-in-fill.c src/cairo-path-stroke.c src/cairo-recording-surface.c src/cairo-rectangle.c src/cairo-surface-fallback.c test/get-path-extents.c

Andrea Canciani ranma42 at kemper.freedesktop.org
Fri Oct 29 09:46:12 PDT 2010


 src/cairo-arc.c                |    4 
 src/cairo-box-private.h        |   75 ++++++
 src/cairo-clip.c               |   18 -
 src/cairo-gstate.c             |    2 
 src/cairo-image-surface.c      |    6 
 src/cairo-path-bounds.c        |  249 ++++----------------
 src/cairo-path-fill.c          |    8 
 src/cairo-path-fixed-private.h |   53 +++-
 src/cairo-path-fixed.c         |  497 +++++++++++++++++++++--------------------
 src/cairo-path-in-fill.c       |    2 
 src/cairo-path-stroke.c        |    7 
 src/cairo-recording-surface.c  |    4 
 src/cairo-rectangle.c          |   34 ++
 src/cairo-surface-fallback.c   |    6 
 src/cairo.c                    |   21 +
 src/cairoint.h                 |   20 -
 test/get-path-extents.c        |    8 
 17 files changed, 513 insertions(+), 501 deletions(-)

New commits:
commit b8444a5c78c5d254b21331353884bcfe8efb0a5e
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 23:16:21 2010 +0200

    path: Tighten transformed extents
    
    The transformation code should produce tight extents if they are to be
    used in the new simple extents functions.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index fd7bdcd..3093f26 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1055,6 +1055,7 @@ void
 _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
 			     const cairo_matrix_t     *matrix)
 {
+    cairo_box_t extents;
     cairo_point_t point;
     cairo_path_buf_t *buf;
     unsigned int i;
@@ -1076,6 +1077,7 @@ _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
     if (buf->num_points == 0)
 	return;
 
+    extents = path->extents;
     point = buf->points[0];
     _cairo_path_fixed_transform_point (&point, matrix);
     _cairo_box_set (&path->extents, &point, &point);
@@ -1087,6 +1089,19 @@ _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
 	}
     } cairo_path_foreach_buf_end (buf, path);
 
+    if (path->has_curve_to) {
+	cairo_bool_t is_tight;
+
+	_cairo_matrix_transform_bounding_box_fixed (matrix, &extents, &is_tight);
+	if (!is_tight) {
+	    cairo_bool_t has_extents;
+
+	    has_extents = _cairo_path_bounder_extents (path, &extents);
+	    assert (has_extents);
+	}
+	path->extents = extents;
+    }
+
     /* flags might become more strict than needed */
     path->stroke_is_rectilinear = FALSE;
     path->fill_is_rectilinear = FALSE;
commit 89e1261dd0fdb6c6c0271f71dd84d72504969ab1
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 12:35:40 2010 +0200

    path-bounder: Simplify code
    
    If the path extents are tight, all the extents computations and
    approximations become trivial except for the stroke extents.

diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 9cfc56d..d17d9a2 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -1,3 +1,4 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
 /* cairo - a vector graphics library with display and print output
  *
  * Copyright © 2003 University of Southern California
@@ -35,48 +36,16 @@
  */
 
 #include "cairoint.h"
+#include "cairo-box-private.h"
 #include "cairo-path-fixed-private.h"
 
-typedef struct cairo_path_bounder {
-    cairo_point_t current_point;
-    cairo_bool_t has_initial_point;
-    cairo_bool_t has_point;
 
+typedef struct _cairo_path_bounder {
+    cairo_point_t current_point;
+    cairo_bool_t has_extents;
     cairo_box_t extents;
 } cairo_path_bounder_t;
 
-static void
-_cairo_path_bounder_init (cairo_path_bounder_t *bounder)
-{
-    bounder->has_initial_point = FALSE;
-    bounder->has_point = FALSE;
-}
-
-static void
-_cairo_path_bounder_add_point (cairo_path_bounder_t *bounder,
-			       const cairo_point_t *point)
-{
-    if (bounder->has_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;
-    } else {
-	bounder->extents.p1.x = point->x;
-	bounder->extents.p1.y = point->y;
-	bounder->extents.p2.x = point->x;
-	bounder->extents.p2.y = point->y;
-	bounder->has_point = TRUE;
-    }
-}
-
 static cairo_status_t
 _cairo_path_bounder_move_to (void *closure,
 			     const cairo_point_t *point)
@@ -84,7 +53,13 @@ _cairo_path_bounder_move_to (void *closure,
     cairo_path_bounder_t *bounder = closure;
 
     bounder->current_point = *point;
-    bounder->has_initial_point = TRUE;
+
+    if (likely (bounder->has_extents)) {
+	_cairo_box_add_point (&bounder->extents, point);
+    } else {
+	bounder->has_extents = TRUE;
+	_cairo_box_set (&bounder->extents, point, point);
+    }
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -95,13 +70,8 @@ _cairo_path_bounder_line_to (void *closure,
 {
     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, point);
     bounder->current_point = *point;
+    _cairo_box_add_point (&bounder->extents, point);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -114,25 +84,12 @@ _cairo_path_bounder_curve_to (void *closure,
 {
     cairo_path_bounder_t *bounder = closure;
 
-    /* If the bbox of the control points is entirely inside, then we
-     * do not need to further evaluate the spline.
-     */
-    if (! bounder->has_point ||
-	b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x ||
-	b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y ||
-	c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x ||
-	c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y ||
-	d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x ||
-	d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y)
-    {
-	return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder,
-				    &bounder->current_point, b, c, d);
-    }
-    else
-    {
-	/* All control points are within the current extents. */
-	return CAIRO_STATUS_SUCCESS;
-    }
+    _cairo_box_add_curve_to (&bounder->extents,
+			     &bounder->current_point,
+			     b, c, d);
+    bounder->current_point = *d;
+
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_status_t
@@ -141,53 +98,40 @@ _cairo_path_bounder_close_path (void *closure)
     return CAIRO_STATUS_SUCCESS;
 }
 
-/* This computes the extents of all the points in the path, not those of
- * the damage area (i.e it does not consider winding and it only inspects
- * the control points of the curves, not the flattened path).
- */
+cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+			     cairo_box_t *extents)
+{
+    cairo_path_bounder_t bounder;
+    cairo_status_t status;
+
+    bounder.has_extents = FALSE;
+    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);
+
+    if (bounder.has_extents)
+	*extents = bounder.extents;
+
+    return bounder.has_extents;
+}
+
 void
 _cairo_path_fixed_approximate_clip_extents (const cairo_path_fixed_t *path,
 					    cairo_rectangle_int_t *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;
-    }
+    _cairo_path_fixed_approximate_fill_extents (path, extents);
 }
 
-/* A slightly better approximation than above - we actually decompose the
- * Bezier, but we continue to ignore winding.
- */
 void
 _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path,
 					    cairo_rectangle_int_t *extents)
 {
-    cairo_path_bounder_t bounder;
-    cairo_status_t status;
-
-    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);
-    } else {
-	extents->x = extents->y = 0;
-	extents->width = extents->height = 0;
-    }
+    _cairo_path_fixed_fill_extents (path, CAIRO_FILL_RULE_WINDING, 0, extents);
 }
 
 void
@@ -196,25 +140,9 @@ _cairo_path_fixed_fill_extents (const cairo_path_fixed_t	*path,
 				double			 tolerance,
 				cairo_rectangle_int_t	*extents)
 {
-    cairo_path_bounder_t bounder;
-    cairo_status_t status;
-
-    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);
+    if (path->extents.p1.x < path->extents.p2.x &&
+	path->extents.p1.y < path->extents.p2.y) {
+	_cairo_box_round_to_rectangle (&path->extents, extents);
     } else {
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
@@ -228,65 +156,18 @@ _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
 					      const cairo_matrix_t *ctm,
 					      cairo_rectangle_int_t *extents)
 {
-    cairo_path_bounder_t bounder;
-    cairo_status_t status;
-
-    if (! path->has_curve_to) {
-	bounder.extents = path->extents;
-
-	/* include trailing move-to for degenerate segments */
-	if (path->needs_move_to) {
-	    const cairo_point_t *point = &path->current_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;
-
-	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
-
-	bounder.extents.p1.x -= _cairo_fixed_from_double (dx);
-	bounder.extents.p2.x += _cairo_fixed_from_double (dx);
-	bounder.extents.p1.y -= _cairo_fixed_from_double (dy);
-	bounder.extents.p2.y += _cairo_fixed_from_double (dy);
-
-	_cairo_box_round_to_rectangle (&bounder.extents, extents);
-    } else if (bounder.has_initial_point) {
+    if (path->has_extents) {
+	cairo_box_t box_extents;
 	double dx, dy;
 
-	/* accommodate capping of degenerate paths */
-
+	box_extents = path->extents;
 	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);
+	box_extents.p1.x -= _cairo_fixed_from_double (dx);
+	box_extents.p1.y -= _cairo_fixed_from_double (dy);
+	box_extents.p2.x += _cairo_fixed_from_double (dx);
+	box_extents.p2.y += _cairo_fixed_from_double (dy);
 
-	bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
-	bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
-	bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
-	bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);
-
-	_cairo_box_round_to_rectangle (&bounder.extents, extents);
+	_cairo_box_round_to_rectangle (&box_extents, extents);
     } else {
 	extents->x = extents->y = 0;
 	extents->width = extents->height = 0;
@@ -326,24 +207,6 @@ 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->has_extents;
-    }
-
-    _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);
-
-    *box = bounder.extents;
-    return bounder.has_point;
+    *box = path->extents;
+    return path->has_extents;
 }
diff --git a/src/cairoint.h b/src/cairoint.h
index 4ca6a42..5836101 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1217,6 +1217,11 @@ _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path,
 		       void				  *closure,
 		       double				  tolerance);
 
+
+cairo_private cairo_bool_t
+_cairo_path_bounder_extents (const cairo_path_fixed_t *path,
+			     cairo_box_t *box);
+
 cairo_private cairo_bool_t
 _cairo_path_fixed_extents (const cairo_path_fixed_t *path,
 			   cairo_box_t *box);
commit 958c56e2b4f5447cc5a1cc137a8d287aebabe5dc
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 23:03:51 2010 +0200

    path: Tighten curve_to extents
    
    The additional time spent in the computation of tight extents for the
    curve_to operation doesn't seem to be significant, but it makes the
    extents computations faster and the approximations more accurate.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 636a889..fd7bdcd 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -609,10 +609,8 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     point[1].x = x1; point[1].y = y1;
     point[2].x = x2; point[2].y = y2;
 
-    /* coarse bounds */
-    _cairo_box_add_point (&path->extents, &point[0]);
-    _cairo_box_add_point (&path->extents, &point[1]);
-    _cairo_box_add_point (&path->extents, &point[2]);
+    _cairo_box_add_curve_to (&path->extents, &path->current_point,
+			     &point[0], &point[1], &point[2]);
 
     path->current_point = point[2];
     path->has_curve_to = TRUE;
commit 0655198301ec60b387b581a10b991ee442743374
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Oct 16 22:51:28 2010 +0200

    Fix degenerate arcs
    
    Make sure that degenerate arcs become a move_to-line_to sequence
    instead of just a move_to.
    
    Fixes get-path-extents.

diff --git a/src/cairo-arc.c b/src/cairo-arc.c
index 56d42f1..1b2713f 100644
--- a/src/cairo-arc.c
+++ b/src/cairo-arc.c
@@ -231,6 +231,10 @@ _cairo_arc_in_direction (cairo_t	  *cr,
 				angle,
 				angle + angle_step);
 	}
+    } else {
+	cairo_line_to (cr,
+		       xc + radius * cos (angle_min),
+		       yc + radius * sin (angle_min));
     }
 }
 
diff --git a/src/cairo.c b/src/cairo.c
index cf2824d..5621610 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -1856,6 +1856,7 @@ cairo_arc (cairo_t *cr,
 
     /* Do nothing, successfully, if radius is <= 0 */
     if (radius <= 0.0) {
+	cairo_line_to (cr, xc, yc); /* might become a move_to */
 	cairo_line_to (cr, xc, yc);
 	return;
     }
commit a1d8763236ccbb7bc11724a87b25bacafce44a7c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 23:06:36 2010 +0200

    path: Replace _cairo_path_fixed_extents_add with _cairo_box_add_point
    
    Path extents now satisfy _cairo_box_add_point requirements, so it can
    be used instead of _cairo_path_fixed_extents_add.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 8952cff..636a889 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -408,21 +408,6 @@ _cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path)
     buf->num_ops--;
 }
 
-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,
@@ -625,9 +610,9 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     point[2].x = x2; point[2].y = y2;
 
     /* coarse bounds */
-    _cairo_path_fixed_extents_add (path, &point[0]);
-    _cairo_path_fixed_extents_add (path, &point[1]);
-    _cairo_path_fixed_extents_add (path, &point[2]);
+    _cairo_box_add_point (&path->extents, &point[0]);
+    _cairo_box_add_point (&path->extents, &point[1]);
+    _cairo_box_add_point (&path->extents, &point[2]);
 
     path->current_point = point[2];
     path->has_curve_to = TRUE;
commit 02687065509aa4dead4528b2c8dd9aa31dc76332
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 21 17:25:46 2010 +0200

    path: Fix _cairo_path_fixed_transform
    
    current_point and last_move_to were previously left in their old
    position (which could lead to incorrect flag computation if other
    operations were added to the path) and flags were not updated.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 7ee9104..8952cff 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1045,6 +1045,20 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path,
     path->extents.p2.y += offy;
 }
 
+
+static inline void
+_cairo_path_fixed_transform_point (cairo_point_t *p,
+				   const cairo_matrix_t *matrix)
+{
+    double dx, dy;
+
+    dx = _cairo_fixed_to_double (p->x);
+    dy = _cairo_fixed_to_double (p->y);
+    cairo_matrix_transform_point (matrix, &dx, &dy);
+    p->x = _cairo_fixed_from_double (dx);
+    p->y = _cairo_fixed_from_double (dy);
+}
+
 /**
  * _cairo_path_fixed_transform:
  * @path: a #cairo_path_fixed_t to be transformed
@@ -1058,11 +1072,9 @@ void
 _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
 			     const cairo_matrix_t     *matrix)
 {
+    cairo_point_t point;
     cairo_path_buf_t *buf;
     unsigned int i;
-    double dx, dy;
-
-    /* XXX current_point, last_move_to */
 
     if (matrix->yx == 0.0 && matrix->xy == 0.0) {
 	/* Fast path for the common case of scale+transform */
@@ -1074,23 +1086,29 @@ _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
 	return;
     }
 
-    path->extents.p1.x = path->extents.p1.y = INT_MAX;
-    path->extents.p2.x = path->extents.p2.y = INT_MIN;
-    path->fill_maybe_region = FALSE;
-    cairo_path_foreach_buf_start (buf, path) {
-	 for (i = 0; i < buf->num_points; i++) {
-	    dx = _cairo_fixed_to_double (buf->points[i].x);
-	    dy = _cairo_fixed_to_double (buf->points[i].y);
+    _cairo_path_fixed_transform_point (&path->last_move_point, matrix);
+    _cairo_path_fixed_transform_point (&path->current_point, matrix);
 
-	    cairo_matrix_transform_point (matrix, &dx, &dy);
+    buf = cairo_path_head (path);
+    if (buf->num_points == 0)
+	return;
 
-	    buf->points[i].x = _cairo_fixed_from_double (dx);
-	    buf->points[i].y = _cairo_fixed_from_double (dy);
+    point = buf->points[0];
+    _cairo_path_fixed_transform_point (&point, matrix);
+    _cairo_box_set (&path->extents, &point, &point);
 
-	    /* XXX need to eliminate surplus move-to's? */
-	    _cairo_path_fixed_extents_add (path, &buf->points[i]);
-	 }
+    cairo_path_foreach_buf_start (buf, path) {
+	for (i = 0; i < buf->num_points; i++) {
+	    _cairo_path_fixed_transform_point (&buf->points[i], matrix);
+	    _cairo_box_add_point (&path->extents, &buf->points[i]);
+	}
     } cairo_path_foreach_buf_end (buf, path);
+
+    /* flags might become more strict than needed */
+    path->stroke_is_rectilinear = FALSE;
+    path->fill_is_rectilinear = FALSE;
+    path->fill_is_empty = FALSE;
+    path->fill_maybe_region = FALSE;
 }
 
 /* Closure for path flattening */
commit 29d5b18cba05357dc4885447035e65f93e7d7728
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 28 18:38:50 2010 +0200

    path: Recompute flags in _cairo_path_fixed_scale_and_offset
    
    Only fill_maybe_region can change its value because the transformation
    preserves vertical and horizontal lines, but can move the points and
    make them integer if they were not or non-integer if they were.
    
    Recomputing it is just as easy as checking if all the points are
    integer and the path is fill_is_rectilinear.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 4fdcfe0..7ee9104 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -979,15 +979,8 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
     path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy;
     path->current_point.x   = _cairo_fixed_mul (scalex, path->current_point.x) + offx;
     path->current_point.y   = _cairo_fixed_mul (scaley, path->current_point.y) + offy;
-    /* Recompute an approximation of the flags.
-     * It might be more strict than what is actually needed.
-     */
-    if (path->fill_maybe_region) {
-	path->fill_maybe_region = _cairo_fixed_is_integer (offx) &&
-	                          _cairo_fixed_is_integer (offy) &&
-			          _cairo_fixed_is_integer (scalex) &&
-			          _cairo_fixed_is_integer (scaley);
-    }
+
+    path->fill_maybe_region = TRUE;
 
     cairo_path_foreach_buf_start (buf, path) {
 	 for (i = 0; i < buf->num_points; i++) {
@@ -998,12 +991,18 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
 	     if (scaley != CAIRO_FIXED_ONE)
 		 buf->points[i].y = _cairo_fixed_mul (buf->points[i].y, scaley);
 	     buf->points[i].y += offy;
+
+	    if (path->fill_maybe_region) {
+		path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+					  _cairo_fixed_is_integer (buf->points[i].y);
+	    }
 	 }
     } cairo_path_foreach_buf_end (buf, path);
 
+    path->fill_maybe_region &= path->fill_is_rectilinear;
+
     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;
 }
commit 634fcf2c0a66b342ae81faa0e0a75ae72491a313
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 28 18:36:42 2010 +0200

    path: Transform current_point and last_move_to in _cairo_path_fixed_scale_and_offset
    
    They were previously left in their old position (which could lead to
    incorrect flag computation if other operations were added to the
    path).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 4ab4e26..4fdcfe0 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -975,6 +975,10 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
 	return;
     }
 
+    path->last_move_point.x = _cairo_fixed_mul (scalex, path->last_move_point.x) + offx;
+    path->last_move_point.y = _cairo_fixed_mul (scaley, path->last_move_point.y) + offy;
+    path->current_point.x   = _cairo_fixed_mul (scalex, path->current_point.x) + offx;
+    path->current_point.y   = _cairo_fixed_mul (scaley, path->current_point.y) + offy;
     /* Recompute an approximation of the flags.
      * It might be more strict than what is actually needed.
      */
commit 9c0e4db570d9de506eb48de0e9a27497b8cf2f61
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 22:19:48 2010 +0200

    path: Recompute flags in _cairo_path_fixed_translate
    
    Only fill_maybe_region can change its value because the transformation
    preserves vertical and horizontal lines, but can move the points and
    make them integer if they were not or non-integer if they were.
    
    Recomputing it is just as easy as checking if all the points are
    integer and the path is fill_is_rectilinear.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index e09426d..4ab4e26 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1015,26 +1015,27 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path,
     if (offx == 0 && offy == 0)
 	return;
 
-    /* Recompute an approximation of the flags.
-     * It might be more strict than what is actually needed.
-     */
-    if (path->fill_maybe_region) {
-	path->fill_maybe_region = _cairo_fixed_is_integer (offx) &&
-				  _cairo_fixed_is_integer (offy);
-    }
-
     path->last_move_point.x += offx;
     path->last_move_point.y += offy;
     path->current_point.x += offx;
     path->current_point.y += offy;
 
+    path->fill_maybe_region = TRUE;
+
     cairo_path_foreach_buf_start (buf, path) {
-	 for (i = 0; i < buf->num_points; i++) {
-	     buf->points[i].x += offx;
-	     buf->points[i].y += offy;
+	for (i = 0; i < buf->num_points; i++) {
+	    buf->points[i].x += offx;
+	    buf->points[i].y += offy;
+
+	    if (path->fill_maybe_region) {
+		path->fill_maybe_region = _cairo_fixed_is_integer (buf->points[i].x) &&
+					  _cairo_fixed_is_integer (buf->points[i].y);
+	    }
 	 }
     } cairo_path_foreach_buf_end (buf, path);
 
+    path->fill_maybe_region &= path->fill_is_rectilinear;
+
     path->extents.p1.x += offx;
     path->extents.p1.y += offy;
     path->extents.p2.x += offx;
commit 9d84dff0c6a7be5abf1f931eabe77afca21f04aa
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 15:02:54 2010 +0200

    path: Cleanup close_path
    
    Instead of explicitly calling _cairo_fixed_move_to, setting the
    needs_move_to flags is sufficient because the current_point is already
    updeted correctly.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 812728b..e09426d 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -686,13 +686,9 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
     if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO)
 	    _cairo_path_fixed_drop_line_to (path);
 
-    status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
-    if (unlikely (status))
-	return status;
+    path->needs_move_to = TRUE; /* After close_path, add an implicit move_to */
 
-    return _cairo_path_fixed_move_to (path,
-				      path->last_move_point.x,
-				      path->last_move_point.y);
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
 }
 
 cairo_bool_t
commit 17fef2fe4ddcba8d0811922f012add50109eb0e2
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 11:56:49 2010 +0200

    path: Make _cairo_path_fixed_last_op assert on empty path
    
    _cairo_path_fixed_last_op should now only be used on non-empty path
    (to test if the previous operation was a line_to).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 5b2bcbe..812728b 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -375,8 +375,7 @@ _cairo_path_fixed_last_op (cairo_path_fixed_t *path)
     cairo_path_buf_t *buf;
 
     buf = cairo_path_tail (path);
-    if (buf->num_ops == 0)
-	return -1;
+    assert (buf->num_ops != 0);
 
     return buf->op[buf->num_ops - 1];
 }
commit 568a975a62fde8d4dfaef2086c4f40e3354ba43b
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 21 17:24:30 2010 +0200

    path: Cleanup _cairo_path_fixed_iter_at_end
    
    The last operation of a path cannot be a move_to anymore (since
    move_to is only added if another operation is added after it).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index f6b6cb4..5b2bcbe 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1426,14 +1426,5 @@ _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter)
     if (iter->buf == NULL)
 	return TRUE;
 
-    if (iter->n_op == iter->buf->num_ops)
-	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;
+    return iter->n_op == iter->buf->num_ops;
 }
commit 929571b4b56d56c669eb4cbb3e109e2bd4620fb0
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 21 17:11:48 2010 +0200

    path: Cleanup _cairo_path_fixed_transform
    
    Clean up the code and make sure that _cairo_path_fixed_translate is
    used whenever the _cairo_fixed_to_double rounding would result in the
    matrix being approximated with a translation.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 76fc196..f6b6cb4 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -975,6 +975,11 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
     cairo_path_buf_t *buf;
     unsigned int i;
 
+    if (scalex == CAIRO_FIXED_ONE && scaley == CAIRO_FIXED_ONE) {
+	_cairo_path_fixed_translate (path, offx, offy);
+	return;
+    }
+
     /* Recompute an approximation of the flags.
      * It might be more strict than what is actually needed.
      */
@@ -1062,17 +1067,11 @@ _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 */
-	 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));
-	 }
+	_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;
     }
 
commit 34f1db13a13f80733a7eb5bef4693c4df7a6fb4e
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 21 16:59:01 2010 +0200

    path: Log flags
    
    When logging path operations, also log computed flags.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 0f8bd5f..76fc196 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -749,9 +749,20 @@ _cairo_path_fixed_add (cairo_path_fixed_t   *path,
 	}
 	len += snprintf (buf + len, sizeof (buf), "]");
 
+#define STRINGIFYFLAG(x)  (path->x ? #x " " : "")
 	fprintf (stderr,
-		 "_cairo_path_fixed_add (%s, %s)\n",
-		 op_str[(int) op], buf);
+		 "_cairo_path_fixed_add (%s, %s) [%s%s%s%s%s%s%s%s]\n",
+		 op_str[(int) op], buf,
+		 STRINGIFYFLAG(has_current_point),
+		 STRINGIFYFLAG(needs_move_to),
+		 STRINGIFYFLAG(has_extents),
+		 STRINGIFYFLAG(has_curve_to),
+		 STRINGIFYFLAG(stroke_is_rectilinear),
+		 STRINGIFYFLAG(fill_is_rectilinear),
+		 STRINGIFYFLAG(fill_is_empty),
+		 STRINGIFYFLAG(fill_maybe_region)
+		 );
+#undef STRINGIFYFLAG
     }
 
     _cairo_path_buf_add_op (buf, op);
commit e8e614db92c43ed1b22251a1903396c99022326c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 28 18:02:59 2010 +0200

    path: Rename fill optimization flags
    
    Rename fill optimization flags making fill_ their common prefix.

diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index 21f30f3..ce138de 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -77,6 +77,13 @@ typedef struct _cairo_path_buf_fixed {
     cairo_point_t points[2 * CAIRO_PATH_BUF_SIZE];
 } cairo_path_buf_fixed_t;
 
+/*
+  NOTES:
+  has_curve_to => !stroke_is_rectilinear
+  fill_is_rectilinear => stroke_is_rectilinear
+  fill_is_empty => fill_is_rectilinear
+  fill_maybe_region => fill_is_rectilinear
+*/
 struct _cairo_path_fixed {
     cairo_point_t last_move_point;
     cairo_point_t current_point;
@@ -86,8 +93,8 @@ struct _cairo_path_fixed {
     unsigned int has_curve_to		: 1;
     unsigned int stroke_is_rectilinear	: 1;
     unsigned int fill_is_rectilinear	: 1;
-    unsigned int maybe_fill_region	: 1;
-    unsigned int is_empty_fill		: 1;
+    unsigned int fill_maybe_region	: 1;
+    unsigned int fill_is_empty		: 1;
 
     cairo_box_t extents;
 
@@ -137,7 +144,7 @@ _cairo_path_fixed_iter_at_end (const cairo_path_fixed_iter_t *iter);
 static inline cairo_bool_t
 _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
 {
-    return path->is_empty_fill;
+    return path->fill_is_empty;
 }
 
 static inline cairo_bool_t
@@ -163,7 +170,7 @@ _cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
 static inline cairo_bool_t
 _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
 {
-    if (! path->maybe_fill_region)
+    if (! path->fill_maybe_region)
 	return FALSE;
 
     if (! path->has_current_point || path->needs_move_to)
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index df81dd8..0f8bd5f 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -105,8 +105,8 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path)
     path->has_curve_to = FALSE;
     path->stroke_is_rectilinear = TRUE;
     path->fill_is_rectilinear = TRUE;
-    path->maybe_fill_region = TRUE;
-    path->is_empty_fill = TRUE;
+    path->fill_maybe_region = TRUE;
+    path->fill_is_empty = TRUE;
 
     path->extents.p1.x = path->extents.p1.y = 0;
     path->extents.p2.x = path->extents.p2.y = 0;
@@ -137,8 +137,8 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
     path->has_curve_to = other->has_curve_to;
     path->stroke_is_rectilinear = other->stroke_is_rectilinear;
     path->fill_is_rectilinear = other->fill_is_rectilinear;
-    path->maybe_fill_region = other->maybe_fill_region;
-    path->is_empty_fill = other->is_empty_fill;
+    path->fill_maybe_region = other->fill_maybe_region;
+    path->fill_is_empty = other->fill_is_empty;
 
     path->extents = other->extents;
 
@@ -453,8 +453,8 @@ _cairo_path_fixed_move_to_apply (cairo_path_fixed_t  *path)
 	path->has_extents = TRUE;
     }
 
-    if (path->maybe_fill_region) {
-	path->maybe_fill_region = _cairo_fixed_is_integer (path->current_point.x) &&
+    if (path->fill_maybe_region) {
+	path->fill_maybe_region = _cairo_fixed_is_integer (path->current_point.x) &&
 				  _cairo_fixed_is_integer (path->current_point.y);
     }
 
@@ -472,7 +472,7 @@ _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
 	    /* Implicitly close for fill */
 	    path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x ||
 					path->current_point.y == path->last_move_point.y;
-	    path->maybe_fill_region &= path->fill_is_rectilinear;
+	    path->fill_maybe_region &= path->fill_is_rectilinear;
 	}
 	path->needs_move_to = TRUE;
     }
@@ -560,13 +560,13 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	path->stroke_is_rectilinear = path->current_point.x == x ||
 				      path->current_point.y == y;
 	path->fill_is_rectilinear &= path->stroke_is_rectilinear;
-	path->maybe_fill_region &= path->fill_is_rectilinear;
-	if (path->maybe_fill_region) {
-	    path->maybe_fill_region = _cairo_fixed_is_integer (x) &&
+	path->fill_maybe_region &= path->fill_is_rectilinear;
+	if (path->fill_maybe_region) {
+	    path->fill_maybe_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 &&
+	if (path->fill_is_empty) {
+	    path->fill_is_empty = path->current_point.x == x &&
 				  path->current_point.y == y;
 	}
     }
@@ -634,8 +634,8 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     path->has_curve_to = TRUE;
     path->stroke_is_rectilinear = FALSE;
     path->fill_is_rectilinear = FALSE;
-    path->maybe_fill_region = FALSE;
-    path->is_empty_fill = FALSE;
+    path->fill_maybe_region = FALSE;
+    path->fill_is_empty = FALSE;
 
     return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
 }
@@ -964,8 +964,11 @@ _cairo_path_fixed_offset_and_scale (cairo_path_fixed_t *path,
     cairo_path_buf_t *buf;
     unsigned int i;
 
-    if (path->maybe_fill_region) {
-	path->maybe_fill_region = _cairo_fixed_is_integer (offx) &&
+    /* Recompute an approximation of the flags.
+     * It might be more strict than what is actually needed.
+     */
+    if (path->fill_maybe_region) {
+	path->fill_maybe_region = _cairo_fixed_is_integer (offx) &&
 	                          _cairo_fixed_is_integer (offy) &&
 			          _cairo_fixed_is_integer (scalex) &&
 			          _cairo_fixed_is_integer (scaley);
@@ -1001,10 +1004,12 @@ _cairo_path_fixed_translate (cairo_path_fixed_t *path,
     if (offx == 0 && offy == 0)
 	return;
 
-    if (path->maybe_fill_region &&
-	! (_cairo_fixed_is_integer (offx) && _cairo_fixed_is_integer (offy)))
-    {
-	path->maybe_fill_region = FALSE;
+    /* Recompute an approximation of the flags.
+     * It might be more strict than what is actually needed.
+     */
+    if (path->fill_maybe_region) {
+	path->fill_maybe_region = _cairo_fixed_is_integer (offx) &&
+				  _cairo_fixed_is_integer (offy);
     }
 
     path->last_move_point.x += offx;
@@ -1062,7 +1067,7 @@ _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
 
     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;
+    path->fill_maybe_region = FALSE;
     cairo_path_foreach_buf_start (buf, path) {
 	 for (i = 0; i < buf->num_points; i++) {
 	    dx = _cairo_fixed_to_double (buf->points[i].x);
diff --git a/src/cairo.c b/src/cairo.c
index 606b7be..cf2824d 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -122,8 +122,8 @@ static const cairo_t _cairo_nil = {
     FALSE,			/* has_curve_to */
     TRUE,			/* stroke_is_rectilinear */
     TRUE,			/* fill_is_rectilinear */
-    FALSE,			/* maybe_fill_region */
-    TRUE,			/* is_empty_fill */
+    TRUE,			/* fill_maybe_region */
+    TRUE,			/* fill_is_empty */
     { {0, 0}, {0, 0}},		/* extents */
     {{{NULL,NULL}}}		/* link */
   }}
@@ -145,8 +145,8 @@ static const cairo_t _cairo_nil__null_pointer = {
     FALSE,			/* has_curve_to */
     TRUE,			/* stroke_is_rectilinear */
     TRUE,			/* fill_is_rectilinear */
-    FALSE,			/* maybe_fill_region */
-    TRUE,			/* is_empty_fill */
+    TRUE,			/* fill_maybe_region */
+    TRUE,			/* fill_is_empty */
     { {0, 0}, {0, 0}},		/* extents */
     {{{NULL,NULL}}}		/* link */
   }}
commit e48cb95493c1dc9532ae0d689238ff3bc317cc4c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 28 17:56:37 2010 +0200

    path: Add stroke_is_rectilinear flag
    
    Stroke and fill rectilinearity cannot be represented by a single flag
    without missing the opportunity of considering some strokes
    rectilinear.

diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index c1ad047..21f30f3 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -84,7 +84,8 @@ struct _cairo_path_fixed {
     unsigned int needs_move_to		: 1;
     unsigned int has_extents		: 1;
     unsigned int has_curve_to		: 1;
-    unsigned int is_rectilinear		: 1;
+    unsigned int stroke_is_rectilinear	: 1;
+    unsigned int fill_is_rectilinear	: 1;
     unsigned int maybe_fill_region	: 1;
     unsigned int is_empty_fill		: 1;
 
@@ -142,8 +143,8 @@ _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
 static inline cairo_bool_t
 _cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
 {
-    if (! path->is_rectilinear)
-	return 0;
+    if (! path->fill_is_rectilinear)
+	return FALSE;
 
     if (! path->has_current_point || path->needs_move_to)
 	return TRUE;
@@ -156,7 +157,7 @@ _cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
 static inline cairo_bool_t
 _cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
 {
-    return path->is_rectilinear;
+    return path->stroke_is_rectilinear;
 }
 
 static inline cairo_bool_t
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index d3c2c99..df81dd8 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -103,7 +103,8 @@ _cairo_path_fixed_init (cairo_path_fixed_t *path)
     path->needs_move_to = TRUE;
     path->has_extents = FALSE;
     path->has_curve_to = FALSE;
-    path->is_rectilinear = TRUE;
+    path->stroke_is_rectilinear = TRUE;
+    path->fill_is_rectilinear = TRUE;
     path->maybe_fill_region = TRUE;
     path->is_empty_fill = TRUE;
 
@@ -134,7 +135,8 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
     path->needs_move_to = other->needs_move_to;
     path->has_extents = other->has_extents;
     path->has_curve_to = other->has_curve_to;
-    path->is_rectilinear = other->is_rectilinear;
+    path->stroke_is_rectilinear = other->stroke_is_rectilinear;
+    path->fill_is_rectilinear = other->fill_is_rectilinear;
     path->maybe_fill_region = other->maybe_fill_region;
     path->is_empty_fill = other->is_empty_fill;
 
@@ -466,11 +468,11 @@ _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
 {
     if (! path->needs_move_to) {
 	/* If the current subpath doesn't need_move_to, it contains at least one command */
-	if (path->is_rectilinear) {
+	if (path->fill_is_rectilinear) {
 	    /* Implicitly close for fill */
-	    path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
-				   path->current_point.y == path->last_move_point.y;
-	    path->maybe_fill_region &= path->is_rectilinear;
+	    path->fill_is_rectilinear = path->current_point.x == path->last_move_point.x ||
+					path->current_point.y == path->last_move_point.y;
+	    path->maybe_fill_region &= path->fill_is_rectilinear;
 	}
 	path->needs_move_to = TRUE;
     }
@@ -554,19 +556,19 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	}
     }
 
-
-    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;
+    if (path->stroke_is_rectilinear) {
+	path->stroke_is_rectilinear = path->current_point.x == x ||
+				      path->current_point.y == y;
+	path->fill_is_rectilinear &= path->stroke_is_rectilinear;
+	path->maybe_fill_region &= path->fill_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;
@@ -630,7 +632,8 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
 
     path->current_point = point[2];
     path->has_curve_to = TRUE;
-    path->is_rectilinear = FALSE;
+    path->stroke_is_rectilinear = FALSE;
+    path->fill_is_rectilinear = FALSE;
     path->maybe_fill_region = FALSE;
     path->is_empty_fill = FALSE;
 
@@ -1204,7 +1207,7 @@ _cairo_path_fixed_is_box (const cairo_path_fixed_t *path,
 {
     const cairo_path_buf_t *buf = cairo_path_head (path);
 
-    if (! path->is_rectilinear)
+    if (! path->fill_is_rectilinear)
 	return FALSE;
 
     /* Do we have the right number of ops? */
diff --git a/src/cairo.c b/src/cairo.c
index 022e55f..606b7be 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -120,7 +120,8 @@ static const cairo_t _cairo_nil = {
     TRUE,			/* needs_move_to */
     FALSE,			/* has_extents */
     FALSE,			/* has_curve_to */
-    FALSE,			/* is_box */
+    TRUE,			/* stroke_is_rectilinear */
+    TRUE,			/* fill_is_rectilinear */
     FALSE,			/* maybe_fill_region */
     TRUE,			/* is_empty_fill */
     { {0, 0}, {0, 0}},		/* extents */
@@ -142,7 +143,8 @@ static const cairo_t _cairo_nil__null_pointer = {
     TRUE,			/* needs_move_to */
     FALSE,			/* has_extents */
     FALSE,			/* has_curve_to */
-    FALSE,			/* is_box */
+    TRUE,			/* stroke_is_rectilinear */
+    TRUE,			/* fill_is_rectilinear */
     FALSE,			/* maybe_fill_region */
     TRUE,			/* is_empty_fill */
     { {0, 0}, {0, 0}},		/* extents */
commit 166453c1abf2279b884a4d878729fa4fcfa550cb
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Oct 21 15:14:31 2010 +0200

    path: New path construction logic
    
    Now move_to's are actually added to the path when followed by a
    drawing operation (line_to, curve_to or close_path).
    
    This is implemented by updating the current_point and setting the
    needs_move_to when a move_to operation is requested.
    
    Whenever a drawing operation is requested and the needs_move_to flag
    is set, a move_to is added before the drawing operation.

diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index d32c260..9cfc56d 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -235,8 +235,8 @@ _cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
 	bounder.extents = path->extents;
 
 	/* include trailing move-to for degenerate segments */
-	if (path->has_last_move_point) {
-	    const cairo_point_t *point = &path->last_move_point;
+	if (path->needs_move_to) {
+	    const cairo_point_t *point = &path->current_point;
 
 	    if (point->x < bounder.extents.p1.x)
 		bounder.extents.p1.x = point->x;
@@ -331,7 +331,7 @@ _cairo_path_fixed_extents (const cairo_path_fixed_t *path,
 
     if (! path->has_curve_to) {
 	*box = path->extents;
-	return path->extents.p1.x < path->extents.p2.x;
+	return path->has_extents;
     }
 
     _cairo_path_bounder_init (&bounder);
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index 5d665db..c1ad047 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -81,7 +81,8 @@ 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 needs_move_to		: 1;
+    unsigned int has_extents		: 1;
     unsigned int has_curve_to		: 1;
     unsigned int is_rectilinear		: 1;
     unsigned int maybe_fill_region	: 1;
@@ -144,8 +145,8 @@ _cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
     if (! path->is_rectilinear)
 	return 0;
 
-    if (! path->has_current_point)
-	return 1;
+    if (! path->has_current_point || path->needs_move_to)
+	return TRUE;
 
     /* check whether the implicit close preserves the rectilinear property */
     return path->current_point.x == path->last_move_point.x ||
@@ -164,7 +165,7 @@ _cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
     if (! path->maybe_fill_region)
 	return FALSE;
 
-    if (! path->has_current_point)
+    if (! path->has_current_point || path->needs_move_to)
 	return TRUE;
 
     /* check whether the implicit close preserves the rectilinear property
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 627d2cc..d3c2c99 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -98,15 +98,17 @@ _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->needs_move_to = TRUE;
+    path->has_extents = 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;
+    path->extents.p1.x = path->extents.p1.y = 0;
+    path->extents.p2.x = path->extents.p2.y = 0;
 }
 
 cairo_status_t
@@ -127,8 +129,10 @@ _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->needs_move_to = other->needs_move_to;
+    path->has_extents = other->has_extents;
     path->has_curve_to = other->has_curve_to;
     path->is_rectilinear = other->is_rectilinear;
     path->maybe_fill_region = other->maybe_fill_region;
@@ -423,48 +427,54 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t  *path,
 			   cairo_fixed_t	x,
 			   cairo_fixed_t	y)
 {
-    cairo_status_t status;
-    cairo_point_t point;
+    _cairo_path_fixed_new_sub_path (path);
 
-    point.x = x;
-    point.y = y;
+    path->has_current_point = TRUE;
+    path->current_point.x = x;
+    path->current_point.y = y;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_path_fixed_move_to_apply (cairo_path_fixed_t  *path)
+{
+    if (likely (! path->needs_move_to))
+	return CAIRO_STATUS_SUCCESS;
 
-    /* If the previous op was also a MOVE_TO, then just change its
-     * point rather than adding a new op. */
-    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
-	cairo_path_buf_t *buf;
+    path->needs_move_to = FALSE;
 
-	buf = cairo_path_tail (path);
-	buf->points[buf->num_points - 1] = point;
+    if (path->has_extents) {
+	_cairo_box_add_point (&path->extents, &path->current_point);
     } else {
-	status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
-	if (unlikely (status))
-	    return status;
+	_cairo_box_set (&path->extents, &path->current_point, &path->current_point);
+	path->has_extents = TRUE;
+    }
 
-	if (path->has_current_point && path->is_rectilinear) {
-	    /* a move-to is first an implicit close */
-	    path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
-				   path->current_point.y == path->last_move_point.y;
-	    path->maybe_fill_region &= path->is_rectilinear;
-	}
-	if (path->maybe_fill_region) {
-	    path->maybe_fill_region =
-		_cairo_fixed_is_integer (path->last_move_point.x) &&
-		_cairo_fixed_is_integer (path->last_move_point.y);
-	}
+    if (path->maybe_fill_region) {
+	path->maybe_fill_region = _cairo_fixed_is_integer (path->current_point.x) &&
+				  _cairo_fixed_is_integer (path->current_point.y);
     }
 
-    path->current_point = point;
-    path->last_move_point = point;
-    path->has_last_move_point = TRUE;
-    path->has_current_point = TRUE;
+    path->last_move_point = path->current_point;
 
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &path->current_point, 1);
 }
 
 void
 _cairo_path_fixed_new_sub_path (cairo_path_fixed_t *path)
 {
+    if (! path->needs_move_to) {
+	/* If the current subpath doesn't need_move_to, it contains at least one command */
+	if (path->is_rectilinear) {
+	    /* Implicitly close for fill */
+	    path->is_rectilinear = path->current_point.x == path->last_move_point.x ||
+				   path->current_point.y == path->last_move_point.y;
+	    path->maybe_fill_region &= path->is_rectilinear;
+	}
+	path->needs_move_to = TRUE;
+    }
+
     path->has_current_point = FALSE;
 }
 
@@ -501,6 +511,10 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
     if (! path->has_current_point)
 	return _cairo_path_fixed_move_to (path, point.x, point.y);
 
+    status = _cairo_path_fixed_move_to_apply (path);
+    if (unlikely (status))
+	return status;
+
     /* If the previous op was but the initial MOVE_TO and this segment
      * is degenerate, then we can simply skip this point. Note that
      * a move-to followed by a degenerate line-to is a valid path for
@@ -556,11 +570,8 @@ _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);
+
+    _cairo_box_add_point (&path->extents, &point);
 
     return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
 }
@@ -590,10 +601,13 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     /* make sure subpaths are started properly */
     if (! path->has_current_point) {
 	status = _cairo_path_fixed_move_to (path, x0, y0);
-	if (unlikely (status))
-	    return status;
+	assert (status == CAIRO_STATUS_SUCCESS);
     }
 
+    status = _cairo_path_fixed_move_to_apply (path);
+    if (unlikely (status))
+	return status;
+
     /* If the previous op was a degenerate LINE_TO, drop it. */
     if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
 	const cairo_point_t *p;
@@ -609,22 +623,17 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     point[1].x = x1; point[1].y = y1;
     point[2].x = x2; point[2].y = y2;
 
-    path->current_point = point[2];
-    path->has_current_point = TRUE;
-    path->is_empty_fill = FALSE;
-    path->has_curve_to = TRUE;
-    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]);
 
+    path->current_point = point[2];
+    path->has_curve_to = TRUE;
+    path->is_rectilinear = FALSE;
+    path->maybe_fill_region = FALSE;
+    path->is_empty_fill = FALSE;
+
     return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
 }
 
diff --git a/src/cairo.c b/src/cairo.c
index 157f898..022e55f 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -117,7 +117,8 @@ static const cairo_t _cairo_nil = {
     { 0, 0 },			/* last_move_point */
     { 0, 0 },			/* current point */
     FALSE,			/* has_current_point */
-    FALSE,			/* has_last_move_point */
+    TRUE,			/* needs_move_to */
+    FALSE,			/* has_extents */
     FALSE,			/* has_curve_to */
     FALSE,			/* is_box */
     FALSE,			/* maybe_fill_region */
@@ -138,7 +139,8 @@ static const cairo_t _cairo_nil__null_pointer = {
     { 0, 0 },			/* last_move_point */
     { 0, 0 },			/* current point */
     FALSE,			/* has_current_point */
-    FALSE,			/* has_last_move_point */
+    TRUE,			/* needs_move_to */
+    FALSE,			/* has_extents */
     FALSE,			/* has_curve_to */
     FALSE,			/* is_box */
     FALSE,			/* maybe_fill_region */
commit a2ac91eb5f66f4c633abbcd6945f6015837ff211
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 14:10:58 2010 +0200

    path: Drop degenerate line_to in _cairo_path_fixed_curve_to
    
    When a degenerate line_to is followed by a curve_to operation, the
    line_to can be safely dropped, just like for degenerate line_to
    followed by line_to.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 36e18ec..627d2cc 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -594,6 +594,17 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
 	    return status;
     }
 
+    /* If the previous op was a degenerate LINE_TO, drop it. */
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+	const cairo_point_t *p;
+
+	p = _cairo_path_fixed_penultimate_point (path);
+	if (p->x == path->current_point.x && p->y == path->current_point.y) {
+	    /* previous line element was degenerate, replace */
+	    _cairo_path_fixed_drop_line_to (path);
+	}
+    }
+
     point[0].x = x0; point[0].y = y0;
     point[1].x = x1; point[1].y = y1;
     point[2].x = x2; point[2].y = y2;
commit 2352b48f9e9b54b4586548df74aaaa28d5308c0b
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 14:21:07 2010 +0200

    path: Move _cairo_path_fixed_add at the end of line_to and curve_to

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 38484f0..36e18ec 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -540,9 +540,6 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	}
     }
 
-    status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
-    if (unlikely (status))
-	return status;
 
     if (path->is_rectilinear) {
 	path->is_rectilinear = path->current_point.x == x ||
@@ -564,7 +561,8 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 	path->has_last_move_point = FALSE;
     }
     _cairo_path_fixed_extents_add (path, &point);
-    return CAIRO_STATUS_SUCCESS;
+
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
 }
 
 cairo_status_t
@@ -599,9 +597,6 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     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;
 
     path->current_point = point[2];
     path->has_current_point = TRUE;
@@ -619,7 +614,7 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t	*path,
     _cairo_path_fixed_extents_add (path, &point[1]);
     _cairo_path_fixed_extents_add (path, &point[2]);
 
-    return CAIRO_STATUS_SUCCESS;
+    return _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
 }
 
 cairo_status_t
commit d6c3451ee24555abd0e9dcb5e5b8e4f85b70b7f0
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 13:43:01 2010 +0200

    box: Add _cairo_box_add_curve_to
    
    Add a function to extend a box with the extents of a curve_to
    operation.

diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
index cb82ea5..3a541eb 100644
--- a/src/cairo-rectangle.c
+++ b/src/cairo-rectangle.c
@@ -262,3 +262,32 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
 
     return FALSE;
 }
+
+static cairo_status_t
+_cairo_box_add_spline_point (void *closure,
+			     const cairo_point_t *point)
+{
+    _cairo_box_add_point (closure, point);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* assumes a has been previously added */
+void
+_cairo_box_add_curve_to (cairo_box_t *extents,
+			 const cairo_point_t *a,
+			 const cairo_point_t *b,
+			 const cairo_point_t *c,
+			 const cairo_point_t *d)
+{
+    _cairo_box_add_point (extents, d);
+    if (!_cairo_box_contains_point (extents, b) ||
+	!_cairo_box_contains_point (extents, c))
+    {
+	cairo_status_t status;
+
+	status = _cairo_spline_bound (_cairo_box_add_spline_point,
+				      extents, a, b, c, d);
+	assert (status == CAIRO_STATUS_SUCCESS);
+    }
+}
diff --git a/src/cairoint.h b/src/cairoint.h
index 6d4da89..4ca6a42 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -263,6 +263,13 @@ _cairo_box_round_to_rectangle (const cairo_box_t     *box,
 			       cairo_rectangle_int_t *rectangle);
 
 cairo_private void
+_cairo_box_add_curve_to (cairo_box_t         *extents,
+			 const cairo_point_t *a,
+			 const cairo_point_t *b,
+			 const cairo_point_t *c,
+			 const cairo_point_t *d);
+
+cairo_private void
 _cairo_boxes_get_extents (const cairo_box_t *boxes,
 			  int num_boxes,
 			  cairo_box_t *extents);
commit 46584e01a8acfb43bb4af1b4e3b89b5cb5ebe246
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 22 13:41:51 2010 +0200

    box: Add box header
    
    Add a new header implementing very simple box functions:
     - initialization with the two extrema
     - extension with a point
     - in/out test

diff --git a/src/cairo-box-private.h b/src/cairo-box-private.h
new file mode 100644
index 0000000..3bced99
--- /dev/null
+++ b/src/cairo-box-private.h
@@ -0,0 +1,75 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2010 Andrea Canciani
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * Contributor(s):
+ *	Andrea Canciani <ranma42 at gmail.com>
+ */
+
+#ifndef CAIRO_BOX_H
+#define CAIRO_BOX_H
+
+#include "cairo-types-private.h"
+#include "cairo-compiler-private.h"
+
+static inline void
+_cairo_box_set (cairo_box_t *box,
+		const cairo_point_t *p1,
+		const cairo_point_t *p2)
+{
+    box->p1 = *p1;
+    box->p2 = *p2;
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline void
+_cairo_box_add_point (cairo_box_t *box,
+		      const cairo_point_t *point)
+{
+    if (point->x < box->p1.x)
+	box->p1.x = point->x;
+    else if (point->x > box->p2.x)
+	box->p2.x = point->x;
+
+    if (point->y < box->p1.y)
+	box->p1.y = point->y;
+    else if (point->y > box->p2.y)
+	box->p2.y = point->y;
+}
+
+/* assumes box->p1 is top-left, p2 bottom-right */
+static inline cairo_bool_t
+_cairo_box_contains_point (cairo_box_t *box,
+			   const cairo_point_t *point)
+{
+    return box->p1.x <= point->x  && point->x <= box->p2.x &&
+	box->p1.y <= point->y  && point->y <= box->p2.y;
+}
+
+#endif /* CAIRO_BOX_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 3ab00be..38484f0 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -38,6 +38,7 @@
 
 #include "cairoint.h"
 
+#include "cairo-box-private.h"
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
 #include "cairo-slope-private.h"
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index a31ac67..cdf22ce 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -39,6 +39,7 @@
 #define _BSD_SOURCE /* for hypot() */
 #include "cairoint.h"
 
+#include "cairo-box-private.h"
 #include "cairo-boxes-private.h"
 #include "cairo-error-private.h"
 #include "cairo-path-fixed-private.h"
diff --git a/src/cairo-rectangle.c b/src/cairo-rectangle.c
index 185c75c..cb82ea5 100644
--- a/src/cairo-rectangle.c
+++ b/src/cairo-rectangle.c
@@ -39,6 +39,8 @@
 
 #include "cairoint.h"
 
+#include "cairo-box-private.h"
+
 cairo_private void
 _cairo_box_from_doubles (cairo_box_t *box,
 			 double *x1, double *y1,
@@ -260,12 +262,3 @@ _cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
 
     return FALSE;
 }
-
-cairo_bool_t
-_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point)
-{
-    if (point->x < box->p1.x || point->x > box->p2.x ||
-	point->y < box->p1.y || point->y > box->p2.y)
-	return FALSE;
-    return TRUE;
-}
diff --git a/src/cairoint.h b/src/cairoint.h
index a0d988c..6d4da89 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -291,10 +291,6 @@ cairo_private cairo_bool_t
 _cairo_box_intersects_line_segment (cairo_box_t *box,
 	                            cairo_line_t *line) cairo_pure;
 
-cairo_private cairo_bool_t
-_cairo_box_contains_point (cairo_box_t *box,
-	                   const cairo_point_t *point) cairo_pure;
-
 /* cairo-array.c structures and functions */
 
 cairo_private void
commit 65d57313f00b3775eb443f0c0069b996b44941d8
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 12:33:40 2010 +0200

    path: Cleanup _cairo_path_fixed_line_to
    
    The low-level line_to optimizations can be implemented in a more
    abstract way using _cairo_path_fixed_penultimate_point and
    _cairo_path_fixed_drop_line_to.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 3a60653..3ab00be 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -514,21 +514,12 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
      * then just change its end-point rather than adding a new op.
      */
     if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
-	cairo_path_buf_t *buf;
 	const cairo_point_t *p;
 
-	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)];
-	}
-
+	p = _cairo_path_fixed_penultimate_point (path);
 	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;
+	    _cairo_path_fixed_drop_line_to (path);
 	} else {
 	    cairo_slope_t prev, self;
 
@@ -538,8 +529,12 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
 		/* cannot trim anti-parallel segments whilst stroking */
 		! _cairo_slope_backwards (&prev, &self))
 	    {
-		buf->points[buf->num_points - 1] = point;
-		goto FLAGS;
+		_cairo_path_fixed_drop_line_to (path);
+		/* In this case the flags might be more restrictive than
+		 * what we actually need.
+		 * When changing the flags definition we should check if
+		 * changing the line_to point can affect them.
+		*/
 	    }
 	}
     }
@@ -548,7 +543,6 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
     if (unlikely (status))
 	return status;
 
-  FLAGS:
     if (path->is_rectilinear) {
 	path->is_rectilinear = path->current_point.x == x ||
 			       path->current_point.y == y;
commit f3e7677109d7ac0b775f2d373796f444cc3bff54
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 11:40:34 2010 +0200

    path: Simplify close_path
    
    Instead of explicitly computing the flag in close_path, manually close
    the path with a line_to, then drop the last operation if it is a
    line_to (it might be another operation if the line_to was ignored
    because it would have been degenerate).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 2e8da1c..3a60653 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -655,27 +655,24 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
     if (! path->has_current_point)
 	return CAIRO_STATUS_SUCCESS;
 
-    /* If the previous op was also a LINE_TO back to the start, discard it */
-    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
-	if (path->current_point.x == path->last_move_point.x &&
-	    path->current_point.y == path->last_move_point.y)
-	{
-	    cairo_path_buf_t *buf;
-	    cairo_point_t *p;
-
-	    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)];
-	    }
+    /*
+     * Add a line_to, to compute flags and solve any degeneracy.
+     * It will be removed later (if it was actually added).
+     */
+    status = _cairo_path_fixed_line_to (path,
+					path->last_move_point.x,
+					path->last_move_point.y);
+    if (unlikely (status))
+	return status;
 
-	    path->current_point = *p;
-	    buf->num_ops--;
-	    buf->num_points--;
-	}
-    }
+    /*
+     * If the command used to close the path is a line_to, drop it.
+     * We must check that last command is actually a line_to,
+     * because the path could have been closed with a curve_to (and
+     * the previous line_to not added as it would be degenerate).
+     */
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO)
+	    _cairo_path_fixed_drop_line_to (path);
 
     status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CLOSE_PATH, NULL, 0);
     if (unlikely (status))
commit 641d314b9a3c670ddade74df99f1443063bd991b
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 11:54:19 2010 +0200

    path: Add utility functions
    
    Add a function to get the penultimate point and another one to drop
    the last operation (assuming it is a line_to).
    
    This allows some more abstraction in the line_to and close_path code.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index dfc1474..2e8da1c 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -374,6 +374,34 @@ _cairo_path_fixed_last_op (cairo_path_fixed_t *path)
     return buf->op[buf->num_ops - 1];
 }
 
+static inline const cairo_point_t *
+_cairo_path_fixed_penultimate_point (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    buf = cairo_path_tail (path);
+    if (likely (buf->num_points >= 2)) {
+	return &buf->points[buf->num_points - 2];
+    } else {
+	cairo_path_buf_t *prev_buf = cairo_path_buf_prev (buf);
+
+	assert (prev_buf->num_points >= 2 - buf->num_points);
+	return &prev_buf->points[prev_buf->num_points - (2 - buf->num_points)];
+    }
+}
+
+static inline void
+_cairo_path_fixed_drop_line_to (cairo_path_fixed_t *path)
+{
+    cairo_path_buf_t *buf;
+
+    assert (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO);
+
+    buf = cairo_path_tail (path);
+    buf->num_points--;
+    buf->num_ops--;
+}
+
 static inline void
 _cairo_path_fixed_extents_add (cairo_path_fixed_t *path,
 			       const cairo_point_t *point)
commit 4075ed9686483defa9fb1cffca6509f079f9a91d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 11:53:58 2010 +0200

    path: Rename _cairo_path_last_op to _cairo_path_fixed_last_op
    
    Aestetical change, to make the naming consistent with that of the
    other functions.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index a0e04ac..dfc1474 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -362,8 +362,8 @@ _cairo_path_fixed_destroy (cairo_path_fixed_t *path)
     free (path);
 }
 
-static cairo_path_op_t
-_cairo_path_last_op (cairo_path_fixed_t *path)
+static inline cairo_path_op_t
+_cairo_path_fixed_last_op (cairo_path_fixed_t *path)
 {
     cairo_path_buf_t *buf;
 
@@ -402,7 +402,7 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t  *path,
 
     /* If the previous op was also a MOVE_TO, then just change its
      * point rather than adding a new op. */
-    if (_cairo_path_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_MOVE_TO) {
 	cairo_path_buf_t *buf;
 
 	buf = cairo_path_tail (path);
@@ -477,7 +477,7 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
      * a move-to followed by a degenerate line-to is a valid path for
      * stroking, but at all other times is simply a degenerate segment.
      */
-    if (_cairo_path_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
+    if (_cairo_path_fixed_last_op (path) != CAIRO_PATH_OP_MOVE_TO) {
 	if (x == path->current_point.x && y == path->current_point.y)
 	    return CAIRO_STATUS_SUCCESS;
     }
@@ -485,7 +485,7 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
     /* 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 (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
 	cairo_path_buf_t *buf;
 	const cairo_point_t *p;
 
@@ -628,7 +628,7 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
 	return CAIRO_STATUS_SUCCESS;
 
     /* If the previous op was also a LINE_TO back to the start, discard it */
-    if (_cairo_path_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
+    if (_cairo_path_fixed_last_op (path) == CAIRO_PATH_OP_LINE_TO) {
 	if (path->current_point.x == path->last_move_point.x &&
 	    path->current_point.y == path->last_move_point.y)
 	{
commit a8763d8fdeeb16323b8641e168475f77d73908a3
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 11:38:23 2010 +0200

    path: Make path equality independent of flags
    
    Flags for the same path can be different depending on its "history"
    (in particular if it was constructed and transformed they might be
    different from what they would be if each point was transformed and
    then the path constructed).

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 96dd0eb..a0e04ac 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -233,10 +233,7 @@ _cairo_path_fixed_equal (const cairo_path_fixed_t *a,
 	return TRUE;
 
     /* use the flags to quickly differentiate based on contents */
-    if (a->is_empty_fill != b->is_empty_fill ||
-	a->has_curve_to != b->has_curve_to ||
-	a->maybe_fill_region != b->maybe_fill_region ||
-	a->is_rectilinear != b->is_rectilinear)
+    if (a->has_curve_to != b->has_curve_to)
     {
 	return FALSE;
     }
commit f4b2ce1c78c05c0a551aab7c84451c7ee1759213
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Oct 17 10:55:15 2010 +0200

    path: Improve hashing
    
    Make the hash independent of buf bucketing, extents and flags.
    
    This makes the hash depend only on the actual content of the path, not
    on how it is stored or on any computed property.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index b6495e1..96dd0eb 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -182,23 +182,23 @@ _cairo_path_fixed_hash (const cairo_path_fixed_t *path)
 {
     unsigned long hash = _CAIRO_HASH_INIT_VALUE;
     const cairo_path_buf_t *buf;
-    int num_points, num_ops;
-
-    hash = _cairo_hash_bytes (hash, &path->extents, sizeof (path->extents));
+    unsigned int count;
 
-    num_ops = num_points = 0;
+    count = 0;
     cairo_path_foreach_buf_start (buf, path) {
 	hash = _cairo_hash_bytes (hash, buf->op,
 			          buf->num_ops * sizeof (buf->op[0]));
+	count += buf->num_ops;
+    } cairo_path_foreach_buf_end (buf, path);
+    hash = _cairo_hash_bytes (hash, &count, sizeof (count));
+
+    count = 0;
+    cairo_path_foreach_buf_start (buf, path) {
 	hash = _cairo_hash_bytes (hash, buf->points,
 			          buf->num_points * sizeof (buf->points[0]));
-
-	num_ops    += buf->num_ops;
-	num_points += buf->num_points;
+	count += buf->num_points;
     } cairo_path_foreach_buf_end (buf, path);
-
-    hash = _cairo_hash_bytes (hash, &num_ops, sizeof (num_ops));
-    hash = _cairo_hash_bytes (hash, &num_points, sizeof (num_points));
+    hash = _cairo_hash_bytes (hash, &count, sizeof (count));
 
     return hash;
 }
commit e9c1fc31887c5bfbb7d086f923a7628b7cfa739c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Oct 16 23:57:06 2010 +0200

    path: Do not access flags directly
    
    Use inline accessors to hide the flags in the code.
    
    This ensures that flags that need additional computations (example:
    is_rectilinear for the fill case) are always used correctly.

diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index d5a2fab..5b5d66e 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -229,7 +229,7 @@ _cairo_clip_intersect_path (cairo_clip_t       *clip,
 
     if (clip->path != NULL) {
 	if (clip->path->fill_rule == fill_rule &&
-	    (path->is_rectilinear || tolerance == clip->path->tolerance) &&
+	    (_cairo_path_fixed_fill_is_rectilinear (path) || tolerance == clip->path->tolerance) &&
 	    antialias == clip->path->antialias &&
 	    _cairo_path_fixed_equal (&clip->path->path, path))
 	{
@@ -553,7 +553,7 @@ static inline cairo_bool_t
 _clip_paths_are_rectilinear (cairo_clip_path_t *clip_path)
 {
     while (clip_path != NULL) {
-	if (! clip_path->path.is_rectilinear)
+	if (! _cairo_path_fixed_fill_is_rectilinear (&clip_path->path))
 	    return FALSE;
 
 	clip_path = clip_path->prev;
@@ -578,7 +578,7 @@ _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path)
 	goto UNSUPPORTED;
 
     /* Start simple... Intersect some boxes with an arbitrary path. */
-    if (! clip_path->path.is_rectilinear)
+    if (! _cairo_path_fixed_fill_is_rectilinear (&clip_path->path))
 	goto UNSUPPORTED;
     if (clip_path->prev->prev != NULL)
 	goto UNSUPPORTED;
@@ -651,7 +651,7 @@ _cairo_clip_path_to_region (cairo_clip_path_t *clip_path)
 	    CAIRO_STATUS_SUCCESS;
     }
 
-    if (! clip_path->path.maybe_fill_region)
+    if (! _cairo_path_fixed_fill_maybe_region (&clip_path->path))
 	return _cairo_clip_path_to_region_geometric (clip_path);
 
     /* first retrieve the region for our antecedents */
@@ -961,7 +961,7 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 
     while (clip_path->prev != NULL &&
 	   clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
-	   clip_path->path.maybe_fill_region)
+	   _cairo_path_fixed_fill_maybe_region (&clip_path->path))
     {
 	clip_path = clip_path->prev;
     }
@@ -989,7 +989,7 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
 
     need_translate = clip_extents->x | clip_extents->y;
     if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
-	clip_path->path.maybe_fill_region)
+	_cairo_path_fixed_fill_maybe_region (&clip_path->path))
     {
 	status = _cairo_surface_paint (surface,
 				       CAIRO_OPERATOR_SOURCE,
@@ -1033,11 +1033,11 @@ _cairo_clip_path_get_surface (cairo_clip_path_t *clip_path,
     prev = clip_path->prev;
     while (prev != NULL) {
 	if (prev->flags & CAIRO_CLIP_PATH_IS_BOX &&
-	    prev->path.maybe_fill_region)
+	    _cairo_path_fixed_fill_maybe_region (&prev->path))
 	{
 	    /* a simple box only affects the extents */
 	}
-	else if (prev->path.is_rectilinear ||
+	else if (_cairo_path_fixed_fill_is_rectilinear (&prev->path) ||
 		prev->surface == NULL ||
 		prev->surface->backend != target->backend)
 	{
@@ -1233,7 +1233,7 @@ _cairo_clip_combine_with_surface (cairo_clip_t *clip,
 	}
 
 	if (clip_path->flags & CAIRO_CLIP_PATH_IS_BOX &&
-	    clip_path->path.maybe_fill_region)
+	    _cairo_path_fixed_fill_maybe_region (&clip_path->path))
 	{
 	    continue;
 	}
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index b1d09d2..7c54dcb 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -1454,7 +1454,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t     *gstate,
     cairo_status_t status;
     cairo_traps_t traps;
 
-    if (path->is_empty_fill) {
+    if (_cairo_path_fixed_fill_is_empty (path)) {
 	if (x1)
 	    *x1 = 0.0;
 	if (y1)
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 2d769ea..a84aaab 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -3629,7 +3629,7 @@ _cairo_image_surface_stroke (void			*abstract_surface,
     }
 
     status = CAIRO_INT_STATUS_UNSUPPORTED;
-    if (path->is_rectilinear) {
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
 	cairo_boxes_t boxes;
 
 	_cairo_boxes_init (&boxes);
@@ -3729,7 +3729,7 @@ _cairo_image_surface_fill (void				*abstract_surface,
 	return status;
     }
 
-    if (_cairo_path_fixed_is_rectilinear_fill (path)) {
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
 	cairo_boxes_t boxes;
 
 	_cairo_boxes_init (&boxes);
@@ -3748,7 +3748,7 @@ _cairo_image_surface_fill (void				*abstract_surface,
     } else {
 	cairo_polygon_t polygon;
 
-	assert (! path->is_empty_fill);
+	assert (! _cairo_path_fixed_fill_is_empty (path));
 
 	_cairo_polygon_init (&polygon);
 	_cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index 24aaa39..358c8b9 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -139,7 +139,7 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     cairo_polygon_t polygon;
     cairo_status_t status;
 
-    if (path->is_empty_fill)
+    if (_cairo_path_fixed_fill_is_empty (path))
 	return CAIRO_STATUS_SUCCESS;
 
     _cairo_polygon_init (&polygon);
@@ -152,7 +152,7 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
     if (unlikely (status || polygon.num_edges == 0))
 	goto CLEANUP;
 
-    if (path->is_rectilinear) {
+    if (_cairo_path_fixed_fill_is_rectilinear (path)) {
 	status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (traps,
 									&polygon,
 									fill_rule);
@@ -242,8 +242,8 @@ _cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t	*path,
     cairo_box_t box;
     cairo_region_t *region = NULL;
 
-    assert (path->maybe_fill_region);
-    assert (! path->is_empty_fill);
+    assert (_cairo_path_fixed_fill_maybe_region (path));
+    assert (! _cairo_path_fixed_fill_is_empty (path));
 
     if (_cairo_path_fixed_is_box (path, &box)) {
 	rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index 42e64ed..5d665db 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -139,7 +139,7 @@ _cairo_path_fixed_fill_is_empty (const cairo_path_fixed_t *path)
 }
 
 static inline cairo_bool_t
-_cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path)
+_cairo_path_fixed_fill_is_rectilinear (const cairo_path_fixed_t *path)
 {
     if (! path->is_rectilinear)
 	return 0;
@@ -153,13 +153,25 @@ _cairo_path_fixed_is_rectilinear_fill (const cairo_path_fixed_t *path)
 }
 
 static inline cairo_bool_t
-_cairo_path_fixed_maybe_fill_region (const cairo_path_fixed_t *path)
+_cairo_path_fixed_stroke_is_rectilinear (const cairo_path_fixed_t *path)
 {
-#if WATCH_PATH
-    fprintf (stderr, "_cairo_path_fixed_maybe_fill_region () = %s\n",
-	     path->maybe_fill_region ? "true" : "false");
-#endif
-    return path->maybe_fill_region;
+    return path->is_rectilinear;
+}
+
+static inline cairo_bool_t
+_cairo_path_fixed_fill_maybe_region (const cairo_path_fixed_t *path)
+{
+    if (! path->maybe_fill_region)
+	return FALSE;
+
+    if (! path->has_current_point)
+	return TRUE;
+
+    /* check whether the implicit close preserves the rectilinear property
+     * (the integer point property is automatically preserved)
+     */
+    return path->current_point.x == path->last_move_point.x ||
+	   path->current_point.y == path->last_move_point.y;
 }
 
 #endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-in-fill.c b/src/cairo-path-in-fill.c
index b344f52..a19784c 100644
--- a/src/cairo-path-in-fill.c
+++ b/src/cairo-path-in-fill.c
@@ -254,7 +254,7 @@ _cairo_path_fixed_in_fill (const cairo_path_fixed_t	*path,
     cairo_status_t status;
     cairo_bool_t is_inside;
 
-    if (path->is_empty_fill)
+    if (_cairo_path_fixed_fill_is_empty (path))
 	return FALSE;
 
     _cairo_in_fill_init (&in_fill, tolerance, x, y);
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 505b6ab..a31ac67 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -1399,7 +1399,7 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t	*path,
      * device-pixel boundaries when possible. Many backends can render
      * those much faster than non-aligned trapezoids, (by using clip
      * regions, etc.) */
-    if (path->is_rectilinear) {
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
 	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
 								stroke_style,
 								ctm,
@@ -2037,7 +2037,7 @@ _cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t	*path,
     cairo_rectilinear_stroker_t rectilinear_stroker;
     cairo_int_status_t status;
 
-    assert (path->is_rectilinear);
+    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
 
     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
 					   stroke_style, ctm,
@@ -2091,7 +2091,7 @@ _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t	*path,
     cairo_rectilinear_stroker_t rectilinear_stroker;
     cairo_int_status_t status;
 
-    assert (path->is_rectilinear);
+    assert (_cairo_path_fixed_stroke_is_rectilinear (path));
 
     if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
 					   stroke_style, ctm,
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 99ea07e..61944c0 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1073,7 +1073,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t		*surface,
     _cairo_traps_init (&traps);
     _cairo_traps_limit (&traps, clip_boxes, num_boxes);
 
-    if (path->is_rectilinear) {
+    if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
 	status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
 								stroke_style,
 								ctm,
@@ -1166,10 +1166,10 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
     _cairo_polygon_init (&polygon);
     _cairo_polygon_limit (&polygon, clip_boxes, num_boxes);
 
-    if (path->is_empty_fill)
+    if (_cairo_path_fixed_fill_is_empty (path))
 	goto DO_TRAPS;
 
-    is_rectilinear = _cairo_path_fixed_is_rectilinear_fill (path);
+    is_rectilinear = _cairo_path_fixed_fill_is_rectilinear (path);
     if (is_rectilinear) {
 	status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
 							      fill_rule,
commit 14cc9846b302a990e65d7572e7f095a8873a213d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Oct 16 23:08:03 2010 +0200

    path: Replace _cairo_path_fixed_is_equal with _cairo_path_fixed_equal
    
    Remove _cairo_path_fixed_is_equal and use _cairo_path_fixed_equal
    instead.
    The latter function can recognize that two paths are equal even if the
    drawing commands have been partitioned in a different way in the buf
    list.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index eea8630..b6495e1 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -1044,41 +1044,6 @@ _cairo_path_fixed_transform (cairo_path_fixed_t	*path,
     } cairo_path_foreach_buf_end (buf, path);
 }
 
-cairo_bool_t
-_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
-			    const cairo_path_fixed_t *other)
-{
-    const cairo_path_buf_t *path_buf, *other_buf;
-
-    if (path->current_point.x != other->current_point.x ||
-	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_rectilinear != other->is_rectilinear ||
-	path->maybe_fill_region != other->maybe_fill_region ||
-	path->last_move_point.x != other->last_move_point.x ||
-	path->last_move_point.y != other->last_move_point.y)
-    {
-	return FALSE;
-    }
-
-    other_buf = cairo_path_head (other);
-    cairo_path_foreach_buf_start (path_buf, path) {
-	if (path_buf->num_ops != other_buf->num_ops ||
-	    path_buf->num_points != other_buf->num_points ||
-	    memcmp (path_buf->op, other_buf->op,
-		    sizeof (cairo_path_op_t) * path_buf->num_ops) != 0 ||
-	    memcmp (path_buf->points, other_buf->points,
-		    sizeof (cairo_point_t) * path_buf->num_points) != 0)
-	{
-	    return FALSE;
-	}
-	other_buf = cairo_path_buf_next (other_buf);
-    } cairo_path_foreach_buf_end (path_buf, path);
-
-    return TRUE;
-}
-
 /* Closure for path flattening */
 typedef struct cairo_path_flattener {
     double tolerance;
diff --git a/src/cairo-recording-surface.c b/src/cairo-recording-surface.c
index 79d51e8..0ac938b 100644
--- a/src/cairo-recording-surface.c
+++ b/src/cairo-recording-surface.c
@@ -898,8 +898,8 @@ _cairo_recording_surface_replay_internal (cairo_surface_t	     *surface,
 
 	    if (stroke_command != NULL &&
 		stroke_command->header.type == CAIRO_COMMAND_STROKE &&
-		_cairo_path_fixed_is_equal (&command->fill.path,
-					    &stroke_command->stroke.path))
+		_cairo_path_fixed_equal (&command->fill.path,
+					 &stroke_command->stroke.path))
 	    {
 		status = _cairo_surface_wrapper_fill_stroke (&wrapper,
 							     command->header.op,
diff --git a/src/cairoint.h b/src/cairoint.h
index 346fc51..a0d988c 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1130,10 +1130,6 @@ cairo_private cairo_status_t
 _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
 			     const cairo_path_fixed_t *other);
 
-cairo_private cairo_bool_t
-_cairo_path_fixed_is_equal (const cairo_path_fixed_t *path,
-			    const cairo_path_fixed_t *other);
-
 cairo_private void
 _cairo_path_fixed_fini (cairo_path_fixed_t *path);
 
commit ac7b2a972097f4080ab6e5a29974c830b8b57a4f
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Oct 15 21:51:12 2010 +0200

    test: Fix get-path-extents
    
    The test was considering all the empty rects equals, but this is
    not correct when testing the results of cairo_path_extents().

diff --git a/test/get-path-extents.c b/test/get-path-extents.c
index a3e9554..7a09317 100644
--- a/test/get-path-extents.c
+++ b/test/get-path-extents.c
@@ -61,10 +61,6 @@ check_extents (const cairo_test_context_t *ctx,
     if (cairo_status (cr))
 	return 1;
 
-    /* let empty rects match */
-    if ((ext_x1 == ext_x2 || ext_y1 == ext_y2) && (width == 0 || height == 0))
-        return 1;
-
     switch (relation) {
     default:
     case EQUALS:
@@ -159,7 +155,7 @@ draw (cairo_t *cr, int width, int height)
     phase = "Degenerate arc (Θ=0)";
     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, 0, 0, 0, 0);
-    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 200, 400, 0, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 210, 400, 0, 0);
 
     cairo_new_path (cr2);
     cairo_restore (cr2);
@@ -199,7 +195,7 @@ draw (cairo_t *cr, int width, int height)
     cairo_line_to (cr2, 750, 180);
     errors += !check_extents (ctx, phase, cr2, FILL, EQUALS, 0, 0, 0, 0);
     errors += !check_extents (ctx, phase, cr2, STROKE, EQUALS, -5, 175, 760, 10);
-    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 180, 755, 0);
+    errors += !check_extents (ctx, phase, cr2, PATH, EQUALS, 0, 180, 750, 0);
     cairo_new_path (cr2);
     cairo_restore (cr2);
 


More information about the cairo-commit mailing list