[cairo-commit] 6 commits - perf/cairo-perf-diff.c perf/.gitignore perf/Makefile.am src/cairo-pen.c test/infinite-join.c test/Makefile.am

Carl Worth cworth at kemper.freedesktop.org
Wed Oct 18 15:07:01 PDT 2006


 perf/.gitignore        |    1 
 perf/Makefile.am       |    6 
 perf/cairo-perf-diff.c |  478 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/cairo-pen.c        |    7 
 test/Makefile.am       |    1 
 test/infinite-join.c   |   80 ++++++++
 6 files changed, 571 insertions(+), 2 deletions(-)

New commits:
diff-tree e21c155e73569f6533dd01bdd7ec62461e7f215f (from parents)
Merge: a53eabf0a505bcf01e52af1b47b194a482eddf51 10cd23d51fbfc99d6e3e401440eebb56df3b3327
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Oct 18 15:03:04 2006 -0700

    Merge branch '8379' into cairo

diff --cc test/Makefile.am
index c513949,1630a4e..f5d0667
@@@ -39,10 -38,9 +39,11 @@@
  font-matrix-translation		\
  glyph-cache-pressure		\
  get-and-set			\
 +get-clip                        \
  get-group-target		\
 +get-path-extents                \
  gradient-alpha			\
+ infinite-join			\
  leaky-dash			\
  leaky-polygon			\
  line-width			\
diff-tree 10cd23d51fbfc99d6e3e401440eebb56df3b3327 (from 5b7a7f39ad8b726e9ee582bcd10500a1e5b16554)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Sep 21 15:17:59 2006 -0700

    Fix infinite-join test case (bug #8379)
    
    The trick for this was to carefully ensure that the pen always has
    at least 4 vertices. There was a previous attempt at this in the
    code already but the test case had a combination of matrix and radius
    that resulted in a value that was just able to sneak past the previous
    check.

diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index 3d42314..0e24f2d 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -270,7 +270,12 @@ _cairo_pen_vertices_needed (double	    t
 	/* number of vertices must be even */
 	if (num_vertices % 2)
 	    num_vertices++;
+
+	/* And we must always have at least 4 vertices. */
+	if (num_vertices < 4)
+	    num_vertices = 4;
     }
+
     return num_vertices;
 }
 
diff-tree 5b7a7f39ad8b726e9ee582bcd10500a1e5b16554 (from 5492a7c7618a747130b844a509fb79b886f1bc54)
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Oct 18 15:01:58 2006 -0700

    test/infinite-join: Modify to draw something visible, and make the output a more reasonable size.
    
    The modification was performed with care to ensure that the bug
    is still exercised. Also, reference images are added.

diff --git a/test/infinite-join.c b/test/infinite-join.c
index fffb386..74d4206 100644
--- a/test/infinite-join.c
+++ b/test/infinite-join.c
@@ -38,13 +38,18 @@ static cairo_test_draw_function_t draw;
 cairo_test_t test = {
     "infinite-join",
     "Test case for infinite loop when stroking with round joins",
-    120, 70,
+    8, 8,
     draw
 };
 
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
+    /* Paint white, then draw in black. */
+    cairo_set_source_rgb (cr, 1, 1, 1); /* white */
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* black */
+
     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
 
     /* scaling 2 times causes a slight rounding error in the ctm.
@@ -55,11 +60,13 @@ draw (cairo_t *cr, int width, int height
     cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
     cairo_set_line_width (cr, 20);
 
+    cairo_translate (cr, -18300, -13200);
+
     cairo_new_path (cr);
     cairo_move_to (cr, 18928, 13843);
-    cairo_line_to (cr, -6928, 13843);
-    cairo_line_to (cr, -6928, -6842);
-    cairo_line_to (cr, 18928, -6842);
+    cairo_line_to (cr, 18500, 13843);
+    cairo_line_to (cr, 18500, 13400);
+    cairo_line_to (cr, 18928, 13400);
     cairo_line_to (cr, 18928, 13843);
     cairo_stroke (cr);
 
diff-tree a53eabf0a505bcf01e52af1b47b194a482eddf51 (from 5c7798a646ecad59dfabac1f9afb96006470054e)
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Oct 18 12:14:38 2006 -0700

    Add cairo-perf-diff program for comparing two cairo-perf reports.

diff --git a/perf/.gitignore b/perf/.gitignore
index 66b1d8f..5881588 100644
--- a/perf/.gitignore
+++ b/perf/.gitignore
@@ -1,4 +1,5 @@
 cairo-perf
+cairo-perf-diff
 *.o
 *.exe
 *.manifest
diff --git a/perf/Makefile.am b/perf/Makefile.am
index ef6dc73..54d3557 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -12,7 +12,7 @@ INCLUDES =					\
 	-I$(top_srcdir)/src			\
 	$(CAIRO_CFLAGS)
 
-noinst_PROGRAMS = cairo-perf
+noinst_PROGRAMS = cairo-perf cairo-perf-diff
 
 cairo_perf_SOURCES =		\
 	cairo-perf.c		\
@@ -31,9 +31,11 @@ else
 cairo_perf_SOURCES += cairo-perf-posix.c
 endif
 
+cairo_perf_diff_SOURCES =	\
+	cairo-perf-diff.c
+
 LDADD = $(top_builddir)/boilerplate/libcairoboilerplate.la \
 	$(top_builddir)/src/libcairo.la
 
 perf: cairo-perf
 	./cairo-perf
-
diff --git a/perf/cairo-perf-diff.c b/perf/cairo-perf-diff.c
new file mode 100644
index 0000000..acbef43
--- /dev/null
+++ b/perf/cairo-perf-diff.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Authors: Carl Worth <cworth at cworth.org>
+ */
+
+/* We use _GNU_SOURCE for getline. If someone wants to avoid that
+ * dependence they could conditionally provide a custom implementation
+ * of getline instead. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+typedef struct _test_report {
+    int id;
+    char *backend;
+    char *content;
+    char *name;
+    int size;
+    double ticks;
+    double time;
+    double std_dev;
+    int iterations;
+} test_report_t;
+
+typedef struct _test_diff {
+    test_report_t *old;
+    test_report_t *new;
+    double speedup;
+} test_diff_t;
+
+typedef struct _cairo_perf_report {
+    const char *name;
+    test_report_t *tests;
+    int tests_size;
+    int tests_count;
+} cairo_perf_report_t;
+
+typedef enum {
+    TEST_REPORT_STATUS_SUCCESS,
+    TEST_REPORT_STATUS_COMMENT,
+    TEST_REPORT_STATUS_ERROR
+} test_report_status_t;
+
+/* Ad-hoc parsing, macros with a strong dependence on the calling
+ * context, and plenty of other ugliness is here.  But at least it's
+ * not perl... */
+#define parse_error(...) fprintf(stderr, __VA_ARGS__); return TEST_REPORT_STATUS_ERROR;
+#define skip_char(c)							\
+do {									\
+    if (*s && *s == (c)) {						\
+	s++;								\
+    } else {								\
+	 parse_error ("expected '%c' but found '%c'", c, *s);		\
+    }									\
+} while (0)
+#define skip_space() while (*s && (*s == ' ' || *s == '\t')) s++;
+#define parse_int(result)						\
+do {									\
+    (result) = strtol (s, &end, 10);					\
+    if (*s && end != s) {						\
+	s = end;							\
+    } else {								\
+	parse_error("expected integer but found %s", s);		\
+    }									\
+} while (0)
+#define parse_double(result)						\
+do {									\
+    (result) = strtod (s, &end);					\
+    if (*s && end != s) {						\
+	s = end;							\
+    } else {								\
+	parse_error("expected floating-point value but found %s", s);	\
+    }									\
+} while (0)
+/* Here a string is simply a sequence of non-whitespace */
+#define parse_string(result)						\
+do {									\
+    for (end = s; *end; end++)						\
+	if (isspace (*end))						\
+	    break;							\
+    (result) = strndup (s, end - s);					\
+    if ((result) == NULL) {						\
+	fprintf (stderr, "Out of memory.\n");				\
+	exit (1);							\
+    }									\
+    s = end;								\
+} while (0)
+
+static test_report_status_t
+test_report_parse (test_report_t *report, char *line)
+{
+    char *end;
+    char *s = line;
+
+    /* The code here looks funny unless you understand that these are
+     * all macro calls, (and then the code just looks sick). */
+    if (*s == '\n')
+	return TEST_REPORT_STATUS_COMMENT;
+
+    skip_char ('[');
+    skip_space ();
+    if (*s == '#')
+	return TEST_REPORT_STATUS_COMMENT;
+    parse_int (report->id);
+    skip_char (']');
+
+    skip_space ();
+
+    parse_string (report->backend);
+    end = strrchr (report->backend, '-');
+    if (*end)
+	*end++ = '\0';
+    report->content = end;
+
+    skip_space ();
+
+    parse_string (report->name);
+    end = strrchr (report->name, '-');
+    if (*end)
+	*end++ = '\0';
+    report->size = atoi (end);
+
+    skip_space ();
+
+    parse_double (report->ticks);
+
+    skip_space ();
+
+    parse_double (report->time);
+
+    skip_space ();
+
+    parse_double (report->std_dev);
+    report->std_dev /= 100.0;
+    skip_char ('%');
+
+    skip_space ();
+
+    parse_int (report->iterations);
+
+    skip_space ();
+    skip_char ('\n');
+
+    return TEST_REPORT_STATUS_SUCCESS;
+}
+
+static void
+cairo_perf_report_load (cairo_perf_report_t *report, const char *filename)
+{
+    FILE *file;
+    test_report_status_t status;
+    int line_number = 0;
+    char *line = NULL;
+    size_t line_size = 0;
+
+    report->name = filename;
+    report->tests = NULL;
+    report->tests_size = 0;
+    report->tests_count = 0;
+
+    file = fopen (filename, "r");
+    if (file == NULL) {
+	fprintf (stderr, "Failed to open %s: %s\n",
+		 filename, strerror (errno));
+	exit (1);
+    }
+
+    while (1) {
+	if (report->tests_count == report->tests_size) {
+	    report->tests_size = report->tests_size ? 2 * report->tests_size : 16;
+	    report->tests = realloc (report->tests,
+				     report->tests_size * sizeof (test_report_t));
+	    if (report->tests == NULL) {
+		fprintf (stderr, "Out of memory.\n");
+		exit (1);
+	    }
+	}
+
+	line_number++;
+	if (getline (&line, &line_size, file) == -1)
+	    break;
+
+	status = test_report_parse (&report->tests[report->tests_count], line);
+	if (status == TEST_REPORT_STATUS_ERROR)
+	    fprintf (stderr, "Ignoring unrecognized line %d of %s:\n%s",
+		     line_number, filename, line);
+	if (status == TEST_REPORT_STATUS_SUCCESS)
+	    report->tests_count++;
+	/* Do nothing on TEST_REPORT_STATUS_COMMENT */
+    }
+
+    if (line)
+	free (line);
+}
+
+static int
+test_diff_cmp (const void *a, const void *b)
+{
+    const test_diff_t *a_diff = a;
+    const test_diff_t *b_diff = b;
+    double a_change, b_change;
+
+    a_change = a_diff->speedup;
+    b_change = b_diff->speedup;
+
+    /* First make all speedups come before all slowdowns. */
+    if (a_change > 1.0 && b_change < 1.0)
+	return -1;
+    if (a_change < 1.0 && b_change > 1.0)
+	return 1;
+
+    /* Then, within each, sort by magnitude of speed change */
+    if (a_change < 1.0)
+	a_change = 1.0 / a_change;
+
+    if (b_change < 1.0)
+	b_change = 1.0 / b_change;
+
+    /* Reverse sort so larger changes come first */
+    if (a_change > b_change)
+	return -1;
+    if (a_change < b_change)
+	return 1;
+    return 0;
+}
+
+static int
+test_report_cmp_backend_then_name (const void *a, const void *b)
+{
+    const test_report_t *a_test = a;
+    const test_report_t *b_test = b;
+    int cmp;
+
+    cmp = strcmp (a_test->backend, b_test->backend);
+    if (cmp)
+	return cmp;
+
+    cmp = strcmp (a_test->content, b_test->content);
+    if (cmp)
+	return cmp;
+
+    cmp = strcmp (a_test->name, b_test->name);
+    if (cmp)
+	return cmp;
+
+    if (a_test->size < b_test->size)
+	return -1;
+    if (a_test->size > b_test->size)
+	return 1;
+    return 0;
+}
+
+static void
+cairo_perf_report_sort_by_backend_then_name (cairo_perf_report_t *report)
+{
+   qsort (report->tests, report->tests_count, sizeof (test_report_t),
+	   test_report_cmp_backend_then_name);
+}
+
+#define CHANGE_BAR_WIDTH 70
+static void
+print_change_bar (double change, double max_change)
+{
+    int units_per_cell = (int) ceil (max_change / CHANGE_BAR_WIDTH);
+
+    while (change > units_per_cell) {
+	printf("â–ˆ");
+	change -= units_per_cell;
+    }
+
+    change /= units_per_cell;
+
+    if (change > 7.5/8.0)
+	printf("â–ˆ");
+    else if (change > 6.5/8.0)
+	printf("â–‰");
+    else if (change > 5.5/8.0)
+	printf("â–Š");
+    else if (change > 4.5/8.0)
+	printf("â–‹");
+    else if (change > 3.5/8.0)
+	printf("▌");
+    else if (change > 2.5/8.0)
+	printf("▍");
+    else if (change > 1.5/8.0)
+	printf("â–Ž");
+    else if (change > 0.5/8.0)
+	printf("▏");
+
+    printf ("\n");
+}
+
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+static void
+cairo_perf_report_diff (cairo_perf_report_t	*old,
+			cairo_perf_report_t	*new,
+			double			 min_change)
+{
+    int i, i_old, i_new;
+    test_report_t *o, *n;
+    double o_min, o_max, n_min, n_max;
+    int cmp;
+    test_diff_t *diff, *diffs;
+    int num_diffs = 0;
+    int printed_speedup = 0, printed_slowdown = 0;
+    double change, max_change;
+
+    diffs = malloc (MAX (old->tests_count, new->tests_count) * sizeof (test_diff_t));
+    if (diffs == NULL) {
+	fprintf (stderr, "Out of memory.\n");
+	exit (1);
+    }
+
+    cairo_perf_report_sort_by_backend_then_name (old);
+    cairo_perf_report_sort_by_backend_then_name (new);
+
+    i_old = 0;
+    i_new = 0;
+    while (i_old < old->tests_count && i_new < new->tests_count) {
+	o = &old->tests[i_old];
+	n = &new->tests[i_new];
+	cmp = test_report_cmp_backend_then_name (o, n);
+	if (cmp < 0) {
+	    fprintf (stderr, "Only in old: %s %s\n", o->backend, o->name);
+	    i_old++;
+	    continue;
+	}
+	if (cmp > 0) {
+	    fprintf (stderr, "Only in new: %s %s\n", n->backend, n->name);
+	    i_new++;
+	    continue;
+	}
+
+	/* Discard as uninteresting a change which doesn't separate
+	 * the means by a few standard deviations in each direction,
+	 * (that is, require the bulk of each curve to be
+	 * non-overlapping). */
+	o_min = o->ticks * (1 - 3 * o->std_dev);
+	o_max = o->ticks * (1 + 3 * o->std_dev);
+	n_min = n->ticks * (1 - 3 * n->std_dev);
+	n_max = n->ticks * (1 + 3 * n->std_dev);
+	if (n_min > o_max ||
+	    n_max < o_min)
+	{
+	    diffs[num_diffs].old = o;
+	    diffs[num_diffs].new = n;
+	    diffs[num_diffs].speedup = o->ticks / n->ticks;
+	    num_diffs++;
+	}
+
+	i_old++;
+	i_new++;
+    }
+
+    qsort (diffs, num_diffs, sizeof (test_diff_t), test_diff_cmp);
+
+    max_change = 1.0;
+    for (i = 0; i < num_diffs; i++) {
+	change = diffs[i].speedup;
+	if (change < 1.0)
+	    change = 1.0 / change;
+	if (change > max_change)
+	    max_change = change;
+    }
+
+    for (i = 0; i < num_diffs; i++) {
+	diff = &diffs[i];
+
+	change = diff->speedup;
+	if (change < 1.0)
+	    change = 1.0 / change;
+
+	/* Discard as uninteresting a change which is less than the
+	 * minimum change required, (default may be overriden on
+	 * command-line). */
+	if (change - 1.0 < min_change)
+	    continue;
+
+	if (diff->speedup > 1.0 && ! printed_speedup) {
+	    printf ("Speedups\n"
+		    "========\n");
+	    printed_speedup = 1;
+	}
+	if (diff->speedup < 1.0 && ! printed_slowdown) {
+	    printf ("Slowdowns\n"
+		    "=========\n");
+	    printed_slowdown = 1;
+	}
+
+	printf ("%5s-%-4s %26s-%-3d  %6.2f %4.2f%% -> %6.2f %4.2f%%: %5.2fx ",
+		diff->old->backend, diff->old->content,
+		diff->old->name, diff->old->size,
+		diff->old->time, diff->old->std_dev * 100,
+		diff->new->time, diff->new->std_dev * 100,
+		change);
+
+	if (diff->speedup > 1.0)
+	    printf ("faster\n");
+	else
+	    printf ("slower\n");
+
+	print_change_bar (change, max_change);
+    }
+
+    free (diffs);
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr, "Usage: %s file1 file2 [minimum_significant_change[%%]]\n", argv0);
+    fprintf (stderr,
+	     "Computes significant performance differences for cairo performance reports.\n"
+	     "Each file should be the output of the cairo-perf program (or \"make perf\").\n"
+	     "The third argument is used to supress all changes below some threshold.\n"
+	     "The default value of 5%% ignores any speeedup or slowdown of 5%% or less,\n"
+	     "A value of 0 will cause all output to be reported.\n");
+}
+
+int
+main (int argc, const char *argv[])
+{
+    const char *old_filename, *new_filename;
+    cairo_perf_report_t old, new;
+    double min_change;
+    char *end;
+
+    if (argc < 3) {
+	usage (argv[0]);
+	return 1;
+    }
+
+    old_filename = argv[1];
+    new_filename = argv[2];
+
+    min_change = 0.05;
+    if (argc >= 4) {
+	min_change = strtod (argv[3], &end);
+	if (*end && *end == '%')
+	    min_change = min_change / 100.0;
+    }
+
+    cairo_perf_report_load (&old, old_filename);
+    cairo_perf_report_load (&new, new_filename);
+
+    cairo_perf_report_diff (&old, &new, min_change);
+
+    return 0;
+}
+
diff-tree 5492a7c7618a747130b844a509fb79b886f1bc54 (from 8744929030ed8d42c271d9abb202975f62de166c)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Sep 21 15:15:02 2006 -0700

    Add assert statement so the infinite-join test simply exits rather than looping infinitely.

diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index 87de9a4..3d42314 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -315,6 +315,8 @@ _cairo_pen_find_active_cw_vertex_index (
 	    break;
     }
 
+    assert (i < pen->num_vertices);
+
     *active = i;
 
     return CAIRO_STATUS_SUCCESS;
diff-tree 8744929030ed8d42c271d9abb202975f62de166c (from 303b52919519854b9b5bbc38a9ac115e422dddad)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Sep 21 15:13:47 2006 -0700

    Add test case from bug #8379 demonstrating infinite loop during round join

diff --git a/test/Makefile.am b/test/Makefile.am
index d1706ac..1630a4e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -40,6 +40,7 @@ glyph-cache-pressure		\
 get-and-set			\
 get-group-target		\
 gradient-alpha			\
+infinite-join			\
 leaky-dash			\
 leaky-polygon			\
 line-width			\
diff --git a/test/infinite-join.c b/test/infinite-join.c
new file mode 100644
index 0000000..fffb386
--- /dev/null
+++ b/test/infinite-join.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of the
+ * copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+/* Test case for bug #8379:
+ *
+ *	infinite loop when stroking
+ *	https://bugs.freedesktop.org/show_bug.cgi?id=8379
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+    "infinite-join",
+    "Test case for infinite loop when stroking with round joins",
+    120, 70,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+
+    /* scaling 2 times causes a slight rounding error in the ctm.
+     * Without that, the bug doesn't happen. */
+    cairo_scale (cr, 20 / 100., 20 / 100.);
+    cairo_scale (cr, 1. / 20, 1. / 20);
+
+    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+    cairo_set_line_width (cr, 20);
+
+    cairo_new_path (cr);
+    cairo_move_to (cr, 18928, 13843);
+    cairo_line_to (cr, -6928, 13843);
+    cairo_line_to (cr, -6928, -6842);
+    cairo_line_to (cr, 18928, -6842);
+    cairo_line_to (cr, 18928, 13843);
+    cairo_stroke (cr);
+
+    return 0;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}


More information about the cairo-commit mailing list