[cairo-commit] 4 commits - src/cairo-polygon.c test/horizontal-clip.c test/horizontal-clip.ref.png test/Makefile.refs

Andrea Canciani ranma42 at kemper.freedesktop.org
Thu Jul 28 09:11:47 PDT 2011


 src/cairo-polygon.c          |  205 +++++++++++++++++--------------------------
 test/Makefile.refs           |    4 
 test/horizontal-clip.c       |   39 ++++----
 test/horizontal-clip.ref.png |binary
 4 files changed, 107 insertions(+), 141 deletions(-)

New commits:
commit d7abdab931c1219314c07c0ecf21b0afef19108a
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Jul 27 18:58:03 2011 +0200

    polygon: Fix generic case of edge clipping
    
    The edge clipper is more complicated than it should be and contains a
    subtle bug: when an edge is almost horizontal, it is always considered
    as having a positive slope.
    
    Explain what should be done and do it in a simpler way.
    
    Fixes horizontal-clip.

diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index a20e6ba..74ec9fa 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -333,97 +333,72 @@ _add_clipped_edge (cairo_polygon_t *polygon,
 	} else {
 	    /* The edge and the box intersect in a generic way */
 	    cairo_fixed_t left_y, right_y;
-	    int p1_y, p2_y;
-	    cairo_point_t p[2];
+	    cairo_bool_t top_left_to_bottom_right;
 
 	    left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
 							       limits->p1.x);
 	    right_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
 								limits->p2.x);
 
