[cairo-commit] Branch '1.4' - 3 commits - NEWS src/cairo-path-stroke.c test/.gitignore test/Makefile.am test/miter-precision.c test/miter-precision-ref.png

Carl Worth cworth at kemper.freedesktop.org
Tue Jan 8 16:46:38 PST 2008


 NEWS                         |   21 ++++++
 src/cairo-path-stroke.c      |  139 +++++++++++++++++--------------------------
 test/.gitignore              |    1 
 test/Makefile.am             |    1 
 test/miter-precision-ref.png |binary
 test/miter-precision.c       |   80 ++++++++++++++++++++++++
 6 files changed, 160 insertions(+), 82 deletions(-)

New commits:
commit ca1cd4c28e4082e42b843a805e27d7d2b3dfeb04
Author: Carl Worth <cworth at cworth.org>
Date:   Tue Jan 8 16:23:45 2008 -0800

    NEWS: Add notes for 1.4.14

diff --git a/NEWS b/NEWS
index 2771e31..25a1ca2 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,24 @@
+Release 1.4.14 (2008-01-08 Carl Worth <cworth at cworth.org>)
+==========================================================
+This is the seventh update in cairo's stable 1.4 series. It comes
+little more than a month after the 1.4.12 release. Compared to 1.4.12,
+this release adds only a handful of bug fixes all of which were
+cherry-picked from the 1.5 series. One of these fixes a miter-join
+regression introduced during the 1.4.12 series. See below for details.
+
+Fix a regression (which first appeared in 1.4.12) where stroking under
+a large scale would sometimes incorrectly replace a miter join with a
+bevel join. (Thanks to Keith Packard.)
+
+Fix xlib backend to not consider recent X server release as having a
+buggy repeat implementation in the Render extension. (Thanks to
+Bernardo Innocenti.)
+
+Fix several bugs in cairo's PostScript output. These include making
+the PostScript output more compatible with recent versions of
+ghostscript that are more strict about Type 3 fonts, for
+example. (Many thanks to Adrian Johnson.)
+
 Release 1.4.12 (2007-11-26 Carl Worth <cworth at cworth.org>)
 ==========================================================
 This is the sixth update in cairo's stable 1.4 series. It comes five
commit 0a57aa8ac1839c386ef8329dba4fdbc9d479a5fa
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Jan 3 18:37:53 2008 -0800

    Directly check the miter corner to detect wild miters.
    
    The original test for wild miters would only work with a square transform
    (and, in fact, the original code required an identity transform). Instead of
    fixing that, I replaced it with a more obvious test which makes sure the
    miter corner lies between the two faces and not out in space somewhere.
    (cherry picked from commit 7cf9a6e4e39b18f4967afdb7c3c71eca5f4ba8c7)

diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 8535828..98004a2 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -205,6 +205,22 @@ _cairo_stroker_face_clockwise (cairo_stroke_face_t *in, cairo_stroke_face_t *out
     return _cairo_slope_clockwise (&in_slope, &out_slope);
 }
 
+/**
+ * _cairo_slope_compare_sgn
+ *
+ * Return -1, 0 or 1 depending on the relative slopes of
+ * two lines.
+ */
+static int
+_cairo_slope_compare_sgn (double dx1, double dy1, double dx2, double dy2)
+{
+    double  c = (dx1 * dy2 - dx2 * dy1);
+
+    if (c > 0) return 1;
+    if (c < 0) return -1;
+    return 0;
+}
+
 static cairo_status_t
 _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
 {
@@ -280,9 +296,6 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	double	in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+
 			      (-in->usr_vector.y * out->usr_vector.y));
 	double	ml = stroker->style->miter_limit;
