[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