[cairo-commit] 3 commits - src/cairo-path-stroke-polygon.c test/bug-spline.c test/Makefile.sources test/reference

Chris Wilson ickle at kemper.freedesktop.org
Sun Jun 24 04:05:48 PDT 2012


 src/cairo-path-stroke-polygon.c   |   31 +++++++++---
 test/Makefile.sources             |    1 
 test/bug-spline.c                 |   96 ++++++++++++++++++++++++++++++++++++++
 test/reference/bug-spline.ref.png |binary
 4 files changed, 120 insertions(+), 8 deletions(-)

New commits:
commit 166e6f199e909d8aea13cdd4c858d48faad26247
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Jun 24 11:33:47 2012 +0100

    stroke: Skip inserting a round-join if within tolerance
    
    If the angle between two segments is small we can simply replace the
    round-join with a bevel-join. This is done automatically by the
    insertion of the triangle fan as it will not be able to find a point
    around the pen between the two vectors. However, we can make that search
    cheaper by inspecting whether the bisection angle is small enough that
    the bevel-join will be within geometric tolerance of the round-join.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index e8e7bb2..b7c18b7 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -424,11 +424,15 @@ outer_close (struct stroker *stroker,
     switch (stroker->style.line_join) {
     case CAIRO_LINE_JOIN_ROUND:
 	/* construct a fan around the common midpoint */
-	add_fan (stroker,
-		 &in->dev_vector,
-		 &out->dev_vector,
-		 &in->point, inpt, outpt,
-		 clockwise, outer);
+	if ((in->dev_slope.x * out->dev_slope.x +
+	     in->dev_slope.y * out->dev_slope.y) < stroker->spline_cusp_tolerance)
+	{
+	    add_fan (stroker,
+		     &in->dev_vector,
+		     &out->dev_vector,
+		     &in->point, inpt, outpt,
+		     clockwise, outer);
+	}
 	break;
 
     case CAIRO_LINE_JOIN_MITER:
commit b7bd5ae4f3da44131261711bb236cd7aa24a3ae3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Jun 24 11:27:16 2012 +0100

    stroke: Use round-joins near inflection points of splines
    
    Near an inflection, the angle between two segments of a spline increases
    rapidly (as the radius of curvature decreases for the cusp). The angle
    may increase so much that a simple line connecting the two outside
    points of the spline is not within the user specified geometric
    tolerance (with the result that you can generate severe ugliness around
    a cusp). Extend the current detection of the exact inflection to cover
    the sharp joins near the cusp by inspecting whether the bisection angle
    is larger than acceptable.
    
    Fixes bug-spline.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-path-stroke-polygon.c b/src/cairo-path-stroke-polygon.c
index 88527f5..e8e7bb2 100644
--- a/src/cairo-path-stroke-polygon.c
+++ b/src/cairo-path-stroke-polygon.c
@@ -67,6 +67,7 @@ struct stroker {
     const cairo_matrix_t *ctm;
     const cairo_matrix_t *ctm_inverse;
     double tolerance;
+    double spline_cusp_tolerance;
     cairo_bool_t ctm_det_positive;
 
     cairo_pen_t pen;
@@ -1152,8 +1153,8 @@ spline_to (void *closure,
     } else {
 	compute_face (point, tangent, stroker, &face);
 
-	if (face.dev_slope.x * stroker->current_face.dev_slope.x +
-	    face.dev_slope.y * stroker->current_face.dev_slope.y < 0)
+	if ((face.dev_slope.x * stroker->current_face.dev_slope.x +
+	     face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance)
 	{
 	    const cairo_point_t *inpt, *outpt;
 	    struct stroke_contour *outer;
@@ -1304,7 +1305,17 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
     stroker.ctm = ctm;
     stroker.ctm_inverse = ctm_inverse;
     stroker.tolerance = tolerance;
-
+    /* To test whether we need to join two segments of a spline using
+     * a round-join or a bevel-join, we can inspect the angle between the
+     * two segments. If the difference between the chord distance
+     * (half-line-width times the cosine of the bisection angle) and the
+     * half-line-width itself is greater than tolerance then we need to
+     * inject a point.
+     */
+    stroker.spline_cusp_tolerance = 1 - 2 * tolerance / style->line_width;
+    stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance;
+    stroker.spline_cusp_tolerance *= 2;
+    stroker.spline_cusp_tolerance -= 1;
     stroker.ctm_det_positive =
 	_cairo_matrix_compute_determinant (ctm) >= 0.0;
 
commit 3d482e266febcf7da75f5662e518380460068ce1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Jun 24 11:50:26 2012 +0100

    test: Exercise bug in joining together spline segments around cusps
    
    Carl Worth demonstrated a glaring bug in the new stroking code,
    introduced in commit 545f30856aac98199 (stroke: Convert the outlines
    into contour and then into a polygon), whereby only a bevel join was
    being used to connect segments around a sharp inflection point.
    
    This adds the two examples he reported to the test suite.

diff --git a/test/Makefile.sources b/test/Makefile.sources
index ddb41b5..9aada62 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -21,6 +21,7 @@ test_sources = \
 	big-empty-triangle.c				\
 	big-little-box.c				\
 	big-little-triangle.c				\
+	bug-spline.c					\
 	big-trap.c					\
 	bilevel-image.c					\
 	bug-40410.c					\
diff --git a/test/bug-spline.c b/test/bug-spline.c
new file mode 100644
index 0000000..00a915c
--- /dev/null
+++ b/test/bug-spline.c
@@ -0,0 +1,96 @@
+/* cc `pkg-config --cflags --libs cairo` cairo-spline-image.c -o cairo-spline-image */
+
+/* Copyright © 2005 Carl Worth
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "cairo-test.h"
+
+#define WIDE_LINE_WIDTH 160
+#define NARROW_LINE_WIDTH 2
+
+/* A spline showing bugs in the "contour-based stroking" in cairo 1.12 */
+static const struct spline {
+    struct { double x, y; } pt[5];
+    double line_width;
+    double rgba[4];
+} splines[] = {
+    {
+	{
+	    { 172.25, 156.185 },
+	    { 177.225, 164.06 },
+	    { 176.5, 157.5 },
+	    { 175.5, 159.5 },
+	},
+	WIDE_LINE_WIDTH,
+	{ 1, 1, 1, 1 },
+    },
+    {
+	{
+	    { 571.25, 247.185 },
+	    { 78.225, 224.06 },
+	    { 129.5, 312.5 },
+	    { 210.5, 224.5 },
+	},
+	NARROW_LINE_WIDTH,
+	{ 1, 0, 0, 1 },
+    }
+};
+#define NUM_SPLINES (sizeof(splines)/sizeof(splines[0]))
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    unsigned n;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    cairo_set_line_cap(cr, CAIRO_LINE_CAP_SQUARE);
+
+    for (n = 0; n < NUM_SPLINES; n++) {
+	cairo_set_line_width (cr, splines[n].line_width);
+	cairo_set_source_rgba (cr,
+			       splines[n].rgba[0],
+			       splines[n].rgba[1],
+			       splines[n].rgba[2],
+			       splines[n].rgba[3]);
+
+	cairo_move_to (cr, splines[n].pt[0].x, splines[n].pt[0].y);
+	cairo_curve_to (cr,
+			splines[n].pt[1].x, splines[n].pt[1].y,
+			splines[n].pt[2].x, splines[n].pt[2].y,
+			splines[n].pt[3].x, splines[n].pt[3].y);
+
+	cairo_stroke (cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (bug_spline,
+	    "Exercises a bug in the stroking of splines",
+	    "spline, stroke", /* keywords */
+	    NULL, /* requirements */
+	    300, 300,
+	    NULL, draw)
diff --git a/test/reference/bug-spline.ref.png b/test/reference/bug-spline.ref.png
new file mode 100644
index 0000000..ebef261
Binary files /dev/null and b/test/reference/bug-spline.ref.png differ


More information about the cairo-commit mailing list