-	double tolerance_squared = stroker->tolerance * stroker->tolerance;
-	double line_width_squared = (stroker->style->line_width *
-				     stroker->style->line_width);
 
 	/*
 	 * Check the miter limit -- lines meeting at an acute angle
@@ -311,84 +324,17 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 	 *
 	 *	2 <= ml² (1 - in · out)
 	 *
-	 *
-	 * That gives us the condition to avoid generating miters that
-	 * are too large from angles that are too large. But we also
-	 * need to avoid generating miters when the angle is very small.
-	 *
-	 * The miter formed from a tiny angle is also tiny, so the
-	 * miter limit is not a concern. But with a tiny angle we will
-	 * be computing the intersection of two lines that are very
-	 * near parallel. Also, the limits of the fixed-point grid on
-	 * the input face coordinates mean that the resulting
-	 * intersection could be wildly wrong. (See the
-	 * get-path-extents test case for a call to cairo_arc that
-	 * results in two problematic faces.)
-	 *
-	 * Fortunately we can also derive an expression for when using
-	 * a bevel join instead of a miter will introduce an error no
-	 * larger than the tolerance. Consider the same join from
-	 * before but with the miter now chopped off and replaced with
-	 * a bevel join. The drawing is zoomed in a bit again, the
-	 * point marked as '*' is the center of the stroke---the point
-	 * where the two line segments of interest intersect:
-	 *
-	 *    ----- .
-	 *    ^     ..
-	 *    |     . .
-	 *    |     .  .
-	 *   1/2    .   .
-	 *  miter   .    .         |
-	 *  length  .     .        |
-	 *    |     .______.    ___v___
-	 *    |     |     . \   1/2 bevel
-	 *    v     |  .     \   width
-	 *    ----  *         \ -------
-	 *	    |          \   ^
-	 *
-	 *
-	 * The length of interest here is the vertical length of the
-	 * miter that is eliminated. It's length can be obtained by
-	 * starting with 1/2 the miter length and the subtracting off
-	 * the vertical length that is included by the bevel join,
-	 * (here termed 1/2 bevel width). To determine this new bevel
-	 * width, we have a small right triangle shown, the hypotenuse
-	 * of which has a length of 1/2 the line width, and the small
-	 * angle at the upper right of the figure is psi/2.
-	 *
-	 * So we have:
-	 *
-	 *	sin (psi/2) = (bevel_width / 2) / (line_width / 2)
-	 *
-	 * And we can determine when the miter is required by
-	 * calculating when the eliminated portion of the miter is
-	 * greater than the tolerance:
-	 *
-	 *	(miter_length / 2) - (bevel_width / 2) > tolerance
-	 *
-	 * Substituting in the above expressions for miter_length and
-	 * bevel_width:
-	 *
-	 *	(line_width/2) / sin (psi/2) - (line_width/2) * sin (psi/2) > tolerance
-	 *	1 / sin(psi/2) - sin (psi/2) > 2 * tolerance / line_width
-	 *	1 / sin²(psi/2) -2 +  sin²(psi/2) > 4 * (tolerance/line_width)²
-	 *
-	 * Use identity: sin²(psi/2) = (1-cos(psi))/2
-
-	 *	2/(1 - cos(psi)) - 2 + (1-cos(psi))/2 > 4 * (tolerance/line_width)²
-	 *	4/(1 - cos(psi)) - 4 + (1-cos(psi)) > 8 * (tolerance/line_width)²
-	 *	4/(1 - cos(psi)) + (1-cos(psi)) > 8 * ((tolerance/line_width)² + 0.5)
 	 */
-	if ((2 <= ml * ml * (1 - in_dot_out)) &&
-	    ((8 * (tolerance_squared / line_width_squared + 0.5)) <
-	     4 / (1 - in_dot_out) + (1 - in_dot_out))
-	    )
+	if (2 <= ml * ml * (1 - in_dot_out))
 	{
 	    double		x1, y1, x2, y2;
 	    double		mx, my;
 	    double		dx1, dx2, dy1, dy2;
 	    cairo_point_t	outer;
 	    cairo_point_t	quad[4];
+	    double		ix, iy;
+	    double		fdx1, fdy1, fdx2, fdy2;
+	    double		mdx, mdy;
 
 	    /*
 	     * we've got the points already transformed to device
@@ -426,17 +372,46 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
 		mx = (my - y2) * dx2 / dy2 + x2;
 
 	    /*
-	     * Draw the quadrilateral
+	     * When the two outer edges are nearly parallel, slight
+	     * perturbations in the position of the outer points of the lines
+	     * caused by representing them in fixed point form can cause the
+	     * intersection point of the miter to move a large amount. If
+	     * that moves the miter intersection from between the two faces,
+	     * then draw a bevel instead.
 	     */
-	    outer.x = _cairo_fixed_from_double (mx);
-	    outer.y = _cairo_fixed_from_double (my);
 
-	    quad[0] = in->point;
-	    quad[1] = *inpt;
-	    quad[2] = outer;
-	    quad[3] = *outpt;
+	    ix = _cairo_fixed_to_double (in->point.x);
+	    iy = _cairo_fixed_to_double (in->point.y);
 
-	    return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	    /* slope of one face */
+	    fdx1 = x1 - ix; fdy1 = y1 - iy;
+
+	    /* slope of the other face */
+	    fdx2 = x2 - ix; fdy2 = y2 - iy;
+
+	    /* slope from the intersection to the miter point */
+	    mdx = mx - ix; mdy = my - iy;
+
+	    /*
+	     * Make sure the miter point line lies between the two
+	     * faces by comparing the slopes
+	     */
+	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
+		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
+	    {
+		/*
+		 * Draw the quadrilateral
+		 */
+		outer.x = _cairo_fixed_from_double (mx);
+		outer.y = _cairo_fixed_from_double (my);
+
+		quad[0] = in->point;
+		quad[1] = *inpt;
+		quad[2] = outer;
+		quad[3] = *outpt;
+
+		return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
+	    }
 	}
 	/* fall through ... */
     }
commit 3fb0eaca42b00808d1c61031e385c95768195e7f
Author: Keith Packard <keithp at keithp.com>
Date:   Thu Jan 3 18:17:24 2008 -0800

    Add new miter-precision test. Checks miter joins at many scales.
    
    This demonstrates an error in cairo where miter joins are replaced with
    bevels at high scale factors due to a test added to eliminate wild miters
    drawn when the line faces are nearly parallel.
    
    Cherry picked from commit 81e029edda8c0404c6f0bd1b618e77fb55777c64

diff --git a/test/.gitignore b/test/.gitignore
index 875c1c9..20fdd90 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -78,6 +78,7 @@ long-lines
 mask
 mask-ctm
 mask-surface-ctm
+miter-precision
 move-to-show-surface
 multi-page
 multi-page.pdf
diff --git a/test/Makefile.am b/test/Makefile.am
index 716f558..cd09bd1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -61,6 +61,7 @@ long-lines			\
 mask				\
 mask-ctm			\
 mask-surface-ctm		\
+miter-precision			\
 move-to-show-surface		\
 new-sub-path			\
 nil-surface			\
diff --git a/test/miter-precision-ref.png b/test/miter-precision-ref.png
new file mode 100644
index 0000000..c9f7f5b
Binary files /dev/null and b/test/miter-precision-ref.png differ
diff --git a/test/miter-precision.c b/test/miter-precision.c
new file mode 100644
index 0000000..09cf493
--- /dev/null
+++ b/test/miter-precision.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2007 Keith Packard
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+ *
+ * The Initial Developer of the Original Code is Keith Packard
+ *
+ * Contributor(s):
+ *      Keith Packard <keithp at keithp.com>
+ */
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+    "miter-precision",
+    "test how cairo deals with small miters"
+    "\ncurrent code draws inappropriate bevels at times",
+    120, 100,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double  xscale, yscale;
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_miter_limit (cr, 100000);
+    for (xscale = 1; xscale <= 1000; xscale += 999)
+	for (yscale = 1; yscale <= 1000; yscale += 999)
+	{
+	    double  max_scale = xscale > yscale ? xscale : yscale;
+	    cairo_save (cr);
+	    if (xscale > 1)
+		cairo_translate (cr, 50, 0);
+	    if (yscale > 1)
+		cairo_translate (cr, 0, 50);
+	    cairo_scale (cr, xscale, yscale);
+	    cairo_set_line_width (cr, 10.0 / max_scale);
+	    cairo_move_to (cr, 10.0 / xscale, 10.0 / yscale);
+	    cairo_line_to (cr, 40.0 / xscale, 10.0 / yscale);
+	    cairo_line_to (cr, 10.0 / xscale, 30.0 / yscale);
+	    cairo_stroke (cr);
+	    cairo_restore (cr);
+	}
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}


More information about the cairo-commit mailing list