-	    p1_y = top;
-	    p2_y = bottom;
-
-	    if (left_y < right_y) {
-		if (p1->x < limits->p1.x && left_y > top) {
-		    p[0].x = limits->p1.x;
-		    p[0].y = limits->p1.y;
-		    top_y = p1_y;
-		    if (top_y < p[0].y)
-			top_y = p[0].y;
-
-		    p[1].x = limits->p1.x;
-		    p[1].y = limits->p2.y;
-		    bot_y = left_y;
-		    if (bot_y > p[1].y)
-			bot_y = p[1].y;
-
-		    if (bot_y > top_y)
-			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		    p1_y = bot_y;
+	    /*
+	     * The edge intersects the lines corresponding to the left
+	     * and right sides of the limit box at left_y and right_y,
+	     * but we need to add edges for the range from top_y to
+	     * bot_y.
+	     *
+	     * For both intersections, there are three cases:
+	     *
+	     *  1) It is outside the vertical range of the limit
+	     *     box. In this case we can simply further clip the
+	     *     edge we will be emitting (i.e. restrict its
+	     *     top/bottom limits to those of the limit box).
+	     *
+	     *  2) It is inside the vertical range of the limit
+	     *     box. In this case, we need to add the vertical edge
+	     *     connecting the correct vertex to the intersection,
+	     *     in order to preserve the winding count.
+	     *
+	     *  3) It is exactly on the box. In this case, do nothing.
+	     *
+	     * These operations restrict the active range (stored in
+	     * top_y/bot_y) so that the p1-p2 edge is completely
+	     * inside the box if it is clipped to this vertical range.
+	     */
+
+	    top_left_to_bottom_right = (p1->x < p2->x) == (p1->y < p2->y);
+
+	    if (top_left_to_bottom_right) {
+		left_y = MIN (left_y, bot_y);
+		if (top_y < left_y) {
+		    _add_edge (polygon, &limits->p1, &bot_left,
+			       top_y, left_y, dir);
+		    top_y = left_y;
 		}
 
-		if (p2->x > limits->p2.x && right_y < bottom) {
-		    p[0].x = limits->p2.x;
-		    p[0].y = limits->p1.y;
-		    top_y = right_y;
-		    if (top_y < p[0].y)
-			top_y = p[0].y;
-
-		    p[1].x = limits->p2.x;
-		    p[1].y = limits->p2.y;
-		    bot_y = p2_y;
-		    if (bot_y > p[1].y)
-			bot_y = p[1].y;
-
-		    if (bot_y > top_y)
-			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		    p2_y = top_y;
+		right_y = MAX (right_y, top_y);
+		if (bot_y > right_y) {
+		    _add_edge (polygon, &top_right, &limits->p2,
+			       right_y, bot_y, dir);
+		    bot_y = right_y;
 		}
 	    } else {
-		if (p1->x > limits->p2.x && right_y > top) {
-		    p[0].x = limits->p2.x;
-		    p[0].y = limits->p1.y;
-		    top_y = p1_y;
-		    if (top_y < p[0].y)
-			top_y = p[0].y;
-
-		    p[1].x = limits->p2.x;
-		    p[1].y = limits->p2.y;
-		    bot_y = right_y;
-		    if (bot_y > p[1].y)
-			bot_y = p[1].y;
-
-		    if (bot_y > top_y)
-			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		    p1_y = bot_y;
+		right_y = MIN (right_y, bot_y);
+		if (top_y < right_y) {
+		    _add_edge (polygon, &top_right, &limits->p2,
+			       top_y, right_y, dir);
+		    top_y = right_y;
 		}
 
-		if (p2->x < limits->p1.x && left_y < bottom) {
-		    p[0].x = limits->p1.x;
-		    p[0].y = limits->p1.y;
-		    top_y = left_y;
-		    if (top_y < p[0].y)
-			top_y = p[0].y;
-
-		    p[1].x = limits->p1.x;
-		    p[1].y = limits->p2.y;
-		    bot_y = p2_y;
-		    if (bot_y > p[1].y)
-			bot_y = p[1].y;
-
-		    if (bot_y > top_y)
-			_add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-		    p2_y = top_y;
+		left_y = MAX (left_y, top_y);
+		if (bot_y > left_y) {
+		    _add_edge (polygon, &limits->p1, &bot_left,
+			       left_y, bot_y, dir);
+		    bot_y = left_y;
 		}
 	    }
 
-	    if (p1_y < limits->p1.y)
-		p1_y = limits->p1.y;
-	    if (p2_y > limits->p2.y)
-		p2_y = limits->p2.y;
-	    if (p2_y > p1_y)
-		_add_edge (polygon, p1, p2, p1_y, p2_y, dir);
+	    if (top_y != bot_y)
+		_add_edge (polygon, p1, p2, top_y, bot_y, dir);
 	}
     }
 }
commit 6aa6b7daa8277785f431a07fe3d8d492d62df946
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Jul 27 18:57:59 2011 +0200

    polygon: Simplify code for edge clipping
    
    Add some comments about what's going on and clean up the branches by
    sharing common computations.

diff --git a/src/cairo-polygon.c b/src/cairo-polygon.c
index 82a5819..a20e6ba 100644
--- a/src/cairo-polygon.c
+++ b/src/cairo-polygon.c
@@ -288,67 +288,53 @@ _add_clipped_edge (cairo_polygon_t *polygon,
 		   const int top, const int bottom,
 		   const int dir)
 {
-    cairo_point_t p[2];
-    int top_y, bot_y;
+    cairo_point_t bot_left, top_right;
+    cairo_fixed_t top_y, bot_y;
     int n;
 
     for (n = 0; n < polygon->num_limits; n++) {
 	const cairo_box_t *limits = &polygon->limits[n];
+	cairo_fixed_t pleft, pright;
 
 	if (top >= limits->p2.y)
 	    continue;
 	if (bottom <= limits->p1.y)
 	    continue;
 
-	if (p1->x >= limits->p1.x && p2->x >= limits->p1.x &&
-	    p1->x <= limits->p2.x && p2->x <= limits->p2.x)
-	{
-	    top_y = top;
-	    if (top_y < limits->p1.y)
-		top_y = limits->p1.y;
+	bot_left.x = limits->p1.x;
+	bot_left.y = limits->p2.y;
 
-	    bot_y = bottom;
-	    if (bot_y > limits->p2.y)
-		bot_y = limits->p2.y;
+	top_right.x = limits->p2.x;
+	top_right.y = limits->p1.y;
+
+	/* The useful region */
+	top_y = MAX (top, limits->p1.y);
+	bot_y = MIN (bottom, limits->p2.y);
+
+	/* The projection of the edge on the horizontal axis */
+	pleft = MIN (p1->x, p2->x);
+	pright = MAX (p1->x, p2->x);
+
+	if (limits->p1.x <= pleft && pright <= limits->p2.x) {
+	    /* Projection of the edge completely contained in the box:
+	     * clip vertically by restricting top and bottom */
 
 	    _add_edge (polygon, p1, p2, top_y, bot_y, dir);
-	}
-	else if (p1->x <= limits->p1.x && p2->x <= limits->p1.x)
-	{
-	    p[0].x = limits->p1.x;
-	    p[0].y = limits->p1.y;
-	    top_y = top;
-	    if (top_y < p[0].y)
-		top_y = p[0].y;
-
-	    p[1].x = limits->p1.x;
-	    p[1].y = limits->p2.y;
-	    bot_y = bottom;
-	    if (bot_y > p[1].y)
-		bot_y = p[1].y;
-
-	    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-	}
-	else if (p1->x >= limits->p2.x && p2->x >= limits->p2.x)
-	{
-	    p[0].x = limits->p2.x;
-	    p[0].y = limits->p1.y;
-	    top_y = top;
-	    if (top_y < p[0].y)
-		top_y = p[0].y;
-
-	    p[1].x = limits->p2.x;
-	    p[1].y = limits->p2.y;
-	    bot_y = bottom;
-	    if (bot_y > p[1].y)
-		bot_y = p[1].y;
-
-	    _add_edge (polygon, &p[0], &p[1], top_y, bot_y, dir);
-	}
-	else
-	{
-	    int left_y, right_y;
+	} else if (pright <= limits->p1.x) {
+	    /* Projection of the edge to the left of the box:
+	     * replace with the left side of the box (clipped top/bottom) */
+
+	    _add_edge (polygon, &limits->p1, &bot_left, top_y, bot_y, dir);
+	} else if (limits->p2.x <= pleft) {
+	    /* Projection of the edge to the right of the box:
+	     * replace with the right side of the box (clipped top/bottom) */
+
+	    _add_edge (polygon, &top_right, &limits->p2, top_y, bot_y, dir);
+	} else {
+	    /* The edge and the box intersect in a generic way */
+	    cairo_fixed_t left_y, right_y;
 	    int p1_y, p2_y;
+	    cairo_point_t p[2];
 
 	    left_y = _cairo_edge_compute_intersection_y_for_x (p1, p2,
 							       limits->p1.x);
commit de25fec06bbdbb3cf458aefb4e3b9d2271022b9c
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Jul 27 21:50:45 2011 +0200

    test: Improve horizontal-clip
    
    The bug pointed out by this test now causes failures in xcb and xlib.

diff --git a/test/horizontal-clip.c b/test/horizontal-clip.c
index 2a5025c..93127a7 100644
--- a/test/horizontal-clip.c
+++ b/test/horizontal-clip.c
@@ -29,10 +29,11 @@
 
 #include "cairo-test.h"
 
-#define WIDTH 20
-#define HEIGHT 20
+#define WIDTH 16
+#define HEIGHT 26
 
-#define BUG 1
+#define BUGY 1
+#define BUGX (4 * BUGY * WIDTH * 256)
 
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
@@ -41,28 +42,28 @@ draw (cairo_t *cr, int width, int height)
     cairo_paint (cr);
     cairo_set_source_rgb (cr, 0, 0, 0);
 
-    cairo_move_to (cr, 0-256, 4 - BUG);
-    cairo_line_to (cr, WIDTH + 256, 4 + BUG);
-    cairo_line_to (cr, WIDTH + 256, 2 - BUG);
-    cairo_line_to (cr, 0-256, 2 + BUG);
+    cairo_move_to (cr,       - BUGX, 6 - BUGY);
+    cairo_line_to (cr,       + BUGX, 6 + BUGY);
+    cairo_line_to (cr, WIDTH + BUGX, 2 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 2 + BUGY);
     cairo_fill (cr);
 
-    cairo_move_to (cr, 0-256, 6 - BUG);
-    cairo_line_to (cr, WIDTH + 256, 6 + BUG);
-    cairo_line_to (cr, WIDTH + 256, 8 - BUG);
-    cairo_line_to (cr, 0-256, 8 + BUG);
+    cairo_move_to (cr, WIDTH + BUGX, 8  - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 8  + BUGY);
+    cairo_line_to (cr,       - BUGX, 12 - BUGY);
+    cairo_line_to (cr,       + BUGX, 12 + BUGY);
     cairo_fill (cr);
 
-    cairo_move_to (cr, WIDTH+256, 12 - BUG);
-    cairo_line_to (cr, 0-256, 12 + BUG);
-    cairo_line_to (cr, 0-256, 14 - BUG);
-    cairo_line_to (cr, WIDTH+256, 14 + BUG);
+    cairo_move_to (cr,       - BUGX, 14 - BUGY);
+    cairo_line_to (cr,       + BUGX, 14 + BUGY);
+    cairo_line_to (cr, WIDTH + BUGX, 18 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 18 + BUGY);
     cairo_fill (cr);
 
-    cairo_move_to (cr, WIDTH+256, 18 - BUG);
-    cairo_line_to (cr, 0-256, 18 + BUG);
-    cairo_line_to (cr, 0-256, 16 - BUG);
-    cairo_line_to (cr, WIDTH+256, 16 + BUG);
+    cairo_move_to (cr, WIDTH + BUGX, 24 - BUGY);
+    cairo_line_to (cr, WIDTH - BUGX, 24 + BUGY);
+    cairo_line_to (cr,       - BUGX, 20 - BUGY);
+    cairo_line_to (cr,       + BUGX, 20 + BUGY);
     cairo_fill (cr);
 
     return CAIRO_TEST_SUCCESS;
diff --git a/test/horizontal-clip.ref.png b/test/horizontal-clip.ref.png
index 51d9445..f07e035 100644
Binary files a/test/horizontal-clip.ref.png and b/test/horizontal-clip.ref.png differ
commit 80b7a1aa9b600aa3cc746d662b4090e76838f17a
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Jul 27 22:13:44 2011 +0200

    test: Update image references
    
    Makefile.refs was missing some images.

diff --git a/test/Makefile.refs b/test/Makefile.refs
index 1c05dea..44d8679 100644
--- a/test/Makefile.refs
+++ b/test/Makefile.refs
@@ -616,6 +616,7 @@ REFERENCE_IMAGES = \
 	halo.xlib.ref.png \
 	hatchings.ref.png \
 	hatchings.xlib.ref.png \
+	horizontal-clip.ref.png \
 	huge-linear.image16.ref.png \
 	huge-linear.pdf.ref.png \
 	huge-linear.ps3.ref.png \
@@ -715,6 +716,9 @@ REFERENCE_IMAGES = \
 	long-dashed-lines.ps3.ref.png \
 	long-dashed-lines.quartz.ref.png \
 	long-dashed-lines.ref.png \
+	map-all-to-image.ref.png \
+	map-bit-to-image.ref.png \
+	map-to-image-fill.ref.png \
 	mask-alpha.image16.ref.png \
 	mask-alpha.quartz.argb32.ref.png \
 	mask-alpha.ref.png \


More information about the cairo-commit mailing list