[cairo-commit] 5 commits - src/cairo-arc.c src/cairo.c test/arc-infinite-loop.c test/arc-infinite-loop.ref.png test/arc-looping-dash.c test/arc-looping-dash.image16.ref.png test/arc-looping-dash.ps.ref.png test/arc-looping-dash.quartz.ref.png test/arc-looping-dash.ref.png test/Makefile.am test/Makefile.sources test/testtable.js

Andrea Canciani ranma42 at kemper.freedesktop.org
Fri Dec 10 01:34:00 PST 2010


 src/cairo-arc.c                       |   11 +++-
 src/cairo.c                           |   20 +++++++-
 test/Makefile.am                      |    4 +
 test/Makefile.sources                 |    2 
 test/arc-infinite-loop.c              |   59 ++++++++++++++++++++++++++
 test/arc-infinite-loop.ref.png        |binary
 test/arc-looping-dash.c               |   77 ++++++++++++++++++++++++++++++++++
 test/arc-looping-dash.image16.ref.png |binary
 test/arc-looping-dash.ps.ref.png      |binary
 test/arc-looping-dash.quartz.ref.png  |binary
 test/arc-looping-dash.ref.png         |binary
 test/testtable.js                     |    2 
 12 files changed, 168 insertions(+), 7 deletions(-)

New commits:
commit 17fef8c437483ca082226b41e1850eb383f0251a
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Dec 1 16:16:00 2010 +0100

    test: Add arc-infinite-loop
    
    The handling of angles above 2pi in cairo_arc is not very solid and is
    basically untested.
    
    This test should ensure that huge inputs don't hang cairo.

diff --git a/test/Makefile.am b/test/Makefile.am
index e2f60ed..4ad90db 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -154,6 +154,7 @@ REFERENCE_IMAGES = \
 	alpha-similar.svg.argb32.xfail.png \
 	alpha-similar.svg.rgb24.xfail.png \
 	api-special-cases.ref.png \
+	arc-infinite-loop.ref.png \
 	arc-looping-dash.image16.ref.png \
 	arc-looping-dash.ps.ref.png \
 	arc-looping-dash.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index d9fc862..dfdace6 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -9,6 +9,7 @@ test_sources = \
 	a8-mask.c					\
 	aliasing.c					\
 	alpha-similar.c					\
+	arc-infinite-loop.c				\
 	arc-looping-dash.c				\
 	api-special-cases.c				\
 	big-line.c					\
diff --git a/test/arc-infinite-loop.c b/test/arc-infinite-loop.c
new file mode 100644
index 0000000..e648c70
--- /dev/null
+++ b/test/arc-infinite-loop.c
@@ -0,0 +1,59 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * 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.
+ *
+ * Author: Andrea Canciani <ranma42 at gmail.com>
+ */
+
+#include "cairo-test.h"
+#include <float.h>
+
+#define SIZE 8
+
+/*
+  cairo_arc can hang in an infinite loop if given huge (so big that
+  adding/subtracting 4*M_PI to them doesn't change the value because
+  of floating point rounding).
+
+  The purpose of this test is to check that cairo doesn't hang or crash.
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* Check if the code that guarantees start <= end hangs */
+    cairo_arc (cr, 0, 0, 1, 1024 / DBL_EPSILON * M_PI, 0);
+
+    /* Check if the code that handles huge angles hangs */
+    cairo_arc (cr, 0, 0, 1, 0, 1024 / DBL_EPSILON * M_PI);
+}
+
+CAIRO_TEST (arc_infinite_loop,
+	    "Test cairo_arc with huge angles",
+	    "arc", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw)
diff --git a/test/arc-infinite-loop.ref.png b/test/arc-infinite-loop.ref.png
new file mode 100644
index 0000000..82d645f
Binary files /dev/null and b/test/arc-infinite-loop.ref.png differ
commit 670eb260eb2f0cbe897ce92d3a7ab4542fb17307
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Dec 1 16:13:33 2010 +0100

    test: Add arc-looping-dash
    
    The handling of angles above 2pi in cairo_arc is not very solid and is
    basically untested.
    
    This test should ensure that changes in the behavior will be noticed
    by the testsuite.

diff --git a/test/Makefile.am b/test/Makefile.am
index b1222de..e2f60ed 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -154,6 +154,9 @@ REFERENCE_IMAGES = \
 	alpha-similar.svg.argb32.xfail.png \
 	alpha-similar.svg.rgb24.xfail.png \
 	api-special-cases.ref.png \
+	arc-looping-dash.image16.ref.png \
+	arc-looping-dash.ps.ref.png \
+	arc-looping-dash.ref.png \
 	big-line.ref.png \
 	big-line.image16.ref.png \
 	big-line.ps.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index b6431d1..d9fc862 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -9,6 +9,7 @@ test_sources = \
 	a8-mask.c					\
 	aliasing.c					\
 	alpha-similar.c					\
+	arc-looping-dash.c				\
 	api-special-cases.c				\
 	big-line.c					\
 	big-trap.c					\
diff --git a/test/arc-looping-dash.c b/test/arc-looping-dash.c
new file mode 100644
index 0000000..2ad55f6
--- /dev/null
+++ b/test/arc-looping-dash.c
@@ -0,0 +1,77 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright 2010 Andrea Canciani
+ *
+ * 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.
+ *
+ * Author: Andrea Canciani <ranma42 at gmail.com>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 32
+
+/*
+  When cairo_arc is used to draw an arc of more than 2pi radians
+  (i.e. a circle "looping over itself"), various different behaviors
+  are possible:
+
+  - draw exactly a circle (an arc of 2pi radians)
+
+  - draw an arc such that the current point is the expected one and
+    that does at least a complete circle (an arc of [2pi, 4pi)
+    radians)
+
+  - draw an arc with the original number of loops
+
+  This test produces different results for each of these three cases.
+*/
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    double dashes[] = { 0.3, 7 };
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_save (cr);
+
+    cairo_translate (cr, SIZE * .5, SIZE * .5);
+    cairo_scale (cr, SIZE * 3 / 8., SIZE * 3 / 8.);
+
+    cairo_arc (cr, 0, 0, 1, 0, 11 * M_PI);
+
+    cairo_set_line_width (cr, 8. / SIZE);
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_set_dash (cr, dashes, 2, 0);
+    cairo_stroke (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (arc_looping_dash,
+	    "Test cairo_arc for angles describing more than a complete circle",
+	    "arc", /* keywords */
+	    NULL, /* requirements */
+	    SIZE, SIZE,
+	    NULL, draw)
diff --git a/test/arc-looping-dash.image16.ref.png b/test/arc-looping-dash.image16.ref.png
new file mode 100644
index 0000000..addc93c
Binary files /dev/null and b/test/arc-looping-dash.image16.ref.png differ
diff --git a/test/arc-looping-dash.ps.ref.png b/test/arc-looping-dash.ps.ref.png
new file mode 100644
index 0000000..ab19b19
Binary files /dev/null and b/test/arc-looping-dash.ps.ref.png differ
diff --git a/test/arc-looping-dash.quartz.ref.png b/test/arc-looping-dash.quartz.ref.png
new file mode 100644
index 0000000..70304ca
Binary files /dev/null and b/test/arc-looping-dash.quartz.ref.png differ
diff --git a/test/arc-looping-dash.ref.png b/test/arc-looping-dash.ref.png
new file mode 100644
index 0000000..516e66c
Binary files /dev/null and b/test/arc-looping-dash.ref.png differ
commit 1784fd403e3dce357f018639730dd75e138904b5
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Dec 9 14:38:40 2010 +0100

    arc: Clamp to 65536 full circles
    
    To limit the amount of memory used for arcs describing a circle
    wrapped multiple times we ignore the circles after the 65536th (but
    preserve the same start and end angle mod 2pi).

diff --git a/src/cairo-arc.c b/src/cairo-arc.c
index 1b2713f..c997e5c 100644
--- a/src/cairo-arc.c
+++ b/src/cairo-arc.c
@@ -38,6 +38,8 @@
 
 #include "cairo-arc-private.h"
 
+#define MAX_FULL_CIRCLES 65536
+
 /* Spline deviation from the circle in radius would be given by:
 
 	error = sqrt (x**2 + y**2) - 1
@@ -184,8 +186,13 @@ _cairo_arc_in_direction (cairo_t	  *cr,
     if (cairo_status (cr))
         return;
 
-    while (angle_max - angle_min > 4 * M_PI)
-	angle_max -= 2 * M_PI;
+    assert (angle_max >= angle_min);
+
+    if (angle_max - angle_min > 2 * M_PI * MAX_FULL_CIRCLES) {
+	angle_max = fmod (angle_max - angle_min, 2 * M_PI);
+	angle_min = fmod (angle_min, 2 * M_PI);
+	angle_max += angle_min + 2 * M_PI * MAX_FULL_CIRCLES;
+    }
 
     /* Recurse if drawing arc larger than pi */
     if (angle_max - angle_min > M_PI) {
commit 4314a86aa7813bcd4131827181ecf44994142a72
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Dec 9 14:38:10 2010 +0100

    arc: Avoid infinite loop
    
    Adding/subtracting 2 * M_PI to a huge floating point number doesn't
    change it (because of rounding) and for smaller numbers it still
    requires a lot of cycles before the angle is in the desired range.
    
    The same computation can be performed using fmod, which should provide
    more accurate results and only requires O(1) time.

diff --git a/src/cairo.c b/src/cairo.c
index 5621610..3abcbb1 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -1861,8 +1861,14 @@ cairo_arc (cairo_t *cr,
 	return;
     }
 
-    while (angle2 < angle1)
-	angle2 += 2 * M_PI;
+    if (angle2 < angle1) {
+	/* increase angle2 by multiples of full circle until it
+	 * satisfies angle2 >= angle1 */
+	angle2 = fmod (angle2 - angle1, 2 * M_PI);
+	if (angle2 < 0)
+	    angle2 += 2 * M_PI;
+	angle2 += angle1;
+    }
 
     cairo_line_to (cr,
 		   xc + radius * cos (angle1),
@@ -1903,8 +1909,14 @@ cairo_arc_negative (cairo_t *cr,
     if (radius <= 0.0)
 	return;
 
-    while (angle2 > angle1)
-	angle2 -= 2 * M_PI;
+    if (angle2 > angle1) {
+	/* decrease angle2 by multiples of full circle until it
+	 * satisfies angle2 <= angle1 */
+	angle2 = fmod (angle2 - angle1, 2 * M_PI);
+	if (angle2 > 0)
+	    angle2 -= 2 * M_PI;
+	angle2 += angle1;
+    }
 
     cairo_line_to (cr,
 		   xc + radius * cos (angle1),
commit 028797a729ca74b4ce2d1062fc8c90c111bf2870
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Mon Dec 6 18:04:10 2010 +0100

    test: Handle crashed tests
    
    Crashed tests are reported as "CRASH!" in cairo-test-suite.log

diff --git a/test/testtable.js b/test/testtable.js
index 350ba40..f51b0fd 100644
--- a/test/testtable.js
+++ b/test/testtable.js
@@ -1,7 +1,7 @@
 /* configuration */
 /* TODO: UNTESTED count can't be shown because it's not tracked explicitly */
 headerResults = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
-logResults    = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASHED" ];
+logResults    = [ "PASS", "NEW", "FAIL", "XFAIL", "CRASH!" ];
 resultToImgs = {
     "PASS"     : [],
     "NEW"      : [ "output" ],


More information about the cairo-commit mailing list