[cairo-commit] 49 commits - build/configure.ac.analysis configure.ac perf/cairo-perf.c perf/cairo-perf-diff perf/cairo-perf-diff-files.c perf/cairo-perf-graph perf/cairo-perf-graph-files.c perf/cairo-perf-graph.h perf/cairo-perf-graph-widget.c perf/cairo-perf.h perf/cairo-perf-report.c perf/cairo-stats.h perf/.gitignore perf/Makefile.am perf/Makefile.win32 src/cairo-analysis-surface.c src/cairo-directfb-surface.c src/cairo-ft-font.c src/cairo-glitz-surface.c src/cairo-gstate.c src/cairoint.h src/cairo-meta-surface.c src/cairo-mutex-impl-private.h src/cairo-mutex-type-private.h src/cairo-output-stream.c src/cairo-paginated-surface.c src/cairo-pdf-surface.c src/cairo-ps-surface.c src/cairo-quartz-surface.c src/cairo-scaled-font.c src/cairo-scaled-font-subsets.c src/cairo-surface.c src/cairo-svg-surface.c src/cairo-type1-fallback.c src/cairo-type1-subset.c src/cairo-type3-glyph-surface.c src/cairo-user-font.c src/cairo-win32-surface.c src/cairo-xcb-surface.c src/cairo-xlib-surface.c src/test-paginated-surface.c test/bitmap-font.c test/cairo-test.c test/cairo-test.h test/clip-operator.c test/composite-integer-translate-over-repeat.c test/font-matrix-translation.c test/ft-text-antialias-none.c test/ft-text-vertical-layout-type1.c test/ft-text-vertical-layout-type3.c test/get-clip.c test/mask.c test/README test/show-glyphs-many.c test/smask.c test/smask-fill.c test/smask-image-mask.c test/smask-mask.c test/smask-paint.c test/smask-stroke.c test/smask-text.c test/source-clip.c test/source-clip-scale.c test/stroke-image.c test/surface-pattern-big-scale-down.c test/surface-pattern.c test/user-font.c test/user-font-proxy.c test/user-font-rescale.c

Chris Wilson ickle at kemper.freedesktop.org
Sun Oct 19 01:37:20 PDT 2008


 build/configure.ac.analysis                    |   17 
 configure.ac                                   |    3 
 perf/.gitignore                                |    1 
 perf/Makefile.am                               |   26 -
 perf/Makefile.win32                            |    6 
 perf/cairo-perf-diff                           |   12 
 perf/cairo-perf-diff-files.c                   |  458 -------------------
 perf/cairo-perf-graph                          |  205 ++++++++
 perf/cairo-perf-graph-files.c                  |  593 +++++++++++++++++++++++++
 perf/cairo-perf-graph-widget.c                 |  450 ++++++++++++++++++
 perf/cairo-perf-graph.h                        |   63 ++
 perf/cairo-perf-report.c                       |  456 +++++++++++++++++++
 perf/cairo-perf.c                              |    1 
 perf/cairo-perf.h                              |   61 ++
 perf/cairo-stats.h                             |    8 
 src/cairo-analysis-surface.c                   |   16 
 src/cairo-directfb-surface.c                   |   18 
 src/cairo-ft-font.c                            |   10 
 src/cairo-glitz-surface.c                      |   17 
 src/cairo-gstate.c                             |   33 -
 src/cairo-meta-surface.c                       |    2 
 src/cairo-mutex-impl-private.h                 |   14 
 src/cairo-mutex-type-private.h                 |    6 
 src/cairo-output-stream.c                      |    6 
 src/cairo-paginated-surface.c                  |    1 
 src/cairo-pdf-surface.c                        |    2 
 src/cairo-ps-surface.c                         |    4 
 src/cairo-quartz-surface.c                     |    4 
 src/cairo-scaled-font-subsets.c                |   23 
 src/cairo-scaled-font.c                        |   70 +-
 src/cairo-surface.c                            |  102 ++--
 src/cairo-svg-surface.c                        |    2 
 src/cairo-type1-fallback.c                     |    2 
 src/cairo-type1-subset.c                       |    5 
 src/cairo-type3-glyph-surface.c                |   50 +-
 src/cairo-user-font.c                          |  110 +++-
 src/cairo-win32-surface.c                      |   16 
 src/cairo-xcb-surface.c                        |    2 
 src/cairo-xlib-surface.c                       |   24 -
 src/cairoint.h                                 |    2 
 src/test-paginated-surface.c                   |   16 
 test/README                                    |    9 
 test/bitmap-font.c                             |    8 
 test/cairo-test.c                              |  285 +++++++++---
 test/cairo-test.h                              |   14 
 test/clip-operator.c                           |    6 
 test/composite-integer-translate-over-repeat.c |    7 
 test/font-matrix-translation.c                 |    5 
 test/ft-text-antialias-none.c                  |   31 +
 test/ft-text-vertical-layout-type1.c           |   31 +
 test/ft-text-vertical-layout-type3.c           |   31 +
 test/get-clip.c                                |   98 ++--
 test/mask.c                                    |   12 
 test/show-glyphs-many.c                        |   11 
 test/smask-fill.c                              |   13 
 test/smask-image-mask.c                        |   12 
 test/smask-mask.c                              |   46 -
 test/smask-paint.c                             |    6 
 test/smask-stroke.c                            |   13 
 test/smask-text.c                              |   12 
 test/smask.c                                   |    6 
 test/source-clip-scale.c                       |    8 
 test/source-clip.c                             |    4 
 test/stroke-image.c                            |   19 
 test/surface-pattern-big-scale-down.c          |   43 +
 test/surface-pattern.c                         |    7 
 test/user-font-proxy.c                         |   69 +-
 test/user-font-rescale.c                       |   63 +-
 test/user-font.c                               |   24 -
 69 files changed, 2852 insertions(+), 958 deletions(-)

New commits:
commit f2ff7944264c23cbec856be3e85f240a93184f80
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 11:56:19 2008 +0100

    [perf] A crude tool to visualise performance changes across a series.
    
    Generate a cairo-perf-diff graph for a series of commits in order to be
    able to identify significant commits. Still very crude, but minimally
    functional.

diff --git a/configure.ac b/configure.ac
index 7385f0e..c75ae93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -480,6 +480,9 @@ fi
 
 dnl ===========================================================================
 
+# We use GTK+ for some utility/debugging tools
+PKG_CHECK_MODULES(gtk, "gtk+-2.0",, AC_MSG_RESULT(no))
+
 AC_CONFIG_FILES([
 Makefile
 boilerplate/Makefile
diff --git a/perf/.gitignore b/perf/.gitignore
index 858e28a..29ec88d 100644
--- a/perf/.gitignore
+++ b/perf/.gitignore
@@ -2,6 +2,7 @@ TAGS
 tags
 cairo-perf
 cairo-perf-diff-files
+cairo-perf-graph-files
 valgrind-log
 callgrind.out.*
 index.html
diff --git a/perf/Makefile.am b/perf/Makefile.am
index 1cdcf53..512e6d1 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -7,15 +7,19 @@ AM_CPPFLAGS =					\
 	-I$(top_builddir)/src			\
 	$(CAIRO_CFLAGS)
 
-EXTRA_PROGRAMS += cairo-perf cairo-perf-diff-files
+EXTRA_PROGRAMS += cairo-perf cairo-perf-diff-files cairo-perf-graph-files
 EXTRA_DIST += cairo-perf-diff
+EXTRA_LTLIBRARIES += libcairoperf.la
+
+LDADD = $(top_builddir)/boilerplate/libcairoboilerplate.la \
+	$(top_builddir)/src/libcairo.la \
+	libcairoperf.la \
+	$(CAIROPERF_LIBS)
 
 cairo_perf_SOURCES =		\
 	cairo-perf.c		\
 	cairo-perf.h		\
 	cairo-perf-cover.c	\
-	cairo-stats.c		\
-	cairo-stats.h		\
 	box-outline.c		\
 	composite-checker.c	\
 	fill.c			\
@@ -48,14 +52,20 @@ cairo_perf_SOURCES += cairo-perf-posix.c
 endif
 endif
 
-cairo_perf_diff_files_SOURCES =	\
-	cairo-perf-diff-files.c \
+libcairoperf_la_SOURCES = \
+	cairo-perf-report.c	\
 	cairo-stats.c		\
 	cairo-stats.h
 
-LDADD = $(top_builddir)/boilerplate/libcairoboilerplate.la \
-	$(top_builddir)/src/libcairo.la \
-	$(CAIROPERF_LIBS)
+cairo_perf_diff_files_SOURCES =	\
+	cairo-perf-diff-files.c
+
+cairo_perf_graph_files_SOURCES = \
+	cairo-perf-graph.h \
+	cairo-perf-graph-files.c \
+	cairo-perf-graph-widget.c
+cairo_perf_graph_files_CFLAGS = @gtk_CFLAGS@
+cairo_perf_graph_files_LDADD = @gtk_LIBS@ $(LDADD)
 
 $(top_builddir)/boilerplate/libcairoboilerplate.la: $(top_builddir)/src/libcairo.la
 	cd $(top_builddir)/boilerplate && $(MAKE) $(AM_MAKEFLAGS) libcairoboilerplate.la
diff --git a/perf/Makefile.win32 b/perf/Makefile.win32
index 47e73c4..ef993a7 100644
--- a/perf/Makefile.win32
+++ b/perf/Makefile.win32
@@ -37,4 +37,8 @@ $(CFG)/cairo-perf.exe: $(OBJECTS)
 
 cairo-perf-diff-files:
 	@mkdir -p $(CFG)
-	@$(CC) $(CFLAGS) -Fe"$@" cairo-perf-diff-files.c cairo-stats.c -link $(LDFLAGS)
+	@$(CC) $(CFLAGS) -Fe"$@" cairo-perf-diff-files.c cairo-perf-report.c cairo-stats.c -link $(LDFLAGS)
+
+cairo-perf-graph-files:
+	@mkdir -p $(CFG)
+	@$(CC) $(CFLAGS) -Fe"$@" cairo-perf-graph-files.c cairo-perf-report.c cairo-stats.c -link $(LDFLAGS)
diff --git a/perf/cairo-perf-diff b/perf/cairo-perf-diff
index 5094ec3..50cd1f2 100755
--- a/perf/cairo-perf-diff
+++ b/perf/cairo-perf-diff
@@ -121,9 +121,16 @@ cpu_count() {
 # results from a run with an equivalent src tree.
 rev2perf() {
     rev=$1
+    sha=`rev2sha $rev`
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    echo "$CAIRO_PERF_DIR/${sha}-${perf_tree_sha}-${src_tree_sha}.perf"
+}
+rev2perf_glob() {
+    rev=$1
     src_tree_sha=`rev2sha $rev:src`
     perf_tree_sha=`rev2sha HEAD:perf`
-    echo "$CAIRO_PERF_DIR/${perf_tree_sha}-${src_tree_sha}.perf"
+    echo "$CAIRO_PERF_DIR/*-${perf_tree_sha}-${src_tree_sha}.perf"
 }
 
 # Usage: run_cairo_perf_if_not_cached <rev> <suffix>
@@ -138,7 +145,8 @@ run_cairo_perf_if_not_cached() {
     owd=`pwd`
     sha=`rev2sha $rev`
     perf=`rev2perf $rev`
-    if [ -e $perf ] && [ "$force_cairo_perf" != "true" ]; then
+    glob=`rev2perf_glob $rev`
+    if [ -e $glob ] && [ "$force_cairo_perf" != "true" ]; then
 	return 0
     fi
     if [ ! -d $CAIRO_PERF_DIR ]; then
diff --git a/perf/cairo-perf-diff-files.c b/perf/cairo-perf-diff-files.c
index 9098c91..9b9f5f9 100644
--- a/perf/cairo-perf-diff-files.c
+++ b/perf/cairo-perf-diff-files.c
@@ -27,10 +27,6 @@
 
 #include "cairo-perf.h"
 
-/* We use _GNU_SOURCE for getline and strndup if available. */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-#endif
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,50 +34,6 @@
 #include <ctype.h>
 #include <math.h>
 #include <assert.h>
-#ifdef HAVE_LIBGEN_H
-#include <libgen.h>
-#endif
-
-typedef struct _test_report {
-    int id;
-    const char *configuration;
-    char *backend;
-    char *content;
-    char *name;
-    int size;
-
-    /* The samples only exists for "raw" reports */
-    cairo_perf_ticks_t *samples;
-    unsigned int samples_size;
-    unsigned int samples_count;
-
-    /* The stats are either read directly or computed from samples.
-     * If the stats have not yet been computed from samples, then
-     * iterations will be 0. */
-    cairo_stats_t stats;
-} test_report_t;
-
-typedef struct _test_diff {
-    test_report_t **tests;
-    int num_tests;
-    double min;
-    double max;
-    double change;
-} test_diff_t;
-
-typedef struct _cairo_perf_report {
-    char *configuration;
-    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;
 
 typedef struct _cairo_perf_report_options {
     double min_change;
@@ -96,416 +48,6 @@ typedef struct _cairo_perf_diff_files_args {
     cairo_perf_report_options_t options;
 } cairo_perf_diff_files_args_t;
 
-/* 'ssize_t' does not exist in the C standard on win32. 
- * We use 'ptrdiff_t', which is nearly equivalent. */
-#ifdef _MSC_VER
-typedef ptrdiff_t ssize_t;
-#endif
-
-#ifndef __USE_GNU
-static ssize_t
-getline (char **lineptr, size_t *n, FILE *stream);
-
-static char *
-strndup (const char *s, size_t n);
-#endif
-
-#ifdef _MSC_VER
-static long long
-strtoll(const char *nptr, char **endptr, int base);
-
-static char *
-basename(char *path);
-#endif
-
-/* 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_long_long(result)						\
-do {									\
-    (result) = strtoll (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 *configuration)
-{
-    char *end;
-    char *s = line;
-    cairo_bool_t is_raw = FALSE;
-    double min_time, median_time;
-
-    /* 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;
-    if (*s == '*') {
-	s++;
-	is_raw = TRUE;
-    } else {
-	parse_int (report->id);
-    }
-    skip_char (']');
-
-    skip_space ();
-
-    report->configuration = configuration;
-    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 ();
-
-    report->samples = NULL;
-    report->samples_size = 0;
-    report->samples_count = 0;
-
-    if (is_raw) {
-	parse_double (report->stats.ticks_per_ms);
-	skip_space ();
-
-	report->samples_size = 5;
-	report->samples = xmalloc (report->samples_size * sizeof (cairo_perf_ticks_t));
-	do {
-	    if (report->samples_count == report->samples_size) {
-		report->samples_size *= 2;
-		report->samples = xrealloc (report->samples,
-					    report->samples_size * sizeof (cairo_perf_ticks_t));
-	    }
-	    parse_long_long (report->samples[report->samples_count++]);
-	    skip_space ();
-	} while (*s && *s != '\n');
-	report->stats.iterations = 0;
-	skip_char ('\n');
-    } else {
-	parse_double (report->stats.min_ticks);
-	skip_space ();
-
-	parse_double (min_time);
-	report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
-
-	skip_space ();
-
-	parse_double (median_time);
-	report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
-
-	skip_space ();
-
-	parse_double (report->stats.std_dev);
-	report->stats.std_dev /= 100.0;
-	skip_char ('%');
-
-	skip_space ();
-
-	parse_int (report->stats.iterations);
-
-	skip_space ();
-	skip_char ('\n');
-    }
-
-    return TEST_REPORT_STATUS_SUCCESS;
-}
-
-/* We conditionally provide a custom implementation of getline and strndup
- * as needed. These aren't necessary full-fledged general purpose
- * implementations. They just get the job done for our purposes.
- */
-#ifndef __USE_GNU
-#define POORMANS_GETLINE_BUFFER_SIZE (65536)
-static ssize_t
-getline (char **lineptr, size_t *n, FILE *stream)
-{
-    if (!*lineptr)
-    {
-        *n = POORMANS_GETLINE_BUFFER_SIZE;
-        *lineptr = (char *) malloc (*n);
-    }
-
-    if (!fgets (*lineptr, *n, stream))
-        return -1;
-
-    if (!feof (stream) && !strchr (*lineptr, '\n'))
-    {
-        fprintf (stderr, "The poor man's implementation of getline in "
-                          __FILE__ " needs a bigger buffer. Perhaps it's "
-                         "time for a complete implementation of getline.\n");
-        exit (0);
-    }
-
-    return strlen (*lineptr);
-}
-#undef POORMANS_GETLINE_BUFFER_SIZE
-
-static char *
-strndup (const char *s, size_t n)
-{
-    size_t len;
-    char *sdup;
-
-    if (!s)
-        return NULL;
-
-    len = strlen (s);
-    len = (n < len ? n : len);
-    sdup = (char *) malloc (len + 1);
-    if (sdup)
-    {
-        memcpy (sdup, s, len);
-        sdup[len] = '\0';
-    }
-
-    return sdup;
-}
-#endif /* ifndef __USE_GNU */
-
-/* We provide hereafter a win32 implementation of the basename
- * and strtoll functions which are not available otherwise.
- * The basename function is fully compliant to its GNU specs.
- */
-#ifdef _MSC_VER
-long long
-strtoll(const char *nptr, char **endptr, int base)
-{
-    return _atoi64(nptr);
-}
-
-static char *
-basename(char *path)
-{
-    char *end, *s;
-
-    end = (path + strlen(path) - 1);
-    while (end && (end >= path + 1) && (*end == '/')) {
-	*end = '\0';
-	end--;
-    }
-
-    s = strrchr(path, '/');
-    if (s) {
-	if (s == end) {
-	    return s;
-	} else {
-	    return s+1;
-	}
-    } else {
-	return path;
-    }
-}
-#endif /* ifndef _MSC_VER */
-
-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;
-
-    /* A NULL name is a list-termination marker, so force it last. */
-    if (a_test->name == NULL)
-	if (b_test->name == NULL)
-	    return 0;
-	else
-	    return 1;
-    else if (b_test->name == NULL)
-	return -1;
-
-    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_and_compute_stats (cairo_perf_report_t *report)
-{
-    test_report_t *base, *next, *last, *t;
-
-    /* First we sort, since the diff needs both lists in the same
-     * order */
-    qsort (report->tests, report->tests_count, sizeof (test_report_t),
-	   test_report_cmp_backend_then_name);
-
-    /* The sorting also brings all related raw reports together so we
-     * can condense them and compute the stats.
-     */
-    base = &report->tests[0];
-    last = &report->tests[report->tests_count - 1];
-    while (base <= last) {
-	next = base+1;
-	if (next <= last) {
-	    while (next <= last &&
-		   test_report_cmp_backend_then_name (base, next) == 0)
-	    {
-		next++;
-	    }
-	    if (next != base) {
-		unsigned int new_samples_count = base->samples_count;
-		for (t = base + 1; t < next; t++)
-		    new_samples_count += t->samples_count;
-		if (new_samples_count > base->samples_size) {
-		    base->samples_size = new_samples_count;
-		    base->samples = xrealloc (base->samples,
-					      base->samples_size * sizeof (cairo_perf_ticks_t));
-		}
-		for (t = base + 1; t < next; t++) {
-		    memcpy (&base->samples[base->samples_count], t->samples,
-			    t->samples_count * sizeof (cairo_perf_ticks_t));
-		    base->samples_count += t->samples_count;
-		}
-	    }
-	}
-	if (base->samples)
-	    _cairo_stats_compute (&base->stats, base->samples, base->samples_count);
-	base = next;
-    }
-}
-
-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;
-    char *configuration;
-    char *dot;
-    char *baseName;
-
-    configuration = xmalloc (strlen (filename) * sizeof (char) + 1);
-    strcpy (configuration, filename);
-    baseName = strdup (basename (configuration));
-    report->configuration = xmalloc (strlen (filename) * sizeof (char) + 1);
-    strcpy(report->configuration, baseName);
-    free (configuration);
-    dot = strrchr (report->configuration, '.');
-    if (dot)
-	*dot = '\0';
-
-    report->name = filename;
-    report->tests_size = 16;
-    report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
-    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 *= 2;
-	    report->tests = xrealloc (report->tests,
-				      report->tests_size * sizeof (test_report_t));
-	}
-
-	line_number++;
-	if (getline (&line, &line_size, file) == -1)
-	    break;
-
-	status = test_report_parse (&report->tests[report->tests_count],
-				    line, report->configuration);
-	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);
-
-    fclose (file);
-
-    cairo_perf_report_sort_and_compute_stats (report);
-
-    /* Add one final report with a NULL name to terminate the list. */
-    if (report->tests_count == report->tests_size) {
-	report->tests_size *= 2;
-	report->tests = xrealloc (report->tests,
-				  report->tests_size * sizeof (test_report_t));
-    }
-    report->tests[report->tests_count].name = NULL;
-}
-
 static int
 test_diff_cmp_speedup_before_slowdown (const void *a, const void *b)
 {
diff --git a/perf/cairo-perf-graph b/perf/cairo-perf-graph
new file mode 100755
index 0000000..4d25953
--- /dev/null
+++ b/perf/cairo-perf-graph
@@ -0,0 +1,205 @@
+#!/bin/sh
+set -e
+
+###
+### XXX Source common functions from cairo-perf-diff
+###
+
+usage() {
+    argv0=`basename $0`
+
+    cat >&2 << END
+Usage:
+As opposed to its sibling, cairo-perf-diff, cairo-perf-graph targets
+reviewing changes between series by graphically comparing the performance
+at each commit.
+
+The two revisions can be any revision accepted by git. For example:
+
+	$argv0 1.2.0 1.2.4	# Compare performance of 1.2.0 to 1.2.4
+
+Options:
+
+-f, --force
+	Forces cairo-perf-diff to re-run performance tests
+	even if cached performance data is available.
+
+-h, --html
+	With this option performance changes are summarized
+	as HTML table.
+
+Additional options can be passed the child cairo-perf process
+by separating them with a double hyphen (--). For example, to
+examine what the impact of the latest change is on the stroke
+test you might use:
+
+	$argv0 HEAD -- stroke
+
+The performance results are cached in .perf next to the .git directory.
+
+Set CAIRO_AUTOGEN_OPTIONS to pass options to autogen for both
+builds.
+END
+
+    exit 1
+}
+
+# First, pull off any known options
+while true; do
+    case $1 in
+        -f|--force) force_cairo_perf="true";;
+        -h|--html) html_output="true";;
+        *) break;;
+    esac
+
+    shift
+done
+
+# Then if anything is left that still looks like an option, (begins
+# with a dash), give usage to catch --help or any other -garbage
+if [ $# -eq 0 ] || [ "`echo "$1" | sed 's/^-//'`" != "$1" ]; then
+    usage
+fi
+
+# Finally, pick up the actual revision arguments
+old="$1"
+new="$2"
+shift 2
+
+# And post-finally, pass anything after -- on to cairo-perf
+CAIRO_PERF_OPTIONS="-r -i 25"
+if [ $# -gt 0 ]; then
+    if [ "$1" = "--" ]; then
+	shift 1
+	CAIRO_PERF_OPTIONS="$CAIRO_PERF_OPTIONS $@"
+    else
+	usage
+    fi
+fi
+
+git_setup() {
+    SUBDIRECTORY_OK='Yes'
+    . git-sh-setup
+    CAIRO_DIR=`dirname $GIT_DIR`
+    if [ "$CAIRO_DIR" = "." ]; then
+	CAIRO_DIR=`pwd`
+    fi
+    CAIRO_PERF_DIR=$CAIRO_DIR/.perf
+}
+
+rev2sha() {
+    rev=$1
+    git rev-parse --verify $rev || ( echo "Cannot resolve $rev as a git object" && exit 1 )
+}
+
+cpu_count() {
+    test -f /proc/cpuinfo &&
+    grep -c '^processor[[:blank:]]\+:' /proc/cpuinfo ||
+    echo 1
+}
+
+# We cache performance output based on a two-part name capturing the
+# current performance test suite and the library being tested. We
+# capture these as the tree object of the perf directory in HEAD and
+# the tree object of the src directory of the revision being tested.
+#
+# This way, whenever the performance suite is updated, cached output
+# from old versions of the suite are automatically invalidated. Also,
+# if a commit just changes things outside of the src tree, (say it
+# changes the "test" test suite, or README or configure.in, or
+# whatever), cairo-perf-diff will be smart enough to still use cached
+# results from a run with an equivalent src tree.
+rev2perf() {
+    rev=$1
+    sha=`rev2sha $rev`
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    echo "$CAIRO_PERF_DIR/${sha}-${perf_tree_sha}-${src_tree_sha}.perf"
+}
+rev2perf_glob() {
+    rev=$1
+    src_tree_sha=`rev2sha $rev:src`
+    perf_tree_sha=`rev2sha HEAD:perf`
+    echo "$CAIRO_PERF_DIR/*-${perf_tree_sha}-${src_tree_sha}.perf"
+}
+
+# Usage: run_cairo_perf_if_not_cached <rev> <suffix>
+# The <rev> argument must be a valid git ref-spec that can
+# be resolved to a commit. The suffix is just something
+# unique so that build directories can be separated for
+# multiple calls to this function.
+run_cairo_perf_if_not_cached() {
+    rev=$1
+    build_dir="build-$2"
+
+    owd=`pwd`
+    sha=`rev2sha $rev`
+    perf=`rev2perf $rev`
+    glob=`rev2perf_glob $rev`
+    if [ -e $glob ] && [ "$force_cairo_perf" != "true" ]; then
+	return 0
+    fi
+    if [ ! -d $CAIRO_PERF_DIR ]; then
+	echo "Creating new perf cache in $CAIRO_PERF_DIR"
+	mkdir $CAIRO_PERF_DIR
+    fi
+
+    cd $CAIRO_DIR
+    boilerplate_files=`git ls-tree --name-only HEAD boilerplate/*`
+    perf_files=`git ls-tree --name-only HEAD perf/*`
+    cd $CAIRO_PERF_DIR
+
+    if [ ! -d $build_dir ]; then
+	git clone -s $CAIRO_DIR $build_dir
+	(cd $build_dir; git checkout -b tmp-cairo-perf-diff $sha)
+    fi
+    cd $build_dir
+
+    git checkout tmp-cairo-perf-diff
+    git reset --hard $sha
+
+    if [ -z "$MAKEFLAGS" ]; then
+	CPU_COUNT=`cpu_count`
+        export MAKEFLAGS="-j`expr $CPU_COUNT + 1`"
+    fi
+
+    if [ ! -e Makefile ]; then
+	CFLAGS="-O2" ./autogen.sh $CAIRO_AUTOGEN_OPTIONS
+    fi
+    make CFLAGS="-O2" || (rm config.cache && make CFLAGS="-O2")
+    for file in $boilerplate_files; do
+	rsync $CAIRO_DIR/$file boilerplate
+    done
+    (cd boilerplate; make)
+    for file in $perf_files; do
+	rsync $CAIRO_DIR/$file perf
+    done
+    cd perf;
+    make cairo-perf || exit 1
+    echo "Running \"cairo-perf $CAIRO_PERF_OPTIONS\" against $rev. Results will be cached in:"
+    echo "$perf"
+    (./cairo-perf $CAIRO_PERF_OPTIONS || echo "*** Performance test crashed") >> $perf
+    cd $owd
+}
+
+git_setup
+
+# Build cairo-perf-graph-files if not available
+if [ ! -e $CAIRO_DIR/perf/cairo-perf-graph-files ]; then
+    echo "Building cairo-perf-graph-files"
+    if [ "x$OS" = "xWindows_NT" ]; then
+        make -f Makefile.win32 -C $CAIRO_DIR/perf/ cairo-perf-graph-files CFG=debug
+    else
+        make -C $CAIRO_DIR/perf/ cairo-perf-graph-files
+    fi
+fi
+
+revs=""
+for rev in `git rev-list --reverse $old..$new`; do
+    run_cairo_perf_if_not_cached $rev rev
+    perf=`rev2perf $rev`
+    [ -e $perf ] && revs="$revs $perf"
+done
+
+exec $CAIRO_DIR/perf/cairo-perf-graph-files $revs
+#exec $CAIRO_DIR/libtool --mode=execute gdb --args $CAIRO_DIR/perf/cairo-perf-graph-files $revs
diff --git a/perf/cairo-perf-graph-files.c b/perf/cairo-perf-graph-files.c
new file mode 100644
index 0000000..5122b9f
--- /dev/null
+++ b/perf/cairo-perf-graph-files.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * 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: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+#include "cairo-perf-graph.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <cairo.h>
+
+static void
+usage (const char *argv0)
+{
+    char const *basename = strrchr (argv0, '/');
+    basename = basename ? basename+1 : argv0;
+    g_printerr ("Usage: %s [options] file1 file2 [...]\n\n", basename);
+    g_printerr ("Draws a graph illustrating the change in performance over a series.\n");
+    exit(1);
+}
+
+enum {
+    CASE_SHOWN,
+    CASE_INCONSISTENT,
+    CASE_BACKEND,
+    CASE_CONTENT,
+    CASE_NAME,
+    CASE_SIZE,
+    CASE_FG_COLOR,
+    CASE_DATA,
+    CASE_NCOLS
+};
+
+static GtkTreeStore *
+cases_to_store (test_case_t *cases)
+{
+    GtkTreeStore *store;
+    GtkTreeIter backend_iter;
+    GtkTreeIter content_iter;
+    const char *backend = NULL;
+    const char *content = NULL;
+
+    store = gtk_tree_store_new (CASE_NCOLS,
+	                        G_TYPE_BOOLEAN, /* shown */
+	                        G_TYPE_BOOLEAN, /* inconsistent */
+				G_TYPE_STRING, /* backend */
+				G_TYPE_STRING, /* content */
+				G_TYPE_STRING, /* name */
+				G_TYPE_INT, /* size */
+				GDK_TYPE_COLOR, /* fg color */
+				G_TYPE_POINTER); /* data */
+    while (cases->backend != NULL) {
+	GtkTreeIter iter;
+
+	if (backend == NULL || strcmp (backend, cases->backend)) {
+	    gtk_tree_store_append (store, &backend_iter, NULL);
+	    gtk_tree_store_set (store, &backend_iter,
+		                CASE_SHOWN, TRUE,
+				CASE_BACKEND, cases->backend,
+				-1);
+	    backend = cases->backend;
+	    content = NULL;
+	}
+	if (content == NULL || strcmp (content, cases->content)) {
+	    gtk_tree_store_append (store, &content_iter, &backend_iter);
+	    gtk_tree_store_set (store, &content_iter,
+		                CASE_SHOWN, TRUE,
+				CASE_BACKEND, cases->backend,
+				CASE_CONTENT, cases->content,
+				-1);
+	    content = cases->content;
+	}
+
+	gtk_tree_store_append (store, &iter, &content_iter);
+	gtk_tree_store_set (store, &iter,
+			    CASE_SHOWN, TRUE,
+			    CASE_BACKEND, cases->backend,
+			    CASE_CONTENT, cases->content,
+			    CASE_NAME, cases->name,
+			    CASE_SIZE, cases->size,
+			    CASE_FG_COLOR, &cases->color,
+			    CASE_DATA, cases,
+			    -1);
+	cases++;
+    }
+
+    return store;
+}
+
+struct _app_data {
+    GtkWidget *window;
+
+    test_case_t *cases;
+    cairo_perf_report_t *reports;
+    int num_reports;
+
+    GtkTreeStore *case_store;
+
+    GIOChannel *git_io;
+    GtkTextBuffer *git_buffer;
+
+    GtkWidget *gv;
+};
+
+static void
+recurse_set_shown (GtkTreeModel *model, GtkTreeIter *parent, gboolean shown)
+{
+    GtkTreeIter iter;
+
+    if (gtk_tree_model_iter_children (model, &iter, parent)) do {
+	test_case_t *c;
+
+	gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
+	if (c == NULL) {
+	    recurse_set_shown (model, &iter, shown);
+	} else if (shown != c->shown) {
+	    c->shown = shown;
+	    gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
+		                CASE_SHOWN, shown,
+				CASE_INCONSISTENT, FALSE,
+				-1);
+	}
+    } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static gboolean
+children_consistent (GtkTreeModel *model, GtkTreeIter *parent)
+{
+    GtkTreeIter iter;
+    gboolean first = TRUE;
+    gboolean first_active;
+
+    if (gtk_tree_model_iter_children (model, &iter, parent)) do {
+	gboolean active, inconsistent;
+
+	gtk_tree_model_get (model, &iter,
+		            CASE_INCONSISTENT, &inconsistent,
+			    CASE_SHOWN, &active,
+			    -1);
+	if (inconsistent)
+	    return FALSE;
+
+	if (first) {
+	    first_active = active;
+	    first = FALSE;
+	} else if (active != first_active)
+	    return FALSE;
+    } while (gtk_tree_model_iter_next (model, &iter));
+
+    return TRUE;
+}
+
+static void
+check_consistent (GtkTreeModel *model, GtkTreeIter *child)
+{
+    GtkTreeIter parent;
+
+    if (gtk_tree_model_iter_parent (model, &parent, child)) {
+	gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
+		            CASE_INCONSISTENT,
+			    ! children_consistent (model, &parent),
+			    -1);
+	check_consistent (model, &parent);
+    }
+}
+
+static void
+show_case_toggled (GtkCellRendererToggle *cell,
+	           gchar *str,
+		   struct _app_data *app)
+{
+    GtkTreeModel *model;
+    GtkTreePath *path;
+    GtkTreeIter iter;
+    test_case_t *c;
+    gboolean active;
+
+    active = ! gtk_cell_renderer_toggle_get_active (cell);
+
+    model = GTK_TREE_MODEL (app->case_store);
+
+    path = gtk_tree_path_new_from_string (str);
+    gtk_tree_model_get_iter (model, &iter, path);
+    gtk_tree_path_free (path);
+
+    gtk_tree_store_set (app->case_store, &iter,
+			CASE_SHOWN, active,
+			CASE_INCONSISTENT, FALSE,
+			-1);
+    gtk_tree_model_get (model, &iter, CASE_DATA, &c, -1);
+    if (c != NULL) {
+	if (active == c->shown)
+	    return;
+
+	c->shown = active;
+    } else {
+	recurse_set_shown (model, &iter, active);
+    }
+    check_consistent (model, &iter);
+
+    graph_view_update_visible ((GraphView *) app->gv);
+}
+
+static gboolean
+git_read (GIOChannel *io, GIOCondition cond, struct _app_data *app)
+{
+    int fd;
+
+    fd = g_io_channel_unix_get_fd (io);
+    do {
+	char buf[4096];
+	int len;
+	GtkTextIter end;
+
+	len = read (fd, buf, sizeof (buf));
+	if (len <= 0) {
+	    int err = len ? errno : 0;
+	    switch (err) {
+	    case EAGAIN:
+	    case EINTR:
+		return TRUE;
+	    default:
+		g_io_channel_unref (app->git_io);
+		app->git_io = NULL;
+		return FALSE;
+	    }
+	}
+
+	gtk_text_buffer_get_end_iter (app->git_buffer, &end);
+	gtk_text_buffer_insert (app->git_buffer, &end, buf, len);
+    } while (TRUE);
+}
+
+static void
+do_git (struct _app_data *app, char **argv)
+{
+    gint output;
+    GError *error = NULL;
+    GtkTextIter start, stop;
+    long flags;
+
+    if (! g_spawn_async_with_pipes (NULL, argv, NULL,
+				    G_SPAWN_SEARCH_PATH |
+				    G_SPAWN_STDERR_TO_DEV_NULL |
+				    G_SPAWN_FILE_AND_ARGV_ZERO,
+				    NULL, NULL, NULL,
+				    NULL, &output, NULL,
+				    &error))
+    {
+	g_error ("spawn failed: %s", error->message);
+    }
+
+    if (app->git_io) {
+	g_io_channel_shutdown (app->git_io, FALSE, NULL);
+	g_io_channel_unref (app->git_io);
+    }
+
+    gtk_text_buffer_get_bounds (app->git_buffer, &start, &stop);
+    gtk_text_buffer_delete (app->git_buffer, &start, &stop);
+
+    flags = fcntl (output, F_GETFL);
+    if ((flags & O_NONBLOCK) == 0)
+	fcntl (output, F_SETFL, flags | O_NONBLOCK);
+
+    app->git_io = g_io_channel_unix_new (output);
+    g_io_add_watch (app->git_io, G_IO_IN | G_IO_HUP, (GIOFunc) git_read, app);
+}
+
+static void
+gv_report_selected (GraphView *gv, int i, struct _app_data *app)
+{
+    cairo_perf_report_t *report;
+    char *hyphen;
+
+    if (i == -1)
+	return;
+
+    report = &app->reports[i];
+    hyphen = strchr (report->configuration, '-');
+    if (hyphen != NULL) {
+	int len = hyphen - report->configuration;
+	char *id = g_malloc (len + 1);
+	char *argv[5];
+
+	memcpy (id, report->configuration, len);
+	id[len] = '\0';
+
+	argv[0] = (char *) "git";
+	argv[1] = (char *) "git";
+	argv[2] = (char *) "show";
+	argv[3] = id;
+	argv[4] = NULL;
+
+	do_git (app, argv);
+	free (id);
+    }
+}
+
+static GtkWidget *
+window_create (test_case_t *cases,
+	       cairo_perf_report_t *reports,
+	       int num_reports)
+{
+    GtkWidget *window, *table, *w;
+    GtkWidget *tv, *sw;
+    GtkTreeStore *store;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *renderer;
+    struct _app_data *data;
+
+
+    data = g_new0 (struct _app_data, 1);
+    data->cases = cases;
+    data->reports = reports;
+    data->num_reports = num_reports;
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title (GTK_WINDOW (window), "Cairo Performance Graph");
+    g_object_set_data_full (G_OBJECT (window),
+	                    "app-data", data, (GDestroyNotify)g_free);
+
+    data->window = window;
+
+    table = gtk_table_new (2, 2, FALSE);
+
+    /* legend & show/hide lines (categorised) */
+    tv = gtk_tree_view_new ();
+    store = cases_to_store (cases);
+    data->case_store = store;
+    gtk_tree_view_set_model (GTK_TREE_VIEW (tv), GTK_TREE_MODEL (store));
+
+    renderer = gtk_cell_renderer_toggle_new ();
+    column = gtk_tree_view_column_new_with_attributes (NULL,
+	    renderer,
+	    "active", CASE_SHOWN,
+	    "inconsistent", CASE_INCONSISTENT,
+	    NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+    g_signal_connect (renderer, "toggled",
+	              G_CALLBACK (show_case_toggled), data);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Backend",
+	    renderer,
+	    "text", CASE_BACKEND,
+	    NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Content",
+	    renderer,
+	    "text", CASE_CONTENT,
+	    NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Test",
+	    renderer,
+	    "text", CASE_NAME,
+	    "foreground-gdk", CASE_FG_COLOR,
+	    NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    renderer = gtk_cell_renderer_text_new ();
+    column = gtk_tree_view_column_new_with_attributes ("Size",
+	    renderer,
+	    "text", CASE_SIZE,
+	    NULL);
+    gtk_tree_view_append_column (GTK_TREE_VIEW (tv), column);
+
+    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tv), TRUE);
+    g_object_unref (store);
+
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+	                            GTK_POLICY_NEVER,
+	                            GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (sw), tv);
+    gtk_widget_show (tv);
+    gtk_table_attach (GTK_TABLE (table), sw,
+	              0, 1, 0, 2,
+		      GTK_FILL, GTK_FILL,
+		      4, 4);
+    gtk_widget_show (sw);
+
+    /* the performance chart */
+    w = graph_view_new ();
+    data->gv = w;
+    g_signal_connect (w, "report-selected",
+	              G_CALLBACK (gv_report_selected), data);
+    graph_view_set_reports ((GraphView *)w, cases, reports, num_reports);
+    gtk_table_attach (GTK_TABLE (table), w,
+	              1, 2, 0, 1,
+		      GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND,
+		      4, 4);
+    gtk_widget_show (w);
+
+    /* interesting information - presumably the commit log */
+    w = gtk_text_view_new ();
+    data->git_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w));
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+	                            GTK_POLICY_NEVER,
+	                            GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (sw), w);
+    gtk_widget_show (w);
+    gtk_table_attach (GTK_TABLE (table), sw,
+	              1, 2, 1, 2,
+		      GTK_FILL, GTK_FILL | GTK_EXPAND,
+		      4, 4);
+    gtk_widget_show (sw);
+
+    gtk_container_add (GTK_CONTAINER (window), table);
+    gtk_widget_show (table);
+
+    return window;
+}
+
+static void
+name_to_color (const char *name, GdkColor *color)
+{
+    gint v = g_str_hash (name);
+
+    color->red = ((v >>  0) & 0xff) / 384. * 0xffff;
+    color->green = ((v >>  8) & 0xff) / 384. * 0xffff;
+    color->blue = ((v >> 16) & 0xff) / 384. * 0xffff;
+}
+
+static test_case_t *
+test_cases_from_reports (cairo_perf_report_t *reports,
+	                 int num_reports)
+{
+    test_case_t *cases, *c;
+    test_report_t **tests;
+    int i, j;
+    int num_tests;
+
+    num_tests = 0;
+    for (i = 0; i < num_reports; i++) {
+	for (j = 0; reports[i].tests[j].name != NULL; j++)
+	    ;
+	if (j > num_tests)
+	    num_tests = j;
+    }
+
+    cases = xcalloc (num_tests+1, sizeof (test_case_t));
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+    for (i = 0; i < num_reports; i++)
+	tests[i] = reports[i].tests;
+
+    c = cases;
+    while (1) {
+	int seen_non_null;
+	test_report_t *min_test;
+
+	/* We expect iterations values of 0 when multiple raw reports
+	 * for the same test have been condensed into the stats of the
+	 * first. So we just skip these later reports that have no
+	 * stats. */
+	seen_non_null = 0;
+	for (i = 0; i < num_reports; i++) {
+	    while (tests[i]->name && tests[i]->stats.iterations == 0)
+		tests[i]++;
+	    if (tests[i]->name)
+		seen_non_null++;
+	}
+
+	if (seen_non_null < 2)
+	    break;
+
+	/* Find the minimum of all current tests, (we have to do this
+	 * in case some reports don't have a particular test). */
+	for (i = 0; i < num_reports; i++) {
+	    if (tests[i]->name) {
+		min_test = tests[i];
+		break;
+	    }
+	}
+	for (++i; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) < 0)
+	    {
+		min_test = tests[i];
+	    }
+	}
+
+	c->min_test = min_test;
+	c->backend = min_test->backend;
+	c->content = min_test->content;
+	c->name = min_test->name;
+	c->size = min_test->size;
+	c->baseline = min_test->stats.min_ticks;
+	c->min = c->max = 1.;
+	c->shown = TRUE;
+	name_to_color (c->name, &c->color);
+
+	for (i = 0; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		tests[i]++;
+		break;
+	    }
+	}
+
+	for (++i; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		double v = tests[i]->stats.min_ticks / c->baseline;
+		if (v < c->min)
+		    c->min = v;
+		if (v > c->max)
+		    c->max = v;
+		tests[i]++;
+	    }
+	}
+
+	c++;
+    }
+    free (tests);
+
+    return cases;
+}
+int
+main (int argc, char *argv[])
+{
+    cairo_perf_report_t *reports;
+    test_case_t *cases;
+    test_report_t *t;
+    int i;
+    GtkWidget *window;
+
+    gtk_init (&argc, &argv);
+
+    if (argc < 3)
+	usage (argv[0]);
+
+    reports = xmalloc ((argc-1) * sizeof (cairo_perf_report_t));
+    for (i = 1; i < argc; i++ )
+	cairo_perf_report_load (&reports[i-1], argv[i]);
+
+    cases = test_cases_from_reports (reports, argc-1);
+
+    window = window_create (cases, reports, argc-1);
+    g_signal_connect (window, "delete-event",
+	              G_CALLBACK (gtk_main_quit), NULL);
+    gtk_widget_show (window);
+
+    gtk_main ();
+
+    /* Pointless memory cleanup, (would be a great place for talloc) */
+    free (cases);
+    for (i = 0; i < argc-1; i++) {
+	for (t = reports[i].tests; t->name; t++) {
+	    free (t->samples);
+	    free (t->backend);
+	    free (t->name);
+	}
+	free (reports[i].tests);
+	free (reports[i].configuration);
+    }
+    free (reports);
+
+    return 0;
+}
diff --git a/perf/cairo-perf-graph-widget.c b/perf/cairo-perf-graph-widget.c
new file mode 100644
index 0000000..2bb48a4
--- /dev/null
+++ b/perf/cairo-perf-graph-widget.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * 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: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+#include "cairo-perf-graph.h"
+
+#include <gtk/gtk.h>
+
+struct _GraphView {
+    GtkWidget widget;
+
+    test_case_t *cases;
+    cairo_perf_report_t *reports;
+    int num_reports;
+    double ymin, ymax;
+
+    int selected_report;
+};
+
+typedef struct _GraphViewClass {
+    GtkWidgetClass parent_class;
+} GraphViewClass;
+
+static GType graph_view_get_type (void);
+
+enum {
+    REPORT_SELECTED,
+    LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE (GraphView, graph_view, GTK_TYPE_WIDGET)
+
+static void
+draw_baseline_performance (test_case_t *cases,
+	                   cairo_perf_report_t *reports,
+	                   int num_reports,
+			   cairo_t *cr,
+			   const cairo_matrix_t *m)
+{
+    test_report_t **tests;
+    double dots[2] = { 0, 1.};
+    int i;
+
+    tests = xmalloc (num_reports * sizeof (test_report_t *));
+    for (i = 0; i < num_reports; i++)
+	tests[i] = reports[i].tests;
+
+    while (cases->backend != NULL) {
+	test_report_t *min_test;
+	double baseline, last_y;
+	double x, y;
+
+	if (! cases->shown) {
+	    cases++;
+	    continue;
+	}
+
+	min_test = cases->min_test;
+
+	for (i = 0; i < num_reports; i++) {
+	    while (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) < 0)
+	    {
+		tests[i]++;
+	    }
+	}
+
+	/* first the stroke */
+	cairo_save (cr);
+	cairo_set_line_width (cr, 2.);
+	gdk_cairo_set_source_color (cr, &cases->color);
+	for (i = 0; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		baseline = tests[i]->stats.min_ticks;
+
+		x = i; y = 0;
+		cairo_matrix_transform_point (m, &x, &y);
+		x = floor (x);
+		y = floor (y);
+		cairo_move_to (cr, x, y);
+		last_y = y;
+		break;
+	    }
+	}
+
+	for (++i; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		x = i, y = tests[i]->stats.min_ticks / baseline;
+
+		if (y < 1.)
+		    y = -1./y + 1;
+		else
+		    y -= 1;
+
+		cairo_matrix_transform_point (m, &x, &y);
+		x = floor (x);
+		y = floor (y);
+		cairo_line_to (cr, x, last_y);
+		cairo_line_to (cr, x, y);
+		last_y = y;
+	    }
+	}
+	{
+	    x = num_reports, y = 0;
+	    cairo_matrix_transform_point (m, &x, &y);
+	    x = floor (x);
+	    cairo_line_to (cr, x, last_y);
+	}
+
+	cairo_set_line_width (cr, 1.);
+	cairo_stroke (cr);
+
+	/* then draw the points */
+	for (i = 0; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		baseline = tests[i]->stats.min_ticks;
+
+		x = i; y = 0;
+		cairo_matrix_transform_point (m, &x, &y);
+		x = floor (x);
+		y = floor (y);
+		cairo_move_to (cr, x, y);
+		cairo_close_path (cr);
+		last_y = y;
+
+		tests[i]++;
+		break;
+	    }
+	}
+
+	for (++i; i < num_reports; i++) {
+	    if (tests[i]->name &&
+		test_report_cmp_backend_then_name (tests[i], min_test) == 0)
+	    {
+		x = i, y = tests[i]->stats.min_ticks / baseline;
+
+		if (y < 1.)
+		    y = -1./y + 1;
+		else
+		    y -= 1;
+
+		cairo_matrix_transform_point (m, &x, &y);
+		x = floor (x);
+		y = floor (y);
+		cairo_move_to (cr, x, last_y);
+		cairo_close_path (cr);
+		cairo_move_to (cr, x, y);
+		cairo_close_path (cr);
+		last_y = y;
+
+		tests[i]++;
+	    }
+	}
+	{
+	    x = num_reports, y = 0;
+	    cairo_matrix_transform_point (m, &x, &y);
+	    x = floor (x);
+	    cairo_move_to (cr, x, last_y);
+	    cairo_close_path (cr);
+	}
+	cairo_set_source_rgba (cr, 0, 0, 0, .5);
+	cairo_set_dash (cr, dots, 2, 0.);
+	cairo_set_line_width (cr, 3.);
+	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+	cairo_stroke (cr);
+	cairo_restore (cr);
+
+	cases++;
+    }
+    free (tests);
+}
+
+static void
+draw_hline (cairo_t *cr, const cairo_matrix_t *m, double y0, double xmin, double xmax)
+{
+    double x, y;
+    double py_offset;
+
+    py_offset = fmod (cairo_get_line_width (cr) / 2., 1.);
+
+    x = xmin; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_move_to (cr, floor (x), floor (y) + py_offset);
+
+    x = xmax; y = y0;
+    cairo_matrix_transform_point (m, &x, &y);
+    cairo_line_to (cr, ceil (x), floor (y) + py_offset);
+
+    cairo_stroke (cr);
+}
+
+#define PAD 24
+static void
+graph_view_draw (GraphView *self, cairo_t *cr)
+{
+    cairo_matrix_t m;
+    const double dash[2] = {4, 4};
+    double range;
+    int i;
+
+    range = ceil (self->ymax) - floor (self->ymin);
+
+    cairo_matrix_init_translate (&m, PAD, PAD);
+    cairo_matrix_scale (&m, (self->widget.allocation.width-2*PAD)/self->num_reports, (self->widget.allocation.height-2*PAD)/range);
+    cairo_matrix_translate (&m, 0,  - floor (self->ymin));
+
+    if (self->selected_report != -1) {
+	cairo_save (cr); {
+	    double x0, x1, y;
+	    x0 = self->selected_report; y = 0;
+	    cairo_matrix_transform_point (&m, &x0, &y);
+	    x0 = floor (x0);
+	    x1 = self->selected_report + 1; y = 0;
+	    cairo_matrix_transform_point (&m, &x1, &y);
+	    x1 = ceil (x1);
+	    y = (x1 - x0) / 8;
+	    y = MIN (y, PAD / 2);
+	    x0 -= y;
+	    x1 += y;
+	    cairo_rectangle (cr, x0, PAD/2, x1-x0, self->widget.allocation.height-2*PAD + PAD);
+	    gdk_cairo_set_source_color (cr, &self->widget.style->base[GTK_STATE_SELECTED]);
+	    cairo_fill (cr);
+	} cairo_restore (cr);
+    }
+
+    cairo_save (cr); {
+	cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
+	cairo_set_line_width (cr, 2.);
+	draw_hline (cr, &m, 0, 0, self->num_reports-1);
+
+	cairo_set_line_width (cr, 1.);
+	cairo_set_dash (cr, NULL, 0, 0);
+	for (i = floor (self->ymin); i <= floor (self->ymax); i++) {
+	    if (i != 0)
+		draw_hline (cr, &m, i, 0, self->num_reports);
+	}
+    } cairo_restore (cr);
+
+    draw_baseline_performance (self->cases,
+	                       self->reports, self->num_reports,
+			       cr, &m);
+
+    cairo_save (cr); {
+	cairo_set_source_rgb (cr, 0.7, 0.7, 0.7);
+	cairo_set_line_width (cr, 1.);
+	cairo_set_dash (cr, dash, 2, 0);
+	draw_hline (cr, &m, 0, 0, self->num_reports-1);
+    } cairo_restore (cr);
+
+}
+
+static gboolean
+graph_view_expose (GtkWidget *w, GdkEventExpose *ev)
+{
+    GraphView *self = (GraphView *) w;
+    cairo_t *cr;
+
+    cr = gdk_cairo_create (w->window);
+    gdk_cairo_region (cr, ev->region);
+    cairo_clip (cr);
+
+    gdk_cairo_set_source_color (cr, &w->style->base[GTK_WIDGET_STATE (w)]);
+    cairo_paint (cr);
+
+    graph_view_draw (self, cr);
+
+    cairo_destroy (cr);
+
+    return FALSE;
+}
+
+static gboolean
+graph_view_button_press (GtkWidget *w, GdkEventButton *ev)
+{
+    GraphView *self = (GraphView *) w;
+    cairo_matrix_t m;
+    double x,y;
+    int i;
+
+    cairo_matrix_init_translate (&m, PAD, self->widget.allocation.height-PAD);
+    cairo_matrix_scale (&m, (self->widget.allocation.width-2*PAD)/self->num_reports, -(self->widget.allocation.height-2*PAD)/(self->ymax - self->ymin));
+    cairo_matrix_translate (&m, 0, -self->ymin);
+    cairo_matrix_invert (&m);
+
+    x = ev->x;
+    y = ev->y;
+    cairo_matrix_transform_point (&m, &x, &y);
+
+    i = floor (x);
+    if (i < 0 || i >= self->num_reports)
+	i = -1;
+
+    if (i != self->selected_report) {
+	self->selected_report = i;
+	gtk_widget_queue_draw (w);
+
+	g_signal_emit (w, signals[REPORT_SELECTED], 0, i);
+    }
+
+    return FALSE;
+}
+
+static gboolean
+graph_view_button_release (GtkWidget *w, GdkEventButton *ev)
+{
+    GraphView *self = (GraphView *) w;
+
+    return FALSE;
+}
+
+static void
+graph_view_realize (GtkWidget *widget)
+{
+    GdkWindowAttr attributes;
+
+    GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
+
+    attributes.window_type = GDK_WINDOW_CHILD;
+    attributes.x = widget->allocation.x;
+    attributes.y = widget->allocation.y;
+    attributes.width  = widget->allocation.width;
+    attributes.height = widget->allocation.height;
+    attributes.wclass = GDK_INPUT_OUTPUT;
+    attributes.visual = gtk_widget_get_visual (widget);
+    attributes.colormap = gtk_widget_get_colormap (widget);
+    attributes.event_mask = gtk_widget_get_events (widget) |
+	                    GDK_BUTTON_PRESS_MASK |
+	                    GDK_BUTTON_RELEASE_MASK |
+	                    GDK_EXPOSURE_MASK;
+
+    widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+				     &attributes,
+				     GDK_WA_X | GDK_WA_Y |
+				     GDK_WA_VISUAL | GDK_WA_COLORMAP);
+    gdk_window_set_user_data (widget->window, widget);
+
+    widget->style = gtk_style_attach (widget->style, widget->window);
+    gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
+}
+
+static void
+graph_view_finalize (GObject *obj)
+{
+    G_OBJECT_CLASS (graph_view_parent_class)->finalize (obj);
+}
+
+static void
+graph_view_class_init (GraphViewClass *klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+    GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
+
+    object_class->finalize = graph_view_finalize;
+
+    widget_class->realize = graph_view_realize;
+    widget_class->expose_event = graph_view_expose;
+    widget_class->button_press_event = graph_view_button_press;
+    widget_class->button_release_event = graph_view_button_release;
+
+    signals[REPORT_SELECTED] =
+	g_signal_new ("report-selected",
+	              G_TYPE_FROM_CLASS (object_class),
+		      G_SIGNAL_RUN_FIRST,
+		      0,//G_STRUCT_OFFSET (GraphView, report_selected),
+		      NULL, NULL,
+		      g_cclosure_marshal_VOID__INT,
+		      G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
+static void
+graph_view_init (GraphView *self)
+{
+    self->selected_report = -1;
+}
+
+GtkWidget *
+graph_view_new (void)
+{
+    return g_object_new (graph_view_get_type (), NULL);
+}
+
+void
+graph_view_update_visible (GraphView *gv)
+{
+    double min, max;
+    test_case_t *cases;
+
+    cases = gv->cases;
+
+    min = max = 1.;
+    while (cases->name != NULL) {
+	if (cases->shown) {
+	    if (cases->min < min)
+		min = cases->min;
+	    if (cases->max > max)
+		max = cases->max;
+	}
+	cases++;
+    }
+    gv->ymin = -1/min + 1;
+    gv->ymax = max - 1;
+
+    gtk_widget_queue_draw (&gv->widget);
+}
+
+void
+graph_view_set_reports (GraphView *gv,
+	                test_case_t *cases,
+	                cairo_perf_report_t *reports,
+			int num_reports)
+{
+    /* XXX ownership? */
+    gv->cases = cases;
+    gv->reports = reports;
+    gv->num_reports = num_reports;
+
+    graph_view_update_visible (gv);
+}
diff --git a/perf/cairo-perf-graph.h b/perf/cairo-perf-graph.h
new file mode 100644
index 0000000..62bf30f
--- /dev/null
+++ b/perf/cairo-perf-graph.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * 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: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_PERF_GRAPH_H
+#define CAIRO_PERF_GRAPH_H
+
+#include <gtk/gtk.h>
+
+#include "cairo-perf.h"
+
+typedef struct _test_case {
+    const char *backend;
+    const char *content;
+    const char *name;
+    int size;
+
+    test_report_t *min_test;
+
+    cairo_bool_t shown;
+    double baseline;
+    double min, max;
+    GdkColor color;
+} test_case_t;
+
+typedef struct _GraphView GraphView;
+
+GtkWidget *
+graph_view_new (void);
+
+void
+graph_view_set_reports (GraphView *gv,
+	                test_case_t *tests,
+	                cairo_perf_report_t *reports,
+			int num_reports);
+
+void
+graph_view_update_visible (GraphView *gv);
+
+#endif
diff --git a/perf/cairo-perf-report.c b/perf/cairo-perf-report.c
new file mode 100644
index 0000000..79e2f92
--- /dev/null
+++ b/perf/cairo-perf-report.c
@@ -0,0 +1,456 @@
+/*
+ * 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>
+ */
+
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+/* We use _GNU_SOURCE for getline and strndup if available. */
+#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>
+#include <assert.h>
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+/* 'ssize_t' does not exist in the C standard on win32.
+ * We use 'ptrdiff_t', which is nearly equivalent. */
+#ifdef _MSC_VER
+typedef ptrdiff_t ssize_t;
+#endif
+
+#ifndef __USE_GNU
+static ssize_t
+getline (char **lineptr, size_t *n, FILE *stream);
+
+static char *
+strndup (const char *s, size_t n);
+#endif
+
+#ifdef _MSC_VER
+static long long
+strtoll(const char *nptr, char **endptr, int base);
+
+static char *
+basename(char *path);
+#endif
+
+/* 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_long_long(result)						\
+do {									\
+    (result) = strtoll (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 *configuration)
+{
+    char *end;
+    char *s = line;
+    cairo_bool_t is_raw = FALSE;
+    double min_time, median_time;
+
+    /* 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;
+    if (*s == '*') {
+	s++;
+	is_raw = TRUE;
+    } else {
+	parse_int (report->id);
+    }
+    skip_char (']');
+
+    skip_space ();
+
+    report->configuration = configuration;
+    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 ();
+
+    report->samples = NULL;
+    report->samples_size = 0;
+    report->samples_count = 0;
+
+    if (is_raw) {
+	parse_double (report->stats.ticks_per_ms);
+	skip_space ();
+
+	report->samples_size = 5;
+	report->samples = xmalloc (report->samples_size * sizeof (cairo_perf_ticks_t));
+	do {
+	    if (report->samples_count == report->samples_size) {
+		report->samples_size *= 2;
+		report->samples = xrealloc (report->samples,
+					    report->samples_size * sizeof (cairo_perf_ticks_t));
+	    }
+	    parse_long_long (report->samples[report->samples_count++]);
+	    skip_space ();
+	} while (*s && *s != '\n');
+	report->stats.iterations = 0;
+	skip_char ('\n');
+    } else {
+	parse_double (report->stats.min_ticks);
+	skip_space ();
+
+	parse_double (min_time);
+	report->stats.ticks_per_ms = report->stats.min_ticks / min_time;
+
+	skip_space ();
+
+	parse_double (median_time);
+	report->stats.median_ticks = median_time * report->stats.ticks_per_ms;
+
+	skip_space ();
+
+	parse_double (report->stats.std_dev);
+	report->stats.std_dev /= 100.0;
+	skip_char ('%');
+
+	skip_space ();
+
+	parse_int (report->stats.iterations);
+
+	skip_space ();
+	skip_char ('\n');
+    }
+
+    return TEST_REPORT_STATUS_SUCCESS;
+}
+
+/* We conditionally provide a custom implementation of getline and strndup
+ * as needed. These aren't necessary full-fledged general purpose
+ * implementations. They just get the job done for our purposes.
+ */
+#ifndef __USE_GNU
+#define POORMANS_GETLINE_BUFFER_SIZE (65536)
+static ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+    if (!*lineptr)
+    {
+        *n = POORMANS_GETLINE_BUFFER_SIZE;
+        *lineptr = (char *) malloc (*n);
+    }
+
+    if (!fgets (*lineptr, *n, stream))
+        return -1;
+
+    if (!feof (stream) && !strchr (*lineptr, '\n'))
+    {
+        fprintf (stderr, "The poor man's implementation of getline in "
+                          __FILE__ " needs a bigger buffer. Perhaps it's "
+                         "time for a complete implementation of getline.\n");
+        exit (0);
+    }
+
+    return strlen (*lineptr);
+}
+#undef POORMANS_GETLINE_BUFFER_SIZE
+
+static char *
+strndup (const char *s, size_t n)
+{
+    size_t len;
+    char *sdup;
+
+    if (!s)
+        return NULL;
+
+    len = strlen (s);
+    len = (n < len ? n : len);
+    sdup = (char *) malloc (len + 1);
+    if (sdup)
+    {
+        memcpy (sdup, s, len);
+        sdup[len] = '\0';
+    }
+
+    return sdup;
+}
+#endif /* ifndef __USE_GNU */
+
+/* We provide hereafter a win32 implementation of the basename
+ * and strtoll functions which are not available otherwise.
+ * The basename function is fully compliant to its GNU specs.
+ */
+#ifdef _MSC_VER
+long long
+strtoll(const char *nptr, char **endptr, int base)
+{
+    return _atoi64(nptr);
+}
+
+static char *
+basename(char *path)
+{
+    char *end, *s;
+
+    end = (path + strlen(path) - 1);
+    while (end && (end >= path + 1) && (*end == '/')) {
+	*end = '\0';
+	end--;
+    }
+
+    s = strrchr(path, '/');
+    if (s) {
+	if (s == end) {
+	    return s;
+	} else {
+	    return s+1;
+	}
+    } else {
+	return path;
+    }
+}
+#endif /* ifndef _MSC_VER */
+
+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;
+
+    /* A NULL name is a list-termination marker, so force it last. */
+    if (a_test->name == NULL)
+	if (b_test->name == NULL)
+	    return 0;
+	else
+	    return 1;
+    else if (b_test->name == NULL)
+	return -1;
+
+    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;
+}
+
+void
+cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report)
+{
+    test_report_t *base, *next, *last, *t;
+
+    /* First we sort, since the diff needs both lists in the same
+     * order */
+    qsort (report->tests, report->tests_count, sizeof (test_report_t),
+	   test_report_cmp_backend_then_name);
+
+    /* The sorting also brings all related raw reports together so we
+     * can condense them and compute the stats.
+     */
+    base = &report->tests[0];
+    last = &report->tests[report->tests_count - 1];
+    while (base <= last) {
+	next = base+1;
+	if (next <= last) {
+	    while (next <= last &&
+		   test_report_cmp_backend_then_name (base, next) == 0)
+	    {
+		next++;
+	    }
+	    if (next != base) {
+		unsigned int new_samples_count = base->samples_count;
+		for (t = base + 1; t < next; t++)
+		    new_samples_count += t->samples_count;
+		if (new_samples_count > base->samples_size) {
+		    base->samples_size = new_samples_count;
+		    base->samples = xrealloc (base->samples,
+					      base->samples_size * sizeof (cairo_perf_ticks_t));
+		}
+		for (t = base + 1; t < next; t++) {
+		    memcpy (&base->samples[base->samples_count], t->samples,
+			    t->samples_count * sizeof (cairo_perf_ticks_t));
+		    base->samples_count += t->samples_count;
+		}
+	    }
+	}
+	if (base->samples)
+	    _cairo_stats_compute (&base->stats, base->samples, base->samples_count);
+	base = next;
+    }
+}
+
+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;
+    char *configuration;
+    char *dot;
+    char *baseName;
+
+    configuration = xmalloc (strlen (filename) * sizeof (char) + 1);
+    strcpy (configuration, filename);
+    baseName = strdup (basename (configuration));
+    report->configuration = xmalloc (strlen (filename) * sizeof (char) + 1);
+    strcpy(report->configuration, baseName);
+    free (configuration);
+    dot = strrchr (report->configuration, '.');
+    if (dot)
+	*dot = '\0';
+
+    report->name = filename;
+    report->tests_size = 16;
+    report->tests = xmalloc (report->tests_size * sizeof (test_report_t));
+    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 *= 2;
+	    report->tests = xrealloc (report->tests,
+				      report->tests_size * sizeof (test_report_t));
+	}
+
+	line_number++;
+	if (getline (&line, &line_size, file) == -1)
+	    break;
+
+	status = test_report_parse (&report->tests[report->tests_count],
+				    line, report->configuration);
+	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);
+
+    fclose (file);
+
+    cairo_perf_report_sort_and_compute_stats (report);
+
+    /* Add one final report with a NULL name to terminate the list. */
+    if (report->tests_count == report->tests_size) {
+	report->tests_size *= 2;
+	report->tests = xrealloc (report->tests,
+				  report->tests_size * sizeof (test_report_t));
+    }
+    report->tests[report->tests_count].name = NULL;
+}
+
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 68e145b..613d15c 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -29,6 +29,7 @@
 #define _GNU_SOURCE 1	/* for sched_getaffinity() */
 
 #include "cairo-perf.h"
+#include "cairo-stats.h"
 
 #include "cairo-boilerplate-getopt.h"
 
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index 4a75dc1..57fe85f 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -32,7 +32,13 @@
 
 typedef uint64_t cairo_perf_ticks_t;
 
-#include "cairo-stats.h"
+typedef struct _cairo_stats {
+    cairo_perf_ticks_t min_ticks;
+    cairo_perf_ticks_t median_ticks;
+    double ticks_per_ms;
+    double std_dev;
+    int iterations;
+} cairo_stats_t;
 
 /* timers */
 
@@ -93,6 +99,59 @@ cairo_perf_cover_sources_and_operators (cairo_perf_t		*perf,
 					const char		*name,
 					cairo_perf_func_t	 perf_func);
 
+/* reporter convenience routines */
+
+typedef struct _test_report {
+    int id;
+    const char *configuration;
+    char *backend;
+    char *content;
+    char *name;
+    int size;
+
+    /* The samples only exists for "raw" reports */
+    cairo_perf_ticks_t *samples;
+    unsigned int samples_size;
+    unsigned int samples_count;
+
+    /* The stats are either read directly or computed from samples.
+     * If the stats have not yet been computed from samples, then
+     * iterations will be 0. */
+    cairo_stats_t stats;
+} test_report_t;
+
+typedef struct _test_diff {
+    test_report_t **tests;
+    int num_tests;
+    double min;
+    double max;
+    double change;
+} test_diff_t;
+
+typedef struct _cairo_perf_report {
+    char *configuration;
+    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;
+
+void
+cairo_perf_report_load (cairo_perf_report_t *report,
+	                const char *filename);
+
+void
+cairo_perf_report_sort_and_compute_stats (cairo_perf_report_t *report);
+
+int
+test_report_cmp_backend_then_name (const void *a, const void *b);
+
 #define CAIRO_PERF_DECL(func) void (func) (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 
 CAIRO_PERF_DECL (fill);
diff --git a/perf/cairo-stats.h b/perf/cairo-stats.h
index 3f8a988..58ff0b6 100644
--- a/perf/cairo-stats.h
+++ b/perf/cairo-stats.h
@@ -28,14 +28,6 @@
 
 #include "cairo-perf.h"
 
-typedef struct _cairo_stats {
-    cairo_perf_ticks_t min_ticks;
-    cairo_perf_ticks_t median_ticks;
-    double ticks_per_ms;
-    double std_dev;
-    int iterations;
-} cairo_stats_t;
-
 void
 _cairo_stats_compute (cairo_stats_t		*stats,
 		      cairo_perf_ticks_t	*values,
commit 41c8eefc6d432ab213f6f405c3d6346adb7f7931
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Oct 15 22:24:32 2008 +0100

    [output-stream] Protect against NULL write_func.
    
    Allow the user to specify a NULL write_func for the output stream so that
    a dummy surface can be created, for example, for querying target font
    options or font extents.
    
    Currently we do not perform any sanity checks at the user entry point and
    will generate a mysterious SEGV during cairo_surface_finish() - which may
    not immediately be obvious that it is due to a NULL write_func.

diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
index d02b277..bae6ac4 100644
--- a/src/cairo-output-stream.c
+++ b/src/cairo-output-stream.c
@@ -116,6 +116,9 @@ closure_write (cairo_output_stream_t *stream,
     cairo_output_stream_with_closure_t *stream_with_closure =
 	(cairo_output_stream_with_closure_t *) stream;
 
+    if (stream_with_closure->write_func == NULL)
+	return CAIRO_STATUS_SUCCESS;
+
     return stream_with_closure->write_func (stream_with_closure->closure,
 					    data, length);
 }
commit 84e4a825fffe3d78e95dec3406a86da6a23144c2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Oct 15 22:15:17 2008 +0100

    [output-stream] Accept a NULL filename
    
    Principally to support creating a dummy vector surface (i.e.
    cairo_ps_surface_create (NULL, 1, 1)) that can be used to determine font
    extents (or target font options) before opening an output file, but also
    because we currently fail to do any sanity checking at the entry point.

diff --git a/src/cairo-output-stream.c b/src/cairo-output-stream.c
index 7062007..d02b277 100644
--- a/src/cairo-output-stream.c
+++ b/src/cairo-output-stream.c
@@ -583,6 +583,9 @@ _cairo_output_stream_create_for_filename (const char *filename)
     stdio_stream_t *stream;
     FILE *file;
 
+    if (filename == NULL)
+	return _cairo_null_stream_create ();
+
     file = fopen (filename, "wb");
     if (file == NULL) {
 	switch (errno) {
commit 52ea38f97b1b90e395c667619770482b10e5672e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:44:29 2008 +0100

    [meta] Acquire scaled_font mutex for glyph_path().
    
    The caller of _cairo_scaled_font_glyph_path() is expected to be holding
    the scaled_font->mutex.

diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c
index b86cb51..f45bdb9 100644
--- a/src/cairo-meta-surface.c
+++ b/src/cairo-meta-surface.c
@@ -730,10 +730,12 @@ _cairo_meta_surface_get_path (cairo_surface_t	 *surface,
 	}
 	case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
 	{
+	    CAIRO_MUTEX_LOCK (command->show_text_glyphs.scaled_font->mutex);
 	    status = _cairo_scaled_font_glyph_path (command->show_text_glyphs.scaled_font,
 						    command->show_text_glyphs.glyphs,
 						    command->show_text_glyphs.num_glyphs,
 						    path);
+	    CAIRO_MUTEX_UNLOCK (command->show_text_glyphs.scaled_font->mutex);
 	    break;
 	}
 
commit 299ea0580a646dc55fd0156f1904fe4b45ec5725
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:37:38 2008 +0100

    [user-font] Review locks under error conditions.
    
    Simplify the error handling by only relinquishing the global
    scaled_font_map mutex if we successfully insert the placeholder font. The
    result is that on the error path, there are no changes to global state and
    thus we can entirely skip the user-font initialisation and
    re-registration.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 661d85e..a338860 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -412,17 +412,21 @@ _cairo_scaled_font_map_destroy (void)
  * font backend upon error.
  */
 
-void
+cairo_status_t
 _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_status_t status;
     cairo_scaled_font_t *placeholder_scaled_font;
 
+    assert (CAIRO_HOLDS_MUTEX (_cairo_scaled_font_map_mutex));
+
+    status = scaled_font->status;
+    if (status)
+	return status;
+
     placeholder_scaled_font = malloc (sizeof (cairo_scaled_font_t));
-    if (!placeholder_scaled_font) {
-	status = CAIRO_STATUS_NO_MEMORY;
-	goto FREE;
-    }
+    if (placeholder_scaled_font == NULL)
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     /* full initialization is wasteful, but who cares... */
     status = _cairo_scaled_font_init (placeholder_scaled_font,
@@ -432,32 +436,26 @@ _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t
 				      &scaled_font->options,
 				      NULL);
     if (status)
-	goto FINI;
+	goto FREE_PLACEHOLDER;
 
     placeholder_scaled_font->placeholder = TRUE;
 
-    CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
-
     status = _cairo_hash_table_insert (cairo_scaled_font_map->hash_table,
 				       &placeholder_scaled_font->hash_entry);
     if (status)
-	goto UNLOCK_KEY;
+	goto FINI_PLACEHOLDER;
 
-    goto UNLOCK;
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+    CAIRO_MUTEX_LOCK (placeholder_scaled_font->mutex);
 
-   UNLOCK_KEY:
-    CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
+    return CAIRO_STATUS_SUCCESS;
 
-   FINI:
+  FINI_PLACEHOLDER:
     _cairo_scaled_font_fini (placeholder_scaled_font);
-
-   FREE:
+  FREE_PLACEHOLDER:
     free (placeholder_scaled_font);
 
-    status = _cairo_scaled_font_set_error (scaled_font, status);
-
- UNLOCK:
-    CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+    return _cairo_scaled_font_set_error (scaled_font, status);
 }
 
 void
@@ -473,14 +471,16 @@ _cairo_scaled_font_unregister_placeholder_and_lock_font_map (cairo_scaled_font_t
 				      (cairo_hash_entry_t**) &placeholder_scaled_font);
     assert (found);
     assert (placeholder_scaled_font->placeholder);
+    assert (CAIRO_HOLDS_MUTEX (placeholder_scaled_font->mutex));
 
     _cairo_hash_table_remove (cairo_scaled_font_map->hash_table,
 			      &scaled_font->hash_entry);
 
-    CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
-
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
+
+    CAIRO_MUTEX_UNLOCK (placeholder_scaled_font->mutex);
     cairo_scaled_font_destroy (placeholder_scaled_font);
+
     CAIRO_MUTEX_LOCK (_cairo_scaled_font_map_mutex);
 }
 
diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index 2a12df7..5645287 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -151,9 +151,14 @@ _cairo_user_scaled_glyph_init (void			 *abstract_font,
 	    null_surface = _cairo_null_surface_create (cairo_surface_get_content (meta_surface));
 	    analysis_surface = _cairo_analysis_surface_create (null_surface, -1, -1);
 	    cairo_surface_destroy (null_surface);
+	    status = analysis_surface->status;
+	    if (status)
+		return status;
 
-	    _cairo_analysis_surface_set_ctm (analysis_surface, &scaled_font->extent_scale);
-	    status = _cairo_meta_surface_replay (meta_surface, analysis_surface);
+	    _cairo_analysis_surface_set_ctm (analysis_surface,
+					     &scaled_font->extent_scale);
+	    status = _cairo_meta_surface_replay (meta_surface,
+						 analysis_surface);
 	    _cairo_analysis_surface_get_bounding_box (analysis_surface, &bbox);
 	    cairo_surface_destroy (analysis_surface);
 
@@ -399,7 +404,7 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 
     user_scaled_font = malloc (sizeof (cairo_user_scaled_font_t));
     if (user_scaled_font == NULL)
-	return CAIRO_STATUS_NO_MEMORY;
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     status = _cairo_scaled_font_init (&user_scaled_font->base,
 				      &font_face->base,
@@ -444,29 +449,31 @@ _cairo_user_font_face_scaled_font_create (void                        *abstract_
 	}
     }
 
-    if (status == CAIRO_STATUS_SUCCESS && font_face->scaled_font_methods.init != NULL) {
-
-	cairo_t *cr;
-
+    if (status == CAIRO_STATUS_SUCCESS &&
+	font_face->scaled_font_methods.init != NULL)
+    {
 	/* Lock the scaled_font mutex such that user doesn't accidentally try
          * to use it just yet. */
 	CAIRO_MUTEX_LOCK (user_scaled_font->base.mutex);
 
 	/* Give away fontmap lock such that user-font can use other fonts */
-	_cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base);
+	status = _cairo_scaled_font_register_placeholder_and_unlock_font_map (&user_scaled_font->base);
+	if (status == CAIRO_STATUS_SUCCESS) {
+	    cairo_t *cr;
 
-	cr = _cairo_user_scaled_font_create_meta_context (user_scaled_font);
+	    cr = _cairo_user_scaled_font_create_meta_context (user_scaled_font);
 
-	status = font_face->scaled_font_methods.init (&user_scaled_font->base,
-						      cr,
-						      &font_extents);
+	    status = font_face->scaled_font_methods.init (&user_scaled_font->base,
+							  cr,
+							  &font_extents);
 
-	if (status == CAIRO_STATUS_SUCCESS)
-	    status = cairo_status (cr);
+	    if (status == CAIRO_STATUS_SUCCESS)
+		status = cairo_status (cr);
 
-	cairo_destroy (cr);
+	    cairo_destroy (cr);
 
-	_cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base);
+	    _cairo_scaled_font_unregister_placeholder_and_lock_font_map (&user_scaled_font->base);
+	}
 
 	CAIRO_MUTEX_UNLOCK (user_scaled_font->base.mutex);
     }
diff --git a/src/cairoint.h b/src/cairoint.h
index 960c38f..cdf28b4 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1538,7 +1538,7 @@ _cairo_scaled_font_create_in_error (cairo_status_t status);
 cairo_private void
 _cairo_scaled_font_reset_static_data (void);
 
-cairo_private void
+cairo_private cairo_status_t
 _cairo_scaled_font_register_placeholder_and_unlock_font_map (cairo_scaled_font_t *scaled_font);
 
 cairo_private void
commit c76a8481f372fab8981231b257fdcc69466263d2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:36:34 2008 +0100

    [analysis] Check for error surfaces.
    
    If the target surface is an error surface, ensure that we return the
    appropriate error surface. Likewise, avoid writing to error surfaces.

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index c33260d..5093459 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -752,6 +752,11 @@ _cairo_analysis_surface_create (cairo_surface_t		*target,
 				int			 height)
 {
     cairo_analysis_surface_t *surface;
+    cairo_status_t status;
+
+    status = target->status;
+    if (status)
+	return _cairo_surface_create_in_error (status);
 
     surface = malloc (sizeof (cairo_analysis_surface_t));
     if (surface == NULL)
@@ -795,17 +800,22 @@ _cairo_analysis_surface_create (cairo_surface_t		*target,
     return &surface->base;
 }
 
-cairo_private void
+void
 _cairo_analysis_surface_set_ctm (cairo_surface_t *abstract_surface,
 				 cairo_matrix_t  *ctm)
 {
-    cairo_analysis_surface_t	*surface = (cairo_analysis_surface_t *) abstract_surface;
+    cairo_analysis_surface_t	*surface;
+
+    if (abstract_surface->status)
+	return;
+
+    surface = (cairo_analysis_surface_t *) abstract_surface;
 
     surface->ctm = *ctm;
     surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm);
 }
 
-cairo_private void
+void
 _cairo_analysis_surface_get_ctm (cairo_surface_t *abstract_surface,
 				 cairo_matrix_t  *ctm)
 {
commit 5b28b0b903cb2fdb8a5614659d528bf12488389e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:35:48 2008 +0100

    [ps] Destroy type3 surface on error.
    
    After an error, ensure that the local type3 surface is destroyed.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index b107a54..83c94a8 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -527,7 +527,7 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t		*surface,
 							    &width);
 	}
 	if (status)
-	    return status;
+	    break;
 
 	_cairo_output_stream_printf (surface->final_stream,
 				     "    }\n");
@@ -548,6 +548,8 @@ _cairo_ps_surface_emit_type3_font_subset (cairo_ps_surface_t		*surface,
         }
     }
     cairo_surface_destroy (type3_surface);
+    if (status)
+	return status;
 
     _cairo_output_stream_printf (surface->final_stream,
 				 "] def\n"
commit 6b17c6da47c42dd04ed2acad723c25b6da41b51c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:34:17 2008 +0100

    [type3] Acquire scaled_font mutex whilst looking up glyphs.
    
    When looking up the glyph in the shared scaled_font glyph cache, the
    caller is required to have taken the scaled_font->mutex.

diff --git a/src/cairo-type3-glyph-surface.c b/src/cairo-type3-glyph-surface.c
index 1382743..3508ce7 100644
--- a/src/cairo-type3-glyph-surface.c
+++ b/src/cairo-type3-glyph-surface.c
@@ -286,12 +286,14 @@ _cairo_type3_glyph_surface_show_glyphs (void		     *abstract_surface,
 				     &new_ctm,
 				     &scaled_font->options);
 
+    CAIRO_MUTEX_LOCK (font->mutex);
     status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
 						    NULL, 0,
 						    glyphs, num_glyphs,
 						    NULL, 0,
 						    FALSE,
 						    font);
+    CAIRO_MUTEX_UNLOCK (font->mutex);
 
     cairo_scaled_font_destroy (font);
 
@@ -347,11 +349,13 @@ _cairo_type3_glyph_surface_emit_fallback_image (cairo_type3_glyph_surface_t *sur
     cairo_matrix_t mat;
     double x, y;
 
+    CAIRO_MUTEX_LOCK (surface->scaled_font->mutex);
     status = _cairo_scaled_glyph_lookup (surface->scaled_font,
 					 glyph_index,
 					 CAIRO_SCALED_GLYPH_INFO_METRICS |
 					 CAIRO_SCALED_GLYPH_INFO_SURFACE,
 					 &scaled_glyph);
+    CAIRO_MUTEX_UNLOCK (surface->scaled_font->mutex);
     if (status)
 	return status;
 
@@ -396,12 +400,16 @@ _cairo_type3_glyph_surface_analyze_glyph (void		     *abstract_surface,
 
     null_stream = _cairo_null_stream_create ();
     _cairo_type3_glyph_surface_set_stream (surface, null_stream);
+
+    CAIRO_MUTEX_LOCK (surface->scaled_font->mutex);
     status = _cairo_scaled_glyph_lookup (surface->scaled_font,
 					 glyph_index,
 					 CAIRO_SCALED_GLYPH_INFO_METRICS |
 					 CAIRO_SCALED_GLYPH_INFO_META_SURFACE,
 					 &scaled_glyph);
-    if (status && status != CAIRO_INT_STATUS_UNSUPPORTED)
+    CAIRO_MUTEX_UNLOCK (surface->scaled_font->mutex);
+
+    if (_cairo_status_is_error (status))
 	goto cleanup;
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
@@ -411,20 +419,22 @@ _cairo_type3_glyph_surface_analyze_glyph (void		     *abstract_surface,
 
     status = _cairo_meta_surface_replay (scaled_glyph->meta_surface,
 					 &surface->base);
-
-    status = _cairo_pdf_operators_flush (&surface->pdf_operators);
     if (status)
-	return status;
+	goto cleanup;
+
+    status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
+    if (status == CAIRO_STATUS_SUCCESS)
+	status = status2;
 
     if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
 	status = CAIRO_STATUS_SUCCESS;
 
 cleanup:
     status2 = _cairo_output_stream_destroy (null_stream);
-    if (status)
-	return status;
+    if (status == CAIRO_STATUS_SUCCESS)
+	status = status2;
 
-    return status2;
+    return status;
 }
 
 cairo_status_t
@@ -458,24 +468,24 @@ _cairo_type3_glyph_surface_emit_glyph (void		     *abstract_surface,
     cairo_matrix_t font_matrix_inverse;
 
     _cairo_type3_glyph_surface_set_stream (surface, stream);
+
+    CAIRO_MUTEX_LOCK (surface->scaled_font->mutex);
     status = _cairo_scaled_glyph_lookup (surface->scaled_font,
 					 glyph_index,
 					 CAIRO_SCALED_GLYPH_INFO_METRICS |
 					 CAIRO_SCALED_GLYPH_INFO_META_SURFACE,
 					 &scaled_glyph);
-    if (status && status != CAIRO_INT_STATUS_UNSUPPORTED)
-	return status;
-
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 	status = _cairo_scaled_glyph_lookup (surface->scaled_font,
 					     glyph_index,
 					     CAIRO_SCALED_GLYPH_INFO_METRICS,
 					     &scaled_glyph);
-	if (status)
-	    return status;
-
-	status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = CAIRO_INT_STATUS_IMAGE_FALLBACK;
     }
+    CAIRO_MUTEX_UNLOCK (surface->scaled_font->mutex);
+    if (_cairo_status_is_error (status))
+	return status;
 
     x_advance = scaled_glyph->metrics.x_advance;
     y_advance = scaled_glyph->metrics.y_advance;
@@ -508,11 +518,11 @@ _cairo_type3_glyph_surface_emit_glyph (void		     *abstract_surface,
 
 	_cairo_output_stream_printf (surface->stream, "q\n");
 	status = _cairo_meta_surface_replay (scaled_glyph->meta_surface,
-					 &surface->base);
+					     &surface->base);
 
-	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
-	if (status)
-	    return status;
+	status2 = _cairo_pdf_operators_flush (&surface->pdf_operators);
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
 
 	_cairo_output_stream_printf (surface->stream, "Q\n");
 
@@ -521,8 +531,8 @@ _cairo_type3_glyph_surface_emit_glyph (void		     *abstract_surface,
 	    _cairo_memory_stream_copy (mem_stream, stream);
 
 	status2 = _cairo_output_stream_destroy (mem_stream);
-	if (status2)
-	    return status2;
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = status2;
     }
 
     if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK)
commit f56cf93181e73b4ba74f25ce14f7ed6c7cf36e83
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 22:13:53 2008 +0100

    [user-font] Check for error objects.
    
    Check that the user has not passed in an inert error object before any
    attempts to write to it and return the default values.

diff --git a/src/cairo-user-font.c b/src/cairo-user-font.c
index 014ba70..2a12df7 100644
--- a/src/cairo-user-font.c
+++ b/src/cairo-user-font.c
@@ -568,11 +568,17 @@ void
 cairo_user_font_face_set_init_func (cairo_font_face_t                  *font_face,
 				    cairo_user_scaled_font_init_func_t  init_func)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     if (user_font_face->immutable) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
 	    return;
@@ -604,11 +610,17 @@ void
 cairo_user_font_face_set_render_glyph_func (cairo_font_face_t                          *font_face,
 					    cairo_user_scaled_font_render_glyph_func_t  render_glyph_func)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     if (user_font_face->immutable) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
 	    return;
@@ -636,11 +648,17 @@ void
 cairo_user_font_face_set_text_to_glyphs_func (cairo_font_face_t                            *font_face,
 					      cairo_user_scaled_font_text_to_glyphs_func_t  text_to_glyphs_func)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     if (user_font_face->immutable) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
 	    return;
@@ -667,11 +685,16 @@ void
 cairo_user_font_face_set_unicode_to_glyph_func (cairo_font_face_t                              *font_face,
 						cairo_user_scaled_font_unicode_to_glyph_func_t  unicode_to_glyph_func)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+    if (font_face->status)
+	return;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     if (user_font_face->immutable) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_USER_FONT_IMMUTABLE))
 	    return;
@@ -689,18 +712,24 @@ slim_hidden_def(cairo_user_font_face_set_unicode_to_glyph_func);
  * Gets the scaled-font initialization function of a user-font.
  *
  * Return value: The init callback of @font_face
- * or %NULL if none set.
+ * or %NULL if none set or an error has occurred.
  *
  * Since: 1.8
  **/
 cairo_user_scaled_font_init_func_t
 cairo_user_font_face_get_init_func (cairo_font_face_t *font_face)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return NULL;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return NULL;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     return user_font_face->scaled_font_methods.init;
 }
 
@@ -711,18 +740,24 @@ cairo_user_font_face_get_init_func (cairo_font_face_t *font_face)
  * Gets the glyph rendering function of a user-font.
  *
  * Return value: The render_glyph callback of @font_face
- * or %NULL if none set.
+ * or %NULL if none set or an error has occurred.
  *
  * Since: 1.8
  **/
 cairo_user_scaled_font_render_glyph_func_t
 cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return NULL;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return NULL;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     return user_font_face->scaled_font_methods.render_glyph;
 }
 
@@ -733,18 +768,24 @@ cairo_user_font_face_get_render_glyph_func (cairo_font_face_t *font_face)
  * Gets the text-to-glyphs conversion function of a user-font.
  *
  * Return value: The text_to_glyphs callback of @font_face
- * or %NULL if none set.
+ * or %NULL if none set or an error occurred.
  *
  * Since: 1.8
  **/
 cairo_user_scaled_font_text_to_glyphs_func_t
 cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return NULL;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return NULL;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     return user_font_face->scaled_font_methods.text_to_glyphs;
 }
 
@@ -755,17 +796,23 @@ cairo_user_font_face_get_text_to_glyphs_func (cairo_font_face_t *font_face)
  * Gets the unicode-to-glyph conversion function of a user-font.
  *
  * Return value: The unicode_to_glyph callback of @font_face
- * or %NULL if none set.
+ * or %NULL if none set or an error occurred.
  *
  * Since: 1.8
  **/
 cairo_user_scaled_font_unicode_to_glyph_func_t
 cairo_user_font_face_get_unicode_to_glyph_func (cairo_font_face_t *font_face)
 {
-    cairo_user_font_face_t *user_font_face = (cairo_user_font_face_t *) font_face;
+    cairo_user_font_face_t *user_font_face;
+
+    if (font_face->status)
+	return NULL;
+
     if (! _cairo_font_face_is_user (font_face)) {
 	if (_cairo_font_face_set_error (font_face, CAIRO_STATUS_FONT_TYPE_MISMATCH))
 	    return NULL;
     }
+
+    user_font_face = (cairo_user_font_face_t *) font_face;
     return user_font_face->scaled_font_methods.unicode_to_glyph;
 }
commit 1f9f9d936b296dbe796b1436c7da7fa3462f7d59
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:39:56 2008 +0100

    [pdf] Acquire scaled_font mutex for show_text_glyphs().
    
    We need to be holding the scaled font mutex over a call to draw text
    glyphs from within an smask group.

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index e073eb4..507f522 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -4044,12 +4044,14 @@ _cairo_pdf_surface_write_smask_group (cairo_pdf_surface_t     *surface,
 					      &group->ctm_inverse);
 	break;
     case PDF_SHOW_GLYPHS:
+	CAIRO_MUTEX_LOCK (group->scaled_font->mutex);
 	status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators,
 							group->utf8, group->utf8_len,
 							group->glyphs, group->num_glyphs,
 							group->clusters, group->num_clusters,
 							group->cluster_flags,
 							group->scaled_font);
+	CAIRO_MUTEX_UNLOCK (group->scaled_font->mutex);
 	break;
     }
     if (status)
commit 12fb8c9b7c808ab20bee466aa28ee368559fd902
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:33:07 2008 +0100

    [type1] Acquire scaled_font mutex.
    
    In order to perform glyph lookups we need to be holding the mutex for the
    scaled_font.

diff --git a/src/cairo-type1-fallback.c b/src/cairo-type1-fallback.c
index 7550310..840fc7d 100644
--- a/src/cairo-type1-fallback.c
+++ b/src/cairo-type1-fallback.c
@@ -348,6 +348,7 @@ cairo_type1_font_create_charstring (cairo_type1_font_t      *font,
     cairo_bool_t emit_path = TRUE;
 
     /* This call may return CAIRO_INT_STATUS_UNSUPPORTED for bitmap fonts. */
+    CAIRO_MUTEX_LOCK (font->type1_scaled_font->mutex);
     status = _cairo_scaled_glyph_lookup (font->type1_scaled_font,
 					 glyph_index,
 					 CAIRO_SCALED_GLYPH_INFO_METRICS|
@@ -363,6 +364,7 @@ cairo_type1_font_create_charstring (cairo_type1_font_t      *font,
 					     CAIRO_SCALED_GLYPH_INFO_METRICS,
 					     &scaled_glyph);
     }
+    CAIRO_MUTEX_UNLOCK (font->type1_scaled_font->mutex);
     if (status)
         return status;
 
commit ca5f868a73b35eda737ae6596efff91f82eeea75
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:26:20 2008 +0100

    [scaled-font] Zero font extents for an error surface.
    
    Do not attempt to read from the error object, but just return zero
    extents.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index fcea8aa..661d85e 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1127,6 +1127,15 @@ void
 cairo_scaled_font_extents (cairo_scaled_font_t  *scaled_font,
 			   cairo_font_extents_t *extents)
 {
+    if (scaled_font->status) {
+	extents->ascent  = 0.0;
+	extents->descent = 0.0;
+	extents->height  = 0.0;
+	extents->max_x_advance = 0.0;
+	extents->max_y_advance = 0.0;
+	return;
+    }
+
     *extents = scaled_font->extents;
 }
 slim_hidden_def (cairo_scaled_font_extents);
@@ -1174,8 +1183,10 @@ cairo_scaled_font_text_extents (cairo_scaled_font_t   *scaled_font,
 					       &glyphs, &num_glyphs,
 					       NULL, NULL,
 					       NULL);
-    if (status)
+    if (status) {
+	status = _cairo_scaled_font_set_error (scaled_font, status);
 	goto ZERO_EXTENTS;
+    }
 
     cairo_scaled_font_glyph_extents (scaled_font, glyphs, num_glyphs, extents);
     free (glyphs);
commit 1ddf0b2a5c1f3d20b9a91acf7aae021f6b738485
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 10:35:38 2008 +0100

    [surface] Reorder asserts to make no assumptions about error objects.
    
    If the surface is in error, then we cannot assume anything about the
    validity of its contents other than the error status (and reference
    count). This is for the cases were the surface is replaced by a nil
    surface, and in future where the error surface may be replaced by a tiny
    error object.

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 014acf3..75cccae 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -606,11 +606,11 @@ _cairo_surface_set_font_options (cairo_surface_t       *surface,
 {
     cairo_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status = _cairo_surface_set_error (surface,
 		                           CAIRO_STATUS_SURFACE_FINISHED);
@@ -704,6 +704,9 @@ slim_hidden_def (cairo_surface_flush);
 void
 cairo_surface_mark_dirty (cairo_surface_t *surface)
 {
+    if (surface->status)
+	return;
+
     assert (! surface->is_snapshot);
 
     cairo_surface_mark_dirty_rectangle (surface, 0, 0, -1, -1);
@@ -734,11 +737,11 @@ cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
 {
     cairo_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
 	return;
@@ -792,11 +795,11 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface,
 {
     cairo_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
 	return;
@@ -838,11 +841,11 @@ cairo_surface_set_device_offset (cairo_surface_t *surface,
 {
     cairo_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
 	return;
@@ -921,11 +924,11 @@ cairo_surface_set_fallback_resolution (cairo_surface_t	*surface,
 {
     cairo_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status = _cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
 	return;
@@ -987,11 +990,11 @@ _cairo_surface_acquire_source_image (cairo_surface_t         *surface,
 				     cairo_image_surface_t  **image_out,
 				     void                   **image_extra)
 {
-    assert (!surface->finished);
-
     if (surface->status)
 	return surface->status;
 
+    assert (!surface->finished);
+
     if (surface->backend->acquire_source_image == NULL)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1056,11 +1059,11 @@ _cairo_surface_acquire_dest_image (cairo_surface_t         *surface,
 				   cairo_rectangle_int_t   *image_rect,
 				   void                   **image_extra)
 {
-    assert (!surface->finished);
-
     if (surface->status)
 	return surface->status;
 
+    assert (!surface->finished);
+
     if (surface->backend->acquire_dest_image == NULL)
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
@@ -1265,8 +1268,6 @@ _cairo_surface_composite (cairo_operator_t	op,
 {
     cairo_int_status_t status;
 
-    assert (! dst->is_snapshot);
-
     if (mask) {
 	/* These operators aren't interpreted the same way by the backends;
 	 * they are implemented in terms of other operators in cairo-gstate.c
@@ -1277,6 +1278,8 @@ _cairo_surface_composite (cairo_operator_t	op,
     if (dst->status)
 	return dst->status;
 
+    assert (! dst->is_snapshot);
+
     if (dst->finished)
 	return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED);
 
@@ -1326,11 +1329,11 @@ _cairo_surface_fill_rectangle (cairo_surface_t	   *surface,
 {
     cairo_rectangle_int_t rect;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished)
 	return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED);
 
@@ -1368,11 +1371,11 @@ _cairo_surface_fill_region (cairo_surface_t	   *surface,
     cairo_status_t status;
     int i;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     num_boxes = _cairo_region_num_boxes (region);
 
     if (num_boxes == 0)
@@ -1440,11 +1443,11 @@ _cairo_surface_fill_rectangles (cairo_surface_t		*surface,
 {
     cairo_int_status_t status;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished)
 	return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED);
 
@@ -1471,11 +1474,11 @@ _cairo_surface_paint (cairo_surface_t	*surface,
     cairo_status_t status;
     cairo_pattern_t *dev_source;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source);
     if (status)
 	return _cairo_surface_set_error (surface, status);
@@ -1504,11 +1507,11 @@ _cairo_surface_mask (cairo_surface_t	*surface,
     cairo_pattern_t *dev_source;
     cairo_pattern_t *dev_mask;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source);
     if (status)
 	goto FINISH;
@@ -1616,11 +1619,11 @@ _cairo_surface_stroke (cairo_surface_t		*surface,
     cairo_matrix_t dev_ctm = *ctm;
     cairo_matrix_t dev_ctm_inverse = *ctm_inverse;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source);
     if (status)
 	return _cairo_surface_set_error (surface, status);
@@ -1660,11 +1663,11 @@ _cairo_surface_fill (cairo_surface_t	*surface,
     cairo_status_t status;
     cairo_pattern_t *dev_source;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     status = _cairo_surface_copy_pattern_for_destination (source, surface, &dev_source);
     if (status)
 	return _cairo_surface_set_error (surface, status);
@@ -1704,8 +1707,6 @@ _cairo_surface_composite_trapezoids (cairo_operator_t		op,
 {
     cairo_int_status_t status;
 
-    assert (! dst->is_snapshot);
-
     /* These operators aren't interpreted the same way by the backends;
      * they are implemented in terms of other operators in cairo-gstate.c
      */
@@ -1714,6 +1715,8 @@ _cairo_surface_composite_trapezoids (cairo_operator_t		op,
     if (dst->status)
 	return dst->status;
 
+    assert (! dst->is_snapshot);
+
     if (dst->finished)
 	return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED);
 
@@ -1757,11 +1760,11 @@ cairo_surface_copy_page (cairo_surface_t *surface)
 {
     cairo_status_t status_ignored;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status_ignored = _cairo_surface_set_error (surface,
 		                                 CAIRO_STATUS_SURFACE_FINISHED);
@@ -1794,11 +1797,11 @@ cairo_surface_show_page (cairo_surface_t *surface)
 {
     cairo_status_t status_ignored;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return;
 
+    assert (! surface->is_snapshot);
+
     if (surface->finished) {
 	status_ignored = _cairo_surface_set_error (surface,
 		                                 CAIRO_STATUS_SURFACE_FINISHED);
@@ -1915,9 +1918,6 @@ _cairo_surface_set_clip_region (cairo_surface_t	    *surface,
     if (surface->status)
 	return surface->status;
 
-    if (surface->finished)
-	return _cairo_surface_set_error (surface,CAIRO_STATUS_SURFACE_FINISHED);
-
     assert (surface->backend->set_clip_region != NULL);
 
     surface->current_clip_serial = serial;
@@ -2232,11 +2232,11 @@ _cairo_surface_show_text_glyphs (cairo_surface_t	    *surface,
     cairo_scaled_font_t *dev_scaled_font = scaled_font;
     cairo_pattern_t *dev_source;
 
-    assert (! surface->is_snapshot);
-
     if (surface->status)
 	return surface->status;
 
+    assert (! surface->is_snapshot);
+
     if (!num_glyphs && !utf8_len)
 	return CAIRO_STATUS_SUCCESS;
 
@@ -2359,11 +2359,11 @@ _cairo_surface_old_show_glyphs (cairo_scaled_font_t	*scaled_font,
 {
     cairo_status_t status;
 
-    assert (! dst->is_snapshot);
-
     if (dst->status)
 	return dst->status;
 
+    assert (! dst->is_snapshot);
+
     if (dst->finished)
 	return _cairo_surface_set_error (dst, CAIRO_STATUS_SURFACE_FINISHED);
 
@@ -2483,11 +2483,11 @@ _cairo_surface_composite_fixup_unbounded (cairo_surface_t            *dst,
     cairo_rectangle_int_t *src_rectangle = NULL;
     cairo_rectangle_int_t *mask_rectangle = NULL;
 
-    assert (! dst->is_snapshot);
-
     if (dst->status)
 	return dst->status;
 
+    assert (! dst->is_snapshot);
+
     /* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
      * non-repeating sources and masks. Other sources and masks can be ignored.
      */
@@ -2561,11 +2561,11 @@ _cairo_surface_composite_shape_fixup_unbounded (cairo_surface_t            *dst,
     cairo_rectangle_int_t *src_rectangle = NULL;
     cairo_rectangle_int_t *mask_rectangle = NULL;
 
-    assert (! dst->is_snapshot);
-
     if (dst->status)
 	return dst->status;
 
+    assert (! dst->is_snapshot);
+
     /* The RENDER/libpixman operators are clipped to the bounds of the untransformed,
      * non-repeating sources and masks. Other sources and masks can be ignored.
      */
commit 644e78ca4e6f72499fcf763acd53fa7235b0226d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 20:34:13 2008 +0100

    [svg] Lock the scaled_font whilst emitting glyphs.
    
    We need to hold the scaled_font mutex whilst looking upon glyphs so lock
    the font whilst iterating over the font subset.

diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index ca323dc..428c8fb 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -733,6 +733,7 @@ _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t	*font_subset,
     unsigned int i;
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
 
+    CAIRO_MUTEX_LOCK (font_subset->scaled_font->mutex);
     for (i = 0; i < font_subset->num_glyphs; i++) {
 	status = _cairo_svg_document_emit_glyph (document,
 					         font_subset->scaled_font,
@@ -741,6 +742,7 @@ _cairo_svg_document_emit_font_subset (cairo_scaled_font_subset_t	*font_subset,
 	if (status)
 	    break;
     }
+    CAIRO_MUTEX_UNLOCK (font_subset->scaled_font->mutex);
 
     return status;
 }
commit 4587e9e4f5c46656108b05f7a4841f78df14de26
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 19:11:09 2008 +0100

    [scaled-font-subsets] Add locking to unscaled font.
    
    During map_glyph() we are passed a locked scaled_font for which we are
    asked to add a glyph from that font into the active subsets for the
    surface. This means that we eventually call scaled_glyph_lookup() to load
    the glyph. But first, we attempt to find an existing an existing sub_font
    for glyph, creating a new sub_font as necessary (possibly using an
    entirely different unhinted scaled font). So before accessing the glyph
    cache we need to make sure that we are holding the appropriate mutexes.

diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 46e3b69..3bf9073 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -133,7 +133,7 @@ typedef struct _cairo_string_entry {
 static cairo_status_t
 _cairo_sub_font_map_glyph (cairo_sub_font_t	*sub_font,
 			   unsigned long	 scaled_font_glyph_index,
-			   const char * 	 utf8,
+			   const char *		 utf8,
 			   int			 utf8_len,
                            cairo_scaled_font_subsets_glyph_t *subset_glyph);
 
@@ -649,7 +649,7 @@ cairo_status_t
 _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t	*subsets,
 				      cairo_scaled_font_t		*scaled_font,
 				      unsigned long			 scaled_font_glyph_index,
-				      const char * 			 utf8,
+				      const char *			 utf8,
 				      int				 utf8_len,
                                       cairo_scaled_font_subsets_glyph_t *subset_glyph)
 {
@@ -745,6 +745,7 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t	*subsets,
                 subset_glyph->is_composite = FALSE;
             }
 
+	    CAIRO_MUTEX_LOCK (unscaled_font->mutex);
             status = _cairo_sub_font_create (subsets,
 					     unscaled_font,
 					     subsets->num_sub_fonts,
@@ -752,6 +753,8 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t	*subsets,
 					     subset_glyph->is_scaled,
 					     subset_glyph->is_composite,
 					     &sub_font);
+	    CAIRO_MUTEX_UNLOCK (unscaled_font->mutex);
+
             if (status) {
 		cairo_scaled_font_destroy (unscaled_font);
                 return status;
@@ -812,10 +815,18 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t	*subsets,
         }
     }
 
-    return _cairo_sub_font_map_glyph (sub_font,
-                                      scaled_font_glyph_index,
-				      utf8, utf8_len,
-                                      subset_glyph);
+    if (sub_font->scaled_font != scaled_font)
+	CAIRO_MUTEX_LOCK (sub_font->scaled_font->mutex);
+
+    status =  _cairo_sub_font_map_glyph (sub_font,
+					 scaled_font_glyph_index,
+					 utf8, utf8_len,
+					 subset_glyph);
+
+    if (sub_font->scaled_font != scaled_font)
+	CAIRO_MUTEX_UNLOCK (sub_font->scaled_font->mutex);
+
+    return status;
 }
 
 static cairo_status_t
commit d3a6651237a276c410885578c41fb505f83372ed
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 14:37:42 2008 +0100

    [xlib] Set return code after failing to allocate glyph surface.
    
    Although we checked for an allocation error, we missed propagating the
    status to the return error code.

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 8d67106..7bf34ee 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -3419,7 +3419,7 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
 					     CAIRO_SCALED_GLYPH_INFO_METRICS |
 					     CAIRO_SCALED_GLYPH_INFO_SURFACE,
 					     pscaled_glyph);
-	if (status != CAIRO_STATUS_SUCCESS)
+	if (status)
 	    return status;
 
 	scaled_glyph = *pscaled_glyph;
@@ -3457,7 +3457,8 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
 	cairo_surface_t *tmp_surface;
 
 	tmp_surface = cairo_image_surface_create (glyphset_info->format, 1, 1);
-	if (tmp_surface->status)
+	status = tmp_surface->status;
+	if (status)
 	    goto BAIL;
 
 	cr = cairo_create (tmp_surface);
@@ -3486,7 +3487,8 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
 	tmp_surface = cairo_image_surface_create (glyphset_info->format,
 						  glyph_surface->width,
 						  glyph_surface->height);
-	if (tmp_surface->status)
+	status = tmp_surface->status;
+	if (status)
 	    goto BAIL;
 
 	tmp_surface->device_transform = glyph_surface->base.device_transform;
commit bfc3a72cff1a3f9641dae7c9f121598a14eb9a5a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 14:06:03 2008 +0100

    [gstate] Propagate error from clip.
    
    During clip transformation we query the clip extents which can trigger a
    fatal error. Check and propagate.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index e022efc..e9387f5 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -60,7 +60,7 @@ _cairo_gstate_ensure_scaled_font (cairo_gstate_t *gstate);
 static void
 _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate);
 
-static void
+static cairo_status_t
 _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
                                            const cairo_glyph_t *glyphs,
                                            int                  num_glyphs,
@@ -1574,10 +1574,12 @@ _cairo_gstate_show_text_glyphs (cairo_gstate_t		   *gstate,
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
-    _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs,
-                                               transformed_glyphs, &num_glyphs);
+    status = _cairo_gstate_transform_glyphs_to_backend (gstate,
+							glyphs, num_glyphs,
+							transformed_glyphs,
+							&num_glyphs);
 
-    if (!num_glyphs)
+    if (status || num_glyphs == 0)
 	goto CLEANUP_GLYPHS;
 
     status = _cairo_gstate_copy_transformed_source (gstate, &source_pattern.base);
@@ -1661,8 +1663,12 @@ _cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
     if (transformed_glyphs == NULL)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    _cairo_gstate_transform_glyphs_to_backend (gstate, glyphs, num_glyphs,
-                                               transformed_glyphs, NULL);
+    status = _cairo_gstate_transform_glyphs_to_backend (gstate,
+							glyphs, num_glyphs,
+							transformed_glyphs,
+							NULL);
+    if (status)
+	goto CLEANUP_GLYPHS;
 
     CAIRO_MUTEX_LOCK (gstate->scaled_font->mutex);
     status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
@@ -1670,6 +1676,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t      *gstate,
 					    path);
     CAIRO_MUTEX_UNLOCK (gstate->scaled_font->mutex);
 
+  CLEANUP_GLYPHS:
     if (transformed_glyphs != stack_transformed_glyphs)
       cairo_glyph_free (transformed_glyphs);
 
@@ -1709,7 +1716,7 @@ _cairo_gstate_get_antialias (cairo_gstate_t *gstate)
  * This also uses information from the scaled font and the surface to
  * cull/drop glyphs that will not be visible.
  **/
-static void
+static cairo_status_t
 _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
                                            const cairo_glyph_t *glyphs,
                                            int                  num_glyphs,
@@ -1722,20 +1729,24 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
     cairo_matrix_t *device_transform = &gstate->target->device_transform;
     cairo_bool_t drop = FALSE;
     double x1 = 0, x2 = 0, y1 = 0, y2 = 0;
+    cairo_status_t status;
 
     if (num_transformed_glyphs != NULL) {
 	cairo_rectangle_int_t surface_extents;
 	double scale = _cairo_scaled_font_get_max_scale (gstate->scaled_font);
 
 	drop = TRUE;
+	status = _cairo_gstate_int_clip_extents (gstate, &surface_extents);
+	if (_cairo_status_is_error (status))
+	    return status;
 
-	if (_cairo_gstate_int_clip_extents (gstate, &surface_extents))
+	if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 	    drop = FALSE; /* unbounded surface */
-	else {
+	} else {
 	    if (surface_extents.width == 0 || surface_extents.height == 0) {
 	      /* No visible area.  Don't draw anything */
 	      *num_transformed_glyphs = 0;
-	      return;
+	      return CAIRO_STATUS_SUCCESS;
 	    }
 	    /* XXX We currently drop any glyphs that has its position outside
 	     * of the surface boundaries by a safety margin depending on the
@@ -1815,4 +1826,6 @@ _cairo_gstate_transform_glyphs_to_backend (cairo_gstate_t      *gstate,
         }
 	*num_transformed_glyphs = j;
     }
+
+    return CAIRO_STATUS_SUCCESS;
 }
commit 7975cf3f2c0bc28b8b3c916d5ba725abb38b300d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 13:21:28 2008 +0100

    [paginated] Free local reference to target on error path.
    
    On the error path we must destroy the local reference to the target
    surface.

diff --git a/src/cairo-paginated-surface.c b/src/cairo-paginated-surface.c
index 131d289..97a1f21 100644
--- a/src/cairo-paginated-surface.c
+++ b/src/cairo-paginated-surface.c
@@ -108,6 +108,7 @@ _cairo_paginated_surface_create (cairo_surface_t				*target,
     return &surface->base;
 
   FAIL_CLEANUP_SURFACE:
+    cairo_surface_destroy (target);
     free (surface);
   FAIL:
     return _cairo_surface_create_in_error (status);
commit 4662204a2a02d50e674f121d82bbb4fe1b8f1436
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 12:44:45 2008 +0100

    [test-paginated] Fix double free of surface along error path.
    
    The ownership of the surface is transferred to the paginated surface, so
    we do not need to destroy it ourselves along the error path.

diff --git a/src/test-paginated-surface.c b/src/test-paginated-surface.c
index 7ae58e2..fb7e2e4 100644
--- a/src/test-paginated-surface.c
+++ b/src/test-paginated-surface.c
@@ -93,14 +93,16 @@ _cairo_test_paginated_surface_create_for_data (unsigned char		*data,
     paginated =  _cairo_paginated_surface_create (&surface->base,
 	                                          content, width, height,
 						  &test_paginated_surface_paginated_backend);
-
-    /* paginated keeps the only reference to surface now, drop ours */
-    cairo_surface_destroy (&surface->base);
-
-    if (paginated->status) {
-	cairo_surface_destroy (target);
+    status = paginated->status;
+    if (status == CAIRO_STATUS_SUCCESS) {
+	/* paginated keeps the only reference to surface now, drop ours */
+	cairo_surface_destroy (&surface->base);
+	return paginated;
     }
-    return paginated;
+
+    cairo_surface_destroy (target);
+    free (surface);
+    return _cairo_surface_create_in_error (status);
 }
 
 static cairo_status_t
commit bf3202fcfd281be3fa62c2d6719377a8f2a0dec4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 12:33:22 2008 +0100

    [xlib] Fix double free of Pixmap along error path.
    
    If the paint fails then we attempt to free the Pixmap ourselves and via
    the cairo_surface_destroy (as it also believes that it owns the Pixmap).

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 0d80536..8d67106 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1288,9 +1288,10 @@ _cairo_xlib_surface_create_solid_pattern_surface (void                  *abstrac
     status = surface->base.status;
     if (status)
 	goto BAIL;
-    surface->owns_pixmap = TRUE;
 
-    status = _cairo_surface_paint (&image->base, CAIRO_OPERATOR_SOURCE, &solid_pattern->base);
+    status = _cairo_surface_paint (&image->base,
+				   CAIRO_OPERATOR_SOURCE,
+				   &solid_pattern->base);
     if (status)
 	goto BAIL;
 
@@ -1305,13 +1306,16 @@ _cairo_xlib_surface_create_solid_pattern_surface (void                  *abstrac
   BAIL:
     cairo_surface_destroy (&image->base);
 
-    if (status && surface) {
-	XFreePixmap (other->dpy, pixmap);
+    if (status) {
+	if (pixmap != None)
+	    XFreePixmap (other->dpy, pixmap);
 	cairo_surface_destroy (&surface->base);
-	surface = NULL;
+
+	return _cairo_surface_create_in_error (status);
     }
 
-    return (cairo_surface_t *) surface;
+    surface->owns_pixmap = TRUE;
+    return &surface->base;
 }
 
 static cairo_status_t
commit 901f0b540c764e131dd9745def329308af61b3d5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 12:19:09 2008 +0100

    Review backend->create_similar()
    
    Avoid masking fatal errors by enforcing any error to be returned via an
    error surface, so that the NULL return only means UNSUPPORTED. A few
    backends called their create_similar() directly, without correctly checking
    for a potential NULL (for example, the directfb backend was a timebomb,
    since it used NULL to indicate out-of-memory).

diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c
index d14f8f1..57bc654 100644
--- a/src/cairo-directfb-surface.c
+++ b/src/cairo-directfb-surface.c
@@ -456,10 +456,8 @@ _cairo_directfb_surface_create_similar (void            *abstract_src,
 
     format = _cairo_format_from_content (content);             
     surface = calloc (1, sizeof(cairo_directfb_surface_t));
-    if (!surface) {
-        _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-        return NULL;
-    }
+    if (surface == NULL)
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
    
     surface->dfb = source->dfb;
     
@@ -644,8 +642,10 @@ _cairo_directfb_surface_clone_similar (void             *abstract_surface,
                 _cairo_directfb_surface_create_similar (surface, 
                             _cairo_content_from_format (image_src->format),
                             image_src->width, image_src->height);
-        if (!clone)
-            return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        if (clone == NULL)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	if (clone->base.status)
+	    return clone->base.status;
             
         ret = clone->dfbsurface->Lock (clone->dfbsurface, 
                                    DSLF_WRITE, (void *)&dst, &pitch);
@@ -766,8 +766,10 @@ _directfb_prepare_composite (cairo_directfb_surface_t    *dst,
         if (!dst->color) {
             dst->color = _cairo_directfb_surface_create_similar (dst,
                                                 CAIRO_CONTENT_COLOR_ALPHA, 1, 1);
-            if (!dst->color)
-                return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+            if (dst->color == NULL)
+		return CAIRO_INT_STATUS_UNSUPPORTED;
+	    if (dst->color->base.status)
+		return (dst->color->base.status);
         }
         
         src = (cairo_directfb_surface_t *)dst->color;
diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c
index aa138db..9a148bf 100644
--- a/src/cairo-glitz-surface.c
+++ b/src/cairo-glitz-surface.c
@@ -543,6 +543,8 @@ _cairo_glitz_surface_clone_similar (void	    *abstract_surface,
 	    _cairo_glitz_surface_create_similar (surface, content,
 						 image_src->width,
 						 image_src->height);
+	if (clone == NULL)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
 	if (clone->base.status)
 	    return clone->base.status;
 
@@ -806,8 +808,7 @@ _cairo_glitz_pattern_acquire_surface (cairo_pattern_t	              *pattern,
 	    _cairo_surface_create_similar_scratch (&dst->base,
 						   CAIRO_CONTENT_COLOR_ALPHA,
 						   gradient->n_stops, 1);
-	if (src->base.status)
-	{
+	if (src->base.status) {
 	    glitz_buffer_destroy (buffer);
 	    free (data);
 	    return src->base.status;
@@ -1280,8 +1281,13 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t  op,
 	    _cairo_glitz_surface_create_similar (&dst->base,
 						 CAIRO_CONTENT_ALPHA,
 						 2, 1);
-	if (mask->base.status)
-	{
+	if (mask == NULL) {
+	    _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
+	    if (src_pattern == &tmp_src_pattern.base)
+		_cairo_pattern_fini (&tmp_src_pattern.base);
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+	}
+	if (mask->base.status) {
 	    _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
 	    if (src_pattern == &tmp_src_pattern.base)
 		_cairo_pattern_fini (&tmp_src_pattern.base);
@@ -1389,8 +1395,7 @@ _cairo_glitz_surface_composite_trapezoids (cairo_operator_t  op,
 	    _cairo_surface_create_similar_scratch (&dst->base,
 						   CAIRO_CONTENT_ALPHA,
 						   width, height);
-	if (mask->base.status)
-	{
+	if (mask->base.status) {
 	    _cairo_glitz_pattern_release_surface (src_pattern, src, &attributes);
 	    free (data);
 	    cairo_surface_destroy (&image->base);
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 1c8bbaa..41fde0b 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -1565,8 +1565,8 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
 
     // verify width and height of surface
     if (!_cairo_quartz_verify_surface_size(width, height)) {
-	_cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return NULL;
+	return _cairo_surface_create_in_error (_cairo_error
+					       (CAIRO_STATUS_NO_MEMORY));
     }
 
     return cairo_quartz_surface_create (format, width, height);
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 3f5448e..014acf3 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -238,12 +238,8 @@ _cairo_surface_create_similar_scratch (cairo_surface_t *other,
 
     if (other->backend->create_similar) {
 	surface = other->backend->create_similar (other, content, width, height);
-	/* It's not an error if the backend didn't create a valid
-	 * surface---it may just not be supported. */
-	if (surface && surface->status) {
-	    cairo_surface_destroy (surface);
-	    surface = NULL;
-	}
+	if (surface != NULL && surface->status)
+	    return surface;
     }
 
     if (surface == NULL)
diff --git a/src/cairo-win32-surface.c b/src/cairo-win32-surface.c
index b85b6e5..a473c38 100644
--- a/src/cairo-win32-surface.c
+++ b/src/cairo-win32-surface.c
@@ -446,10 +446,16 @@ _cairo_win32_surface_clone_similar (void *abstract_surface,
 
     src_content = cairo_surface_get_content(src);
     new_surface =
-	_cairo_win32_surface_create_similar_internal (abstract_surface, src_content, width, height, FALSE);
+	_cairo_win32_surface_create_similar_internal (abstract_surface,
+						      src_content,
+						      width, height,
+						      FALSE);
+    if (new_surface == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    if (cairo_surface_status(new_surface))
-	return cairo_surface_status(new_surface);
+    status = new_surface->status;
+    if (status)
+	return status;
 
     _cairo_pattern_init_for_surface (&pattern, src);
 
@@ -516,8 +522,10 @@ _cairo_win32_surface_get_subimage (cairo_win32_surface_t  *surface,
     local =
 	(cairo_win32_surface_t *) _cairo_win32_surface_create_similar_internal
 	(surface, content, width, height, TRUE);
+    if (local == NULL)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
     if (local->base.status)
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	return local->base.status;
 
     status = CAIRO_INT_STATUS_UNSUPPORTED;
 
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index e52b83c..895d8e8 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -722,6 +722,8 @@ _cairo_xcb_surface_clone_similar (void			*abstract_surface,
 
 	clone = (cairo_xcb_surface_t *)
 	    _cairo_xcb_surface_create_similar (surface, content, width, height);
+	if (clone == NULL)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
 	if (clone->base.status)
 	    return clone->base.status;
 
commit 9529699028a5c7a3b0f81f945d25f26285247a11
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Oct 19 09:35:07 2008 +0100

    [test/user-font-rescale] Check and propagate errors.
    
    We need to check the error status on set_user_data() to be sure that the
    data has been set and error otherwise.

diff --git a/test/user-font-rescale.c b/test/user-font-rescale.c
index 4314961..bcee414 100644
--- a/test/user-font-rescale.c
+++ b/test/user-font-rescale.c
@@ -34,7 +34,7 @@
 #define TEXT_SIZE 32
 #define WIDTH  (TEXT_SIZE * 13.75 + 2*BORDER)
 #define HEIGHT ((TEXT_SIZE + 2*BORDER)*3 + BORDER)
-#define TEXT   "test of rescaled glyphs";
+#define TEXT   "test of rescaled glyphs"
 
 static cairo_test_draw_function_t draw;
 
@@ -178,15 +178,17 @@ static void rescale_font_closure_destroy (void *data)
     free (r);
 }
 
-static cairo_font_face_t *
+static cairo_status_t
 create_rescaled_font (cairo_font_face_t *substitute_font,
 		      int glyph_start,
 		      int glyph_count,
-		      double *desired_width)
+		      double *desired_width,
+		      cairo_font_face_t **out)
 {
     cairo_font_face_t *user_font_face;
     struct rescaled_font *r;
     cairo_font_options_t *options;
+    cairo_status_t status;
     cairo_matrix_t m;
     unsigned long i;
 
@@ -221,18 +223,24 @@ create_rescaled_font (cairo_font_face_t *substitute_font,
 	r->rescale_factor[i] = strtod ("NaN", NULL);
     }
 
-    cairo_font_face_set_user_data (user_font_face, &rescale_font_closure_key,
-				   r, rescale_font_closure_destroy);
+    status = cairo_font_face_set_user_data (user_font_face,
+					    &rescale_font_closure_key,
+					    r, rescale_font_closure_destroy);
+    if (status) {
+	rescale_font_closure_destroy (r);
+	cairo_font_face_destroy (user_font_face);
+	return status;
+    }
 
-    return user_font_face;
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
 }
 
-
-
-static cairo_font_face_t *
+static cairo_status_t
 get_user_font_face (cairo_font_face_t *substitute_font,
 		    const char *text,
-		    cairo_font_face_t *old)
+		    cairo_font_face_t *old,
+		    cairo_font_face_t **out)
 {
     cairo_font_options_t *options;
     cairo_matrix_t m;
@@ -242,7 +250,7 @@ get_user_font_face (cairo_font_face_t *substitute_font,
     int count;
     int num_glyphs;
     unsigned long min_index, max_index;
-    cairo_font_face_t *ret;
+    cairo_status_t status;
 
     cairo_glyph_t *glyphs = NULL;
 
@@ -254,10 +262,15 @@ get_user_font_face (cairo_font_face_t *substitute_font,
     cairo_matrix_init_identity (&m);
     measure = cairo_scaled_font_create (old, &m, &m, options);
 
-    cairo_scaled_font_text_to_glyphs (measure, 0, 0,
-				      text, -1,
-				      &glyphs, &num_glyphs,
-				      NULL, NULL, NULL);
+    status = cairo_scaled_font_text_to_glyphs (measure, 0, 0,
+					       text, -1,
+					       &glyphs, &num_glyphs,
+					       NULL, NULL, NULL);
+    if (status) {
+	cairo_font_options_destroy (options);
+	cairo_scaled_font_destroy (measure);
+	return status;
+    }
 
     /* find the glyph range the text covers */
     max_index = glyphs[0].index;
@@ -283,9 +296,11 @@ get_user_font_face (cairo_font_face_t *substitute_font,
     cairo_font_options_destroy (options);
     cairo_scaled_font_destroy (measure);
 
-    ret = create_rescaled_font (substitute_font, min_index, count, widths);
+    status = create_rescaled_font (substitute_font,
+				   min_index, count, widths,
+				   out);
     free (widths);
-    return ret;
+    return status;
 }
 
 static cairo_test_status_t
@@ -297,6 +312,7 @@ draw (cairo_t *cr, int width, int height)
     cairo_font_face_t *old;
     cairo_font_face_t *substitute;
     const char text[] = TEXT;
+    cairo_status_t status;
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -321,19 +337,20 @@ draw (cairo_t *cr, int width, int height)
 			    "Bitstream Vera Sans Mono",
 			    CAIRO_FONT_SLANT_NORMAL,
 			    CAIRO_FONT_WEIGHT_NORMAL);
-    substitute = cairo_font_face_reference (cairo_get_font_face (cr));
+    substitute = cairo_get_font_face (cr);
 
-    rescaled = get_user_font_face (substitute, text, old);
+    status = get_user_font_face (substitute, text, old, &rescaled);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
     cairo_set_font_face (cr, rescaled);
-
-    cairo_font_face_destroy (substitute);
+    cairo_font_face_destroy (rescaled);
 
     cairo_set_source_rgb (cr, 0, 0, 1);
     cairo_move_to (cr, BORDER, BORDER + font_extents.height + 2*BORDER + font_extents.ascent);
     cairo_show_text (cr, text);
 
-    cairo_font_face_destroy (rescaled);
-
     /* mono text */
     cairo_select_font_face (cr,
 			    "Bitstream Vera Sans Mono",
commit 683de2fea2d8cdeea9759cbf3ab7492d634e7af2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat Oct 18 00:01:39 2008 +0100

    [test/user-font[-proxy]] Check for error from set_user_data().
    
    Check in case we fail to attach our user_data.

diff --git a/test/user-font-proxy.c b/test/user-font-proxy.c
index 1a8ad68..f3e1f0e 100644
--- a/test/user-font-proxy.c
+++ b/test/user-font-proxy.c
@@ -115,33 +115,37 @@ test_scaled_font_text_to_glyphs (cairo_scaled_font_t        *scaled_font,
 					   clusters, num_clusters, cluster_flags);
 }
 
-static cairo_font_face_t *user_font_face = NULL;
-
-static cairo_font_face_t *
-get_user_font_face (void)
+static cairo_status_t
+_user_font_face_create (cairo_font_face_t **out)
 {
-    if (!user_font_face) {
-	cairo_font_face_t *fallback_font_face;
-
-	user_font_face = cairo_user_font_face_create ();
-	cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
-	cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
-	cairo_user_font_face_set_text_to_glyphs_func   (user_font_face, test_scaled_font_text_to_glyphs);
-
-	/* This also happens to be default font face on cairo_t, so does
-	 * not make much sense here.  For demonstration only.
-	 */
-	fallback_font_face = cairo_toy_font_face_create ("",
-							 CAIRO_FONT_SLANT_NORMAL,
-							 CAIRO_FONT_WEIGHT_NORMAL);
-
-	cairo_font_face_set_user_data (user_font_face,
-				       &fallback_font_key,
-				       fallback_font_face,
-				       (cairo_destroy_func_t) cairo_font_face_destroy);
+    cairo_font_face_t *user_font_face;
+    cairo_font_face_t *fallback_font_face;
+    cairo_status_t status;
+
+    user_font_face = cairo_user_font_face_create ();
+    cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
+    cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
+    cairo_user_font_face_set_text_to_glyphs_func   (user_font_face, test_scaled_font_text_to_glyphs);
+
+    /* This also happens to be default font face on cairo_t, so does
+     * not make much sense here.  For demonstration only.
+     */
+    fallback_font_face = cairo_toy_font_face_create ("",
+						     CAIRO_FONT_SLANT_NORMAL,
+						     CAIRO_FONT_WEIGHT_NORMAL);
+
+    status = cairo_font_face_set_user_data (user_font_face,
+					    &fallback_font_key,
+					    fallback_font_face,
+					    (cairo_destroy_func_t) cairo_font_face_destroy);
+    if (status) {
+	cairo_font_face_destroy (fallback_font_face);
+	cairo_font_face_destroy (user_font_face);
+	return status;
     }
 
-    return user_font_face;
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_test_status_t
@@ -150,6 +154,8 @@ draw (cairo_t *cr, int width, int height)
     const char text[] = TEXT;
     cairo_font_extents_t font_extents;
     cairo_text_extents_t extents;
+    cairo_font_face_t *font_face;
+    cairo_status_t status;
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -159,7 +165,15 @@ draw (cairo_t *cr, int width, int height)
     cairo_rotate (cr, .6);
 #endif
 
-    cairo_set_font_face (cr, get_user_font_face ());
+    status = _user_font_face_create (&font_face);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
+
+    cairo_set_font_face (cr, font_face);
+    cairo_font_face_destroy (font_face);
+
     cairo_set_font_size (cr, TEXT_SIZE);
 
     cairo_font_extents (cr, &font_extents);
@@ -200,11 +214,6 @@ draw (cairo_t *cr, int width, int height)
     cairo_text_path (cr, text);
     cairo_fill (cr);
 
-    if (user_font_face) {
-        cairo_font_face_destroy (user_font_face);
-	user_font_face = NULL;
-    }
-
     return CAIRO_TEST_SUCCESS;
 }
 
diff --git a/test/user-font.c b/test/user-font.c
index 1f702df..d90cd49 100644
--- a/test/user-font.c
+++ b/test/user-font.c
@@ -145,8 +145,8 @@ test_scaled_font_render_glyph (cairo_scaled_font_t  *scaled_font,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_font_face_t *
-_user_font_face_create (void)
+static cairo_status_t
+_user_font_face_create (cairo_font_face_t **out)
 {
     /* Simple glyph definition: 1 - 15 means lineto (or moveto for first
      * point) for one of the points on this grid:
@@ -181,15 +181,23 @@ _user_font_face_create (void)
     };
 
     cairo_font_face_t *user_font_face;
+    cairo_status_t status;
 
     user_font_face = cairo_user_font_face_create ();
     cairo_user_font_face_set_init_func             (user_font_face, test_scaled_font_init);
     cairo_user_font_face_set_render_glyph_func     (user_font_face, test_scaled_font_render_glyph);
     cairo_user_font_face_set_unicode_to_glyph_func (user_font_face, test_scaled_font_unicode_to_glyph);
 
-    cairo_font_face_set_user_data (user_font_face, &test_font_face_glyphs_key, (void*) glyphs, NULL);
+    status = cairo_font_face_set_user_data (user_font_face,
+					    &test_font_face_glyphs_key,
+					    (void*) glyphs, NULL);
+    if (status) {
+	cairo_font_face_destroy (user_font_face);
+	return status;
+    }
 
-    return user_font_face;
+    *out = user_font_face;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_test_status_t
@@ -199,6 +207,7 @@ draw (cairo_t *cr, int width, int height)
     const char text[] = TEXT;
     cairo_font_extents_t font_extents;
     cairo_text_extents_t extents;
+    cairo_status_t status;
 
     cairo_set_source_rgb (cr, 1, 1, 1);
     cairo_paint (cr);
@@ -208,7 +217,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_rotate (cr, .6);
 #endif
 
-    font_face = _user_font_face_create ();
+    status = _user_font_face_create (&font_face);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
+
     cairo_set_font_face (cr, font_face);
     cairo_font_face_destroy (font_face);
 
commit 198392990082e01443bbda5c0cbe6dc8bf090baf
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 22:14:49 2008 +0100

    [test/bitmap] Leak on error path.
    
    Free the FcPattern on error.

diff --git a/test/bitmap-font.c b/test/bitmap-font.c
index 6d0b219..558ab79 100644
--- a/test/bitmap-font.c
+++ b/test/bitmap-font.c
@@ -62,9 +62,15 @@ static cairo_test_status_t
 check_font_extents (const cairo_test_context_t *ctx, cairo_t *cr, const char *comment)
 {
     cairo_font_extents_t font_extents, ref_font_extents = {11, 2, 13, 6, 0};
+    cairo_status_t status;
 
     memset (&font_extents, 0xff, sizeof (cairo_font_extents_t));
     cairo_font_extents (cr, &font_extents);
+
+    status = cairo_status (cr);
+    if (status)
+	return cairo_test_status_from_status (ctx, status);
+
     if (! font_extents_equal (&font_extents, &ref_font_extents)) {
 	cairo_test_log (ctx, "Error: %s: cairo_font_extents(); extents (%g, %g, %g, %g, %g)\n",
 			comment,
@@ -111,6 +117,7 @@ draw (cairo_t *cr, int width, int height)
 	cairo_test_log (ctx, "Error creating font face for %s: %s\n",
 			filename,
 			cairo_status_to_string (status));
+	FcPatternDestroy (pattern);
 	return CAIRO_TEST_FAILURE;
     }
 
@@ -118,6 +125,7 @@ draw (cairo_t *cr, int width, int height)
 	cairo_test_log (ctx, "Unexpected value from cairo_font_face_get_type: %d (expected %d)\n",
 			cairo_font_face_get_type (font_face), CAIRO_FONT_TYPE_FT);
 	cairo_font_face_destroy (font_face);
+	FcPatternDestroy (pattern);
 	return CAIRO_TEST_FAILURE;
     }
 
commit 0409be426cb7f67974346a93213dd4675b59776c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 14:26:15 2008 +0100

    [test/font-matrix-translation] Check for OOM
    
    First check that querying the extents did not trigger NO_MEMORY.

diff --git a/test/font-matrix-translation.c b/test/font-matrix-translation.c
index 263a378..416956f 100644
--- a/test/font-matrix-translation.c
+++ b/test/font-matrix-translation.c
@@ -58,6 +58,7 @@ box_text (const cairo_test_context_t *ctx, cairo_t *cr,
     double line_width;
     cairo_text_extents_t extents = {0}, scaled_extents = {0};
     cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
 
     cairo_save (cr);
 
@@ -65,6 +66,10 @@ box_text (const cairo_test_context_t *ctx, cairo_t *cr,
 
     scaled_font = cairo_get_scaled_font (cr);
     cairo_scaled_font_text_extents (scaled_font, TEXT, &scaled_extents);
+    status = cairo_scaled_font_status (scaled_font);
+    if (status)
+	return cairo_test_status_from_status (ctx, status);
+
     if (! text_extents_equal (&extents, &scaled_extents)) {
         cairo_test_log (ctx,
 			"Error: extents differ when they shouldn't:\n"
commit 21360bf6748b30cf58d54561e40a1aa711d8d7af
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 20:44:51 2008 +0100

    [test/source-surface-big-scaled-down] Propagate error.
    
    Propagate error to source.

diff --git a/test/surface-pattern-big-scale-down.c b/test/surface-pattern-big-scale-down.c
index 49cf6ad..21b8000 100644
--- a/test/surface-pattern-big-scale-down.c
+++ b/test/surface-pattern-big-scale-down.c
@@ -37,28 +37,36 @@ static const cairo_test_t test = {
     draw
 };
 
-static void
-setup_source_surface (cairo_surface_t *surface, int w, int h)
+static cairo_surface_t *
+create_source_surface (int w, int h)
 {
-  cairo_t *cr = cairo_create (surface);
+    cairo_surface_t *surface;
+    cairo_t *cr;
 
-  cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
-  cairo_rectangle (cr, 0, 0, w/2, h/2);
-  cairo_fill (cr);
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, SRC_WIDTH, SRC_HEIGHT);
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
 
-  cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
-  cairo_rectangle (cr, w/2, 0, w/2, h/2);
-  cairo_fill (cr);
+    cairo_set_source_rgb (cr, 1.0, 0.0, 0.0);
+    cairo_rectangle (cr, 0, 0, w/2, h/2);
+    cairo_fill (cr);
 
-  cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
-  cairo_rectangle (cr, 0, h/2, w/2, h/2);
-  cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0.0, 1.0, 0.0);
+    cairo_rectangle (cr, w/2, 0, w/2, h/2);
+    cairo_fill (cr);
 
-  cairo_set_source_rgb (cr, 1.0, 1.0, 0.0);
-  cairo_rectangle (cr, w/2, h/2, w/2, h/2);
-  cairo_fill (cr);
+    cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
+    cairo_rectangle (cr, 0, h/2, w/2, h/2);
+    cairo_fill (cr);
+
+    cairo_set_source_rgb (cr, 1.0, 1.0, 0.0);
+    cairo_rectangle (cr, w/2, h/2, w/2, h/2);
+    cairo_fill (cr);
 
-  cairo_destroy (cr);
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
 }
 
 static void
@@ -85,8 +93,7 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_source_rgb (cr, 0, 0, 0);
     cairo_paint (cr);
 
-    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, SRC_WIDTH, SRC_HEIGHT);
-    setup_source_surface (surface, SRC_WIDTH, SRC_HEIGHT);
+    surface = create_source_surface (SRC_WIDTH, SRC_HEIGHT);
 
     pat = cairo_pattern_create_for_surface (surface);
     cairo_surface_destroy (surface);
commit 794160c35baf39593942ba71b45e4e75d2a87103
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 20:37:43 2008 +0100

    [test/surface-pattern] Propagate error.
    
    Use cairo_get_target() to propagate error from secondary context.

diff --git a/test/surface-pattern.c b/test/surface-pattern.c
index 41ffdc9..6afdcda 100644
--- a/test/surface-pattern.c
+++ b/test/surface-pattern.c
@@ -55,6 +55,8 @@ draw (cairo_t *cr, int width, int height)
     surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
 					  surface_size, surface_size);
     cr_surface = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
     cairo_set_source_rgb (cr_surface, 1, 1, 1);
     cairo_rectangle (cr_surface,
 		     0, 0,
@@ -75,12 +77,11 @@ draw (cairo_t *cr, int width, int height)
 		     surface_size / 2, surface_size / 2,
 		     surface_size / 2, surface_size / 2);
     cairo_fill (cr_surface);
-    cairo_destroy (cr_surface);
 
     cairo_scale (cr, 10, 10);
     cairo_rotate (cr, 1.);
-    cairo_set_source_surface (cr, surface, 1.5, 1.5);
-    cairo_surface_destroy (surface);
+    cairo_set_source_surface (cr, cairo_get_target (cr_surface), 1.5, 1.5);
+    cairo_destroy (cr_surface);
 
     cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
     cairo_paint (cr);
commit 0f48cce8d90cf95afcf86d71a932ab4e504b909b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 20:31:57 2008 +0100

    [test/source-clip-scale] Propagate error.
    
    Use cairo_get_target() to propagate error from secondary context.

diff --git a/test/source-clip-scale.c b/test/source-clip-scale.c
index 4c55695..6434eb0 100644
--- a/test/source-clip-scale.c
+++ b/test/source-clip-scale.c
@@ -48,6 +48,7 @@ draw (cairo_t *cr, int width, int height)
 					   CAIRO_CONTENT_COLOR_ALPHA,
 					   SIZE, SIZE);
     cr2 = cairo_create (source);
+    cairo_surface_destroy (source);
 
     /* Fill the source surface with green */
     cairo_set_source_rgb (cr2, 0, 1, 0);
@@ -74,11 +75,10 @@ draw (cairo_t *cr, int width, int height)
     /* Now draw the source surface onto the destination with scaling. */
     cairo_scale (cr, 2.0, 1.0);
 
-    cairo_set_source_surface (cr, source, 0, 0);
-    cairo_paint (cr);
-
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
     cairo_destroy (cr2);
-    cairo_surface_destroy (source);
+
+    cairo_paint (cr);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 6cdbd132ceba159bb7e3ac5612aa79293b21e95a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 20:22:49 2008 +0100

    [test/source-clip] Propagate error.
    
    Use cairo_get_target() to propagate error from the secondary context.

diff --git a/test/source-clip.c b/test/source-clip.c
index b24f95c..1f51f32 100644
--- a/test/source-clip.c
+++ b/test/source-clip.c
@@ -49,6 +49,7 @@ draw (cairo_t *cr, int width, int height)
 					   SIZE, SIZE);
 
     cr2 = cairo_create (source);
+    cairo_surface_destroy (source);
 
     /* Fill the source surface with green */
     cairo_set_source_rgb (cr2, 0, 1, 0);
@@ -69,11 +70,10 @@ draw (cairo_t *cr, int width, int height)
     cairo_paint (cr);
 
     /* Now draw the source surface onto the destination surface */
-    cairo_set_source_surface (cr, source, 0, 0);
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
     cairo_paint (cr);
 
     cairo_destroy (cr2);
-    cairo_surface_destroy (source);
 
     return CAIRO_TEST_SUCCESS;
 }
commit f86f233b3e49c3f401b2fa02987b2193485e8be5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 20:14:06 2008 +0100

    [test/smask] Propagate error.
    
    Use cairo_get_target() to propagate errors from the secondary context.

diff --git a/test/smask.c b/test/smask.c
index 2b1d5b3..d6f5e99 100644
--- a/test/smask.c
+++ b/test/smask.c
@@ -51,6 +51,7 @@ draw (cairo_t *cr, int width, int height)
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -101,7 +102,6 @@ draw (cairo_t *cr, int width, int height)
 		   floor ((width - extents.width) / 2 + 0.5) - extents.x_bearing,
 		   floor (height - extents.height - 0.5) - extents.y_bearing - 5);
     cairo_show_text (cr2, "FG");
-    cairo_destroy (cr2);
 
     cairo_set_source_rgb (cr, 0, 0, 1.0);
     cairo_paint (cr);
@@ -116,8 +116,8 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_source (cr, pattern);
     cairo_pattern_destroy (pattern);
 
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 4490a6c363a8745ba89bfa890519a570d098086e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 19:15:17 2008 +0100

    [test/stroke-image] Propagate error
    
    Use cairo_get_target() to propagate errors from the secondary context.

diff --git a/test/stroke-image.c b/test/stroke-image.c
index 01ad436..6096590 100644
--- a/test/stroke-image.c
+++ b/test/stroke-image.c
@@ -42,8 +42,15 @@ static const cairo_test_t test = {
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
-    cairo_surface_t *isurf = cairo_image_surface_create (CAIRO_FORMAT_RGB24, IMAGE_SIZE, IMAGE_SIZE);
-    cairo_t *cr_image = cairo_create (isurf);
+    cairo_surface_t *image;
+    cairo_t *cr_image;
+
+    cairo_set_source_rgb (cr, 0, 0, 0);
+    cairo_paint (cr);
+
+    image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, IMAGE_SIZE, IMAGE_SIZE);
+    cr_image = cairo_create (image);
+    cairo_surface_destroy (image);
 
     /* Create the image */
     cairo_set_source_rgb (cr_image, 0, 0, 0);
@@ -52,16 +59,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_line_width (cr_image, LINE_WIDTH);
     cairo_arc (cr_image, IMAGE_SIZE/2, IMAGE_SIZE/2, IMAGE_SIZE/2 - LINE_WIDTH/2, 0, M_PI * 2.0);
     cairo_stroke (cr_image);
-    cairo_destroy (cr_image);
 
     /* Now stroke with it */
-    cairo_set_source_rgb (cr, 0, 0, 0);
-    cairo_paint (cr);
-
     cairo_translate (cr, PAD, PAD);
 
-    cairo_set_source_surface (cr, isurf, 0, 0);
-    cairo_surface_destroy (isurf);
+    cairo_set_source_surface (cr, cairo_get_target (cr_image), 0, 0);
+    cairo_destroy (cr_image);
 
     cairo_new_path (cr);
     cairo_set_line_width (cr, LINE_WIDTH);
commit 6e6d7e52a3c16787633e5059ae36f2fb0d03148a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 19:02:31 2008 +0100

    [test/mask] Propagate error from secondary context.
    
    Reset the source every time so that the error is propagate from the
    secondary context using cairo_get_target().

diff --git a/test/mask.c b/test/mask.c
index 44bed87..ec7cb8f 100644
--- a/test/mask.c
+++ b/test/mask.c
@@ -191,7 +191,6 @@ draw (cairo_t *cr, int width, int height)
 {
     const cairo_test_context_t *ctx = cairo_test_get_context (cr);
     cairo_surface_t *tmp_surface;
-    cairo_pattern_t *tmp_pattern;
     size_t i, j, k;
     cairo_t *cr2;
 
@@ -202,10 +201,6 @@ draw (cairo_t *cr, int width, int height)
 						CAIRO_CONTENT_COLOR_ALPHA,
 						IMAGE_WIDTH, IMAGE_HEIGHT);
     cr2 = cairo_create (tmp_surface);
-
-    tmp_pattern = cairo_pattern_create_for_surface (tmp_surface);
-    cairo_set_source (cr, tmp_pattern);
-    cairo_pattern_destroy (tmp_pattern);
     cairo_surface_destroy (tmp_surface);
 
     for (k = 0; k < ARRAY_SIZE (clip_funcs); k++) {
@@ -230,6 +225,7 @@ draw (cairo_t *cr, int width, int height)
 		cairo_restore (cr2);
 
 		/* Copy back to the main pixmap */
+		cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
 		cairo_rectangle (cr, x, y, WIDTH, HEIGHT);
 		cairo_fill (cr);
 	    }
commit a56d4530d6149e2c4fc884a5cfeede2d6ff6d3b6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:54:21 2008 +0100

    [test/smask-text] Propagate error.
    
    Use cairo_get_target() to propagate error from the secondary context.

diff --git a/test/smask-text.c b/test/smask-text.c
index 822ea5a..49eb606 100644
--- a/test/smask-text.c
+++ b/test/smask-text.c
@@ -42,10 +42,14 @@ draw (cairo_t *cr, int width, int height)
     cairo_t *cr2;
     cairo_text_extents_t extents;
 
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
     mask = cairo_surface_create_similar (cairo_get_group_target (cr),
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -72,14 +76,10 @@ draw (cairo_t *cr, int width, int height)
 		   floor ((width - extents.width) / 2 + 0.5) - extents.x_bearing,
 		   floor ((height - extents.height) / 2 - 0.5) - extents.y_bearing);
     cairo_show_text (cr2, "cairo");
-    cairo_destroy (cr2);
-
-    cairo_set_source_rgb (cr, 0, 0, 1.0);
-    cairo_paint (cr);
 
     cairo_set_source_rgb (cr, 1.0, 0, 0);
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit b1e884cd89ab2b83d3b8e072414e4e3f63e3a023
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:52:55 2008 +0100

    [test/smask-stroke] Propagate error.
    
    Use cairo_get_target() to propagate the error from the secondary context.

diff --git a/test/smask-stroke.c b/test/smask-stroke.c
index fa0afd6..88fc9f0 100644
--- a/test/smask-stroke.c
+++ b/test/smask-stroke.c
@@ -41,10 +41,14 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_t *pattern;
     cairo_t *cr2;
 
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
     mask = cairo_surface_create_similar (cairo_get_group_target (cr),
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -62,15 +66,10 @@ draw (cairo_t *cr, int width, int height)
 
     cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.4 * height, 0, 2 * M_PI);
     cairo_stroke (cr2);
-    cairo_destroy (cr2);
-
-
-    cairo_set_source_rgb (cr, 0, 0, 1.0);
-    cairo_paint (cr);
 
     cairo_set_source_rgb (cr, 1.0, 0, 0);
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 546fbc2c0cf87abd627477056bf19063c5aa001a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:50:25 2008 +0100

    [test/smask-paint] Propagate error.
    
    Use cairo_get_target() to propagate errors from the secondary context.

diff --git a/test/smask-paint.c b/test/smask-paint.c
index d0b9e94..8378964 100644
--- a/test/smask-paint.c
+++ b/test/smask-paint.c
@@ -45,6 +45,7 @@ draw (cairo_t *cr, int width, int height)
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -61,7 +62,6 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_destroy (pattern);
 
     cairo_paint (cr2);
-    cairo_destroy (cr2);
 
     cairo_set_source_rgb (cr, 0, 0, 1.0);
     cairo_paint (cr);
@@ -76,8 +76,8 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_source (cr, pattern);
     cairo_pattern_destroy (pattern);
 
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 8bd69132827fa539d9dfb65bd00e00c079827360
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:49:02 2008 +0100

    [test/smask-mask] Propagate error.
    
    Use cairo_get_target() to propagate errors from the secondary contexts.

diff --git a/test/smask-mask.c b/test/smask-mask.c
index c38be37..721ca48 100644
--- a/test/smask-mask.c
+++ b/test/smask-mask.c
@@ -37,23 +37,29 @@ static const cairo_test_t test = {
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
-    cairo_surface_t *mask, *mask2;
+    cairo_surface_t *mask;
     cairo_pattern_t *pattern;
-    cairo_t *cr2;
+    cairo_t *cr2, *cr3;
+
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
 
     mask = cairo_surface_create_similar (cairo_get_group_target (cr),
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
+    cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
-    mask2 = cairo_surface_create_similar (mask,
-				          CAIRO_CONTENT_ALPHA,
-					  width, height);
-    cr2 = cairo_create (mask2);
+    mask = cairo_surface_create_similar (cairo_get_group_target (cr2),
+				       CAIRO_CONTENT_ALPHA,
+				       width, height);
+    cr3 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
-    cairo_save (cr2); {
-	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
-	cairo_paint (cr2);
-    } cairo_restore (cr2);
+    cairo_save (cr3); {
+	cairo_set_operator (cr3, CAIRO_OPERATOR_CLEAR);
+	cairo_paint (cr3);
+    } cairo_restore (cr3);
 
     pattern = cairo_pattern_create_linear (0, 0, width, height);
     cairo_pattern_add_color_stop_rgba (pattern, 0.00, 0., 0., 0., 0.);
@@ -61,13 +67,10 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_add_color_stop_rgba (pattern, 0.50, 1., 1., 1., .5);
     cairo_pattern_add_color_stop_rgba (pattern, 0.75, 1., 1., 1., 1.);
     cairo_pattern_add_color_stop_rgba (pattern, 1.00, 0., 0., 0., 0.);
-    cairo_set_source (cr2, pattern);
+    cairo_set_source (cr3, pattern);
     cairo_pattern_destroy (pattern);
-    cairo_paint (cr2);
-    cairo_destroy (cr2);
-
+    cairo_paint (cr3);
 
-    cr2 = cairo_create (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -84,17 +87,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_source (cr2, pattern);
     cairo_pattern_destroy (pattern);
 
-    cairo_mask_surface (cr2, mask2, 0, 0);
-    cairo_surface_destroy (mask2);
-    cairo_destroy (cr2);
-
-
-    cairo_set_source_rgb (cr, 0, 0, 1.0);
-    cairo_paint (cr);
+    cairo_mask_surface (cr2, cairo_get_target (cr3), 0, 0);
+    cairo_destroy (cr3);
 
     cairo_set_source_rgb (cr, 1.0, 0, 0);
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 9c0fe6c61e92044e8599f49e75d38edcdd6be960
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:45:07 2008 +0100

    [test/smask-image-mask] Propagate error.
    
    Use cairo_get_target() to propagate any error status from the secondary
    context.

diff --git a/test/smask-image-mask.c b/test/smask-image-mask.c
index 1edec29..1f250d1 100644
--- a/test/smask-image-mask.c
+++ b/test/smask-image-mask.c
@@ -46,10 +46,14 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_t *pattern;
     cairo_t *cr2;
 
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
     mask = cairo_surface_create_similar (cairo_get_group_target (cr),
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -72,14 +76,10 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
     cairo_mask (cr2, pattern);
     cairo_pattern_destroy (pattern);
-    cairo_destroy (cr2);
-
-    cairo_set_source_rgb (cr, 0, 0, 1.0);
-    cairo_paint (cr);
 
     cairo_set_source_rgb (cr, 1.0, 0, 0);
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit 373d6c76b65e826f81873b51461bdf3c30024c06
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:43:26 2008 +0100

    [test/smask-fill] Propagate error status.
    
    Use cairo_get_target() to propagate the error from the secondary context.

diff --git a/test/smask-fill.c b/test/smask-fill.c
index 66f8b0d..58d32c1 100644
--- a/test/smask-fill.c
+++ b/test/smask-fill.c
@@ -41,10 +41,14 @@ draw (cairo_t *cr, int width, int height)
     cairo_pattern_t *pattern;
     cairo_t *cr2;
 
+    cairo_set_source_rgb (cr, 0, 0, 1.0);
+    cairo_paint (cr);
+
     mask = cairo_surface_create_similar (cairo_get_group_target (cr),
 				         CAIRO_CONTENT_ALPHA,
 					 width, height);
     cr2 = cairo_create (mask);
+    cairo_surface_destroy (mask);
 
     cairo_save (cr2); {
 	cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -62,15 +66,10 @@ draw (cairo_t *cr, int width, int height)
 
     cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
     cairo_fill (cr2);
-    cairo_destroy (cr2);
-
-
-    cairo_set_source_rgb (cr, 0, 0, 1.0);
-    cairo_paint (cr);
 
     cairo_set_source_rgb (cr, 1.0, 0, 0);
-    cairo_mask_surface (cr, mask, 0, 0);
-    cairo_surface_destroy (mask);
+    cairo_mask_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 
     return CAIRO_TEST_SUCCESS;
 }
commit bb05beaab626d2fca6d219bfd8148dde71073b39
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 15:27:11 2008 +0100

    [test/show-glyphs-many] Check for NO_MEMORY
    
    As we do a manual status check, we need to perform a full check.

diff --git a/test/show-glyphs-many.c b/test/show-glyphs-many.c
index 2231517..c8d570c 100644
--- a/test/show-glyphs-many.c
+++ b/test/show-glyphs-many.c
@@ -104,14 +104,15 @@ get_glyph (cairo_t *cr, const char *utf8, cairo_glyph_t *glyph)
 					       NULL, NULL,
 					       0);
     if (status != CAIRO_STATUS_SUCCESS)
-	return status;
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
 
     if (text_to_glyphs != glyph) {
 	*glyph = text_to_glyphs[0];
 	cairo_glyph_free (text_to_glyphs);
     }
 
-    return CAIRO_STATUS_SUCCESS;
+    return CAIRO_TEST_SUCCESS;
 }
 
 static cairo_test_status_t
@@ -138,7 +139,7 @@ draw (cairo_t *cr, int width, int height)
     for (utf8 = characters; *utf8 != NULL; utf8++) {
 	status = get_glyph (cr, *utf8, &glyphs[0]);
 	if (status)
-	    return CAIRO_TEST_FAILURE;
+	    return status;
 
 	if (glyphs[0].index) {
 	    glyphs[0].x = 1.0;
@@ -153,13 +154,13 @@ draw (cairo_t *cr, int width, int height)
     /* we can pack ~21k 1-byte glyphs into a single XRenderCompositeGlyphs8 */
     status = get_glyph (cr, "m", &glyphs[0]);
     if (status)
-	return CAIRO_TEST_FAILURE;
+	return status;
     for (i=1; i < 21500; i++)
 	glyphs[i] = glyphs[0];
     /* so check expanding the current 1-byte request for 2-byte glyphs */
     status = get_glyph (cr, "μ", &glyphs[i]);
     if (status)
-	return CAIRO_TEST_FAILURE;
+	return status;
     for (j=i+1; j < NUM_GLYPHS; j++)
 	glyphs[j] = glyphs[i];
 
commit c0a4ef76e17dfd43d248c13b7f0665da9b847316
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 14:57:42 2008 +0100

    [test/composite-integer-translate-over-repeat] Propagate error.
    
    Use cairo_get_target() to propagate any errors from the secondary context.

diff --git a/test/composite-integer-translate-over-repeat.c b/test/composite-integer-translate-over-repeat.c
index c691acb..3e9d314 100644
--- a/test/composite-integer-translate-over-repeat.c
+++ b/test/composite-integer-translate-over-repeat.c
@@ -49,8 +49,9 @@ draw (cairo_t *cr, int width, int height)
     cairo_t *cr2;
 
     image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, SIZE2, SIZE2);
-
     cr2 = cairo_create (image);
+    cairo_surface_destroy (image);
+
     cairo_set_source_rgba (cr2, 1, 0, 0, 1);
     cairo_rectangle (cr2, 0, 0, SIZE2/2, SIZE2/2);
     cairo_fill (cr2);
@@ -63,9 +64,10 @@ draw (cairo_t *cr, int width, int height)
     cairo_set_source_rgba (cr2, 1, 1, 0, 1);
     cairo_rectangle (cr2, SIZE2/2, SIZE2/2, SIZE2/2, SIZE2/2);
     cairo_fill (cr2);
+
+    pat = cairo_pattern_create_for_surface (cairo_get_target (cr2));
     cairo_destroy (cr2);
 
-    pat = cairo_pattern_create_for_surface (image);
     cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
 
     cairo_set_source_rgba (cr, 0, 0, 0, 1);
@@ -78,7 +80,6 @@ draw (cairo_t *cr, int width, int height)
     cairo_rectangle (cr, 0, 0, SIZE - OFFSET, SIZE - OFFSET);
     cairo_fill (cr);
 
-    cairo_surface_destroy (image);
     cairo_pattern_destroy (pat);
 
     return CAIRO_TEST_SUCCESS;
commit 4a9e0f0e5ba17a322bbf0f38b2fb418211571d48
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 16:50:32 2008 +0100

    [test/mask] Propagate error from secondary context.
    
    Use cairo_get_target() to infect the primary context with any errors
    raised whilst drawing the mask.

diff --git a/test/mask.c b/test/mask.c
index 711c3eb..44bed87 100644
--- a/test/mask.c
+++ b/test/mask.c
@@ -79,6 +79,7 @@ mask_polygon (cairo_t *cr, int x, int y)
 						 CAIRO_CONTENT_ALPHA,
 						 WIDTH, HEIGHT);
     cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
 
     cairo_save (cr2);
     cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
@@ -97,11 +98,8 @@ mask_polygon (cairo_t *cr, int x, int y)
     cairo_close_path (cr2);
     cairo_fill (cr2);
 
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
     cairo_destroy (cr2);
-
-    cairo_mask_surface (cr, mask_surface, x, y);
-
-    cairo_surface_destroy (mask_surface);
 }
 
 static void
commit eaec1caa94ac871eb881ac354c4442ddd2a308ea
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 16:20:28 2008 +0100

    [test/ft-text-*] Check for errors from fontconfig.
    
    If a match cannot be found, or if any other error occured, then fontconfig
    will return NULL. Check and propagate.

diff --git a/test/ft-text-antialias-none.c b/test/ft-text-antialias-none.c
index 5d03269..e139b2f 100644
--- a/test/ft-text-antialias-none.c
+++ b/test/ft-text-antialias-none.c
@@ -42,8 +42,9 @@ static const cairo_test_t test = {
     draw
 };
 
-static cairo_scaled_font_t *
-create_scaled_font (cairo_t * cr)
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+		    cairo_scaled_font_t **out)
 {
     FcPattern *pattern, *resolved;
     FcResult result;
@@ -51,6 +52,7 @@ create_scaled_font (cairo_t * cr)
     cairo_scaled_font_t *scaled_font;
     cairo_font_options_t *font_options;
     cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
     double pixel_size;
 
     font_options = cairo_font_options_create ();
@@ -58,6 +60,8 @@ create_scaled_font (cairo_t * cr)
     cairo_get_font_options (cr, font_options);
 
     pattern = FcPatternCreate ();
+    if (pattern == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
 
     FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)"Bitstream vera sans");
     FcPatternAddDouble (pattern, FC_SIZE, TEXT_SIZE);
@@ -67,6 +71,10 @@ create_scaled_font (cairo_t * cr)
 
     FcDefaultSubstitute (pattern);
     resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+	FcPatternDestroy (pattern);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
 
     /* turn antialiasing off */
     FcPatternDel (resolved, FC_ANTIALIAS);
@@ -91,14 +99,22 @@ create_scaled_font (cairo_t * cr)
     FcPatternDestroy (pattern);
     FcPatternDestroy (resolved);
 
-    return scaled_font;
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+	cairo_scaled_font_destroy (scaled_font);
+	return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
     cairo_text_extents_t extents;
-    cairo_scaled_font_t * scaled_font;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
     const char black[] = "black", blue[] = "blue";
 
     /* We draw in the default black, so paint white first. */
@@ -107,7 +123,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_paint (cr);
     cairo_restore (cr);
 
-    scaled_font = create_scaled_font (cr);
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
+
     cairo_set_scaled_font (cr, scaled_font);
 
     cairo_set_source_rgb (cr, 0, 0, 0); /* black */
diff --git a/test/ft-text-vertical-layout-type1.c b/test/ft-text-vertical-layout-type1.c
index 3cfb153..1dbe31d 100644
--- a/test/ft-text-vertical-layout-type1.c
+++ b/test/ft-text-vertical-layout-type1.c
@@ -43,8 +43,9 @@ static const cairo_test_t test = {
     draw
 };
 
-static cairo_scaled_font_t *
-create_scaled_font (cairo_t * cr)
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+		    cairo_scaled_font_t **out)
 {
     FcPattern *pattern, *resolved;
     FcResult result;
@@ -52,6 +53,7 @@ create_scaled_font (cairo_t * cr)
     cairo_scaled_font_t *scaled_font;
     cairo_font_options_t *font_options;
     cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
     double pixel_size;
 
     font_options = cairo_font_options_create ();
@@ -59,6 +61,8 @@ create_scaled_font (cairo_t * cr)
     cairo_get_font_options (cr, font_options);
 
     pattern = FcPatternCreate ();
+    if (pattern == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
 
     FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)"Nimbus Sans L");
     FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE);
@@ -68,6 +72,10 @@ create_scaled_font (cairo_t * cr)
 
     FcDefaultSubstitute (pattern);
     resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+	FcPatternDestroy (pattern);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
 
     /* set layout to vertical */
     FcPatternDel (resolved, FC_VERTICAL_LAYOUT);
@@ -93,14 +101,22 @@ create_scaled_font (cairo_t * cr)
     FcPatternDestroy (pattern);
     FcPatternDestroy (resolved);
 
-    return scaled_font;
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+	cairo_scaled_font_destroy (scaled_font);
+	return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
     cairo_text_extents_t extents;
-    cairo_scaled_font_t * scaled_font;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
     const char text[] = "i-W";
     double line_width, x, y;
 
@@ -112,7 +128,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_paint (cr);
     cairo_restore (cr);
 
-    scaled_font = create_scaled_font (cr);
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
+
     cairo_set_scaled_font (cr, scaled_font);
 
     cairo_set_line_width (cr, 1.0);
diff --git a/test/ft-text-vertical-layout-type3.c b/test/ft-text-vertical-layout-type3.c
index c1a8bbf..0f85d73 100644
--- a/test/ft-text-vertical-layout-type3.c
+++ b/test/ft-text-vertical-layout-type3.c
@@ -42,8 +42,9 @@ static const cairo_test_t test = {
     draw
 };
 
-static cairo_scaled_font_t *
-create_scaled_font (cairo_t * cr)
+static cairo_status_t
+create_scaled_font (cairo_t * cr,
+		    cairo_scaled_font_t **out)
 {
     FcPattern *pattern, *resolved;
     FcResult result;
@@ -51,6 +52,7 @@ create_scaled_font (cairo_t * cr)
     cairo_scaled_font_t *scaled_font;
     cairo_font_options_t *font_options;
     cairo_matrix_t font_matrix, ctm;
+    cairo_status_t status;
     double pixel_size;
 
     font_options = cairo_font_options_create ();
@@ -58,6 +60,8 @@ create_scaled_font (cairo_t * cr)
     cairo_get_font_options (cr, font_options);
 
     pattern = FcPatternCreate ();
+    if (pattern == NULL)
+	return CAIRO_STATUS_NO_MEMORY;
 
     FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *)"Bitstream Vera Sans");
     FcPatternAddDouble (pattern, FC_PIXEL_SIZE, TEXT_SIZE);
@@ -67,6 +71,10 @@ create_scaled_font (cairo_t * cr)
 
     FcDefaultSubstitute (pattern);
     resolved = FcFontMatch (NULL, pattern, &result);
+    if (resolved == NULL) {
+	FcPatternDestroy (pattern);
+	return CAIRO_STATUS_NO_MEMORY;
+    }
 
     /* set layout to vertical */
     FcPatternDel (resolved, FC_VERTICAL_LAYOUT);
@@ -92,14 +100,22 @@ create_scaled_font (cairo_t * cr)
     FcPatternDestroy (pattern);
     FcPatternDestroy (resolved);
 
-    return scaled_font;
+    status = cairo_scaled_font_status (scaled_font);
+    if (status) {
+	cairo_scaled_font_destroy (scaled_font);
+	return status;
+    }
+
+    *out = scaled_font;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
     cairo_text_extents_t extents;
-    cairo_scaled_font_t * scaled_font;
+    cairo_scaled_font_t *scaled_font;
+    cairo_status_t status;
     const char text[] = "i-W";
     double line_width, x, y;
 
@@ -111,7 +127,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_paint (cr);
     cairo_restore (cr);
 
-    scaled_font = create_scaled_font (cr);
+    status = create_scaled_font (cr, &scaled_font);
+    if (status) {
+	return cairo_test_status_from_status (cairo_test_get_context (cr),
+					      status);
+    }
+
     cairo_set_scaled_font (cr, scaled_font);
 
     cairo_set_line_width (cr, 1.0);
commit 336eddfdbb31865e3669ceb775089c63ce1db27f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 17:19:12 2008 +0100

    [test/get-clip] Check the status on the secondary context.
    
    As we solely use a secondary context, we must manually report NO_MEMORY
    errors whilst running under memfault.

diff --git a/test/cairo-test.c b/test/cairo-test.c
index 55c583a..12fc3ad 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -1433,7 +1433,6 @@ _draw_check (int width, int height)
 void
 cairo_test_paint_checkered (cairo_t *cr)
 {
-    cairo_status_t status;
     cairo_surface_t *check;
 
     check = _draw_check (12, 12);
@@ -1491,3 +1490,16 @@ cairo_test_malloc_failure (const cairo_test_context_t *ctx,
 
     return TRUE;
 }
+
+cairo_test_status_t
+cairo_test_status_from_status (const cairo_test_context_t *ctx,
+			       cairo_status_t status)
+{
+    if (status == CAIRO_STATUS_SUCCESS)
+	return CAIRO_TEST_SUCCESS;
+
+    if (cairo_test_malloc_failure (ctx, status))
+	return CAIRO_TEST_NO_MEMORY;
+
+    return CAIRO_TEST_FAILURE;
+}
diff --git a/test/cairo-test.h b/test/cairo-test.h
index 79a5d71..2571071 100644
--- a/test/cairo-test.h
+++ b/test/cairo-test.h
@@ -192,6 +192,10 @@ cairo_bool_t
 cairo_test_malloc_failure (const cairo_test_context_t *ctx,
 	                   cairo_status_t status);
 
+cairo_test_status_t
+cairo_test_status_from_status (const cairo_test_context_t *ctx,
+			       cairo_status_t status);
+
 char *
 cairo_test_reference_image_filename (const cairo_test_context_t *ctx,
 	                             const char *base_name,
diff --git a/test/get-clip.c b/test/get-clip.c
index bd7f8a1..5e0ff17 100644
--- a/test/get-clip.c
+++ b/test/get-clip.c
@@ -122,14 +122,10 @@ draw (cairo_t *cr, int width, int height)
     cairo_rectangle_list_t *rectangle_list;
     const char             *phase;
     cairo_bool_t            uses_clip_rects;
-    
+    cairo_status_t          status;
+
     surface = cairo_surface_create_similar (cairo_get_group_target (cr),
                                             CAIRO_CONTENT_COLOR, 100, 100);
-    /* don't use cr accidentally */
-    cr = NULL;
-    cr2 = cairo_create (surface);
-    cairo_surface_destroy (surface);
-
     /* Check the surface type so we ignore cairo_copy_clip_rectangle_list failures
      * on surface types that don't use rectangle lists for clipping.
      * Default to FALSE for the internal surface types, (meta, test-fallback, etc.)
@@ -154,15 +150,21 @@ draw (cairo_t *cr, int width, int height)
         break;
     }
 
+    /* don't use cr accidentally */
+    cr = NULL;
+    cr2 = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+
     /* first, test basic stuff. This should not be clipped, it should
        return the surface rectangle. */
     phase = "No clip set";
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
-        !check_clip_extents (ctx, phase, cr2, 0, 0, 100, 100) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 0, 0, 100, 100)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr2, 0, 0, 100, 100) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 0, 0, 100, 100))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
 
@@ -172,11 +174,11 @@ draw (cairo_t *cr, int width, int height)
     cairo_rectangle (cr2, 10, 10, 80, 80);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
-        !check_clip_extents (ctx, phase, cr2, 10, 10, 80, 80) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr2, 10, 10, 80, 80) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
@@ -186,13 +188,13 @@ draw (cairo_t *cr, int width, int height)
     cairo_save (cr2);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 0)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 0))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
-    
+
     /* test two clip rects */
     phase = "Two clip rects";
     cairo_save (cr2);
@@ -202,12 +204,12 @@ draw (cairo_t *cr, int width, int height)
     cairo_rectangle (cr2, 15, 15, 10, 10);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 2) ||
-        !check_clip_extents (ctx, phase, cr2, 15, 15, 10, 10) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 15, 15, 5, 5) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 20, 20, 5, 5)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 2) ||
+        ! check_clip_extents (ctx, phase, cr2, 15, 15, 10, 10) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 15, 15, 5, 5) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 20, 20, 5, 5))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
@@ -222,25 +224,25 @@ draw (cairo_t *cr, int width, int height)
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
      /* can't get this in one tight user-space rectangle */
-    if (!check_unrepresentable (ctx, phase, rectangle_list) ||
-        !check_clip_extents (ctx, phase, cr2, 0, 0, 100, 100)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_unrepresentable (ctx, phase, rectangle_list) ||
+        ! check_clip_extents (ctx, phase, cr2, 0, 0, 100, 100))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
-    
+
     phase = "User space, simple scale, getting clip with same transform";
     cairo_save (cr2);
     cairo_scale (cr2, 2, 2);
     cairo_rectangle (cr2, 5, 5, 40, 40);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
-        !check_clip_extents (ctx, phase, cr2, 5, 5, 40, 40) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 5, 5, 40, 40)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr2, 5, 5, 40, 40) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 5, 5, 40, 40))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
@@ -253,11 +255,11 @@ draw (cairo_t *cr, int width, int height)
     cairo_restore (cr2);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
-        !check_clip_extents (ctx, phase, cr2, 10, 10, 80, 80) ||
-        !check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
+    if (! check_count (ctx, phase, uses_clip_rects, rectangle_list, 1) ||
+        ! check_clip_extents (ctx, phase, cr2, 10, 10, 80, 80) ||
+        ! check_rectangles_contain (ctx, phase, uses_clip_rects, rectangle_list, 10, 10, 80, 80))
+    {
+	goto FAIL;
     }
     cairo_rectangle_list_destroy (rectangle_list);
     cairo_restore (cr2);
@@ -270,15 +272,15 @@ draw (cairo_t *cr, int width, int height)
     cairo_restore (cr2);
     cairo_clip (cr2);
     rectangle_list = cairo_copy_clip_rectangle_list (cr2);
-    if (!check_unrepresentable (ctx, phase, rectangle_list)) {
-        cairo_rectangle_list_destroy (rectangle_list);
-	return CAIRO_TEST_FAILURE;
-    }
-    cairo_rectangle_list_destroy (rectangle_list);
-    cairo_restore (cr2);
+    if (! check_unrepresentable (ctx, phase, rectangle_list))
+	goto FAIL;
 
+FAIL:
+    cairo_rectangle_list_destroy (rectangle_list);
+    status = cairo_status (cr2);
     cairo_destroy (cr2);
-    return CAIRO_TEST_SUCCESS;
+
+    return cairo_test_status_from_status (ctx, status);
 }
 
 int
commit d48f9340514c258cfece1b72d2f7e3f7b2d3c7b1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 14:05:14 2008 +0100

    [test/clip-operator] Propagate error from secondary context.
    
    Use the cairo_get_target() to propagate any errors from the secondary
    context.

diff --git a/test/clip-operator.c b/test/clip-operator.c
index d6e20c8..3c4bcbb 100644
--- a/test/clip-operator.c
+++ b/test/clip-operator.c
@@ -46,6 +46,7 @@ draw_mask (cairo_t *cr, int x, int y)
 						 CAIRO_CONTENT_ALPHA,
 						 width, height);
     cr2 = cairo_create (mask_surface);
+    cairo_surface_destroy (mask_surface);
 
     cairo_save (cr2);
     cairo_set_source_rgba (cr2, 0, 0, 0, 0); /* transparent */
@@ -58,11 +59,8 @@ draw_mask (cairo_t *cr, int x, int y)
     cairo_arc (cr2, 0.5 * width, 0.5 * height, 0.45 * height, 0, 2 * M_PI);
     cairo_fill (cr2);
 
+    cairo_mask_surface (cr, cairo_get_target (cr2), x, y);
     cairo_destroy (cr2);
-
-    cairo_mask_surface (cr, mask_surface, x, y);
-
-    cairo_surface_destroy (mask_surface);
 }
 
 static void
commit d11014386f739f43ec5f290714d7c51cc638f172
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 5 13:37:07 2007 +0100

    Add support for lockdep.
    
    lockdep is a valgrind skin which performs pthread locking correctness
    validation. In particular it allows one to write assert(HOLDS_LOCK(mutex))
    which both documents the preconditions for a function and enforces them
    when the program is run under lockdep.
    
    As an aide to lockdep (as it works by intercepting the pthread functions),
    all the mutexes should be initialised and destroyed using
    pthread_mutex_init() and pthread_mutex_destroy() rather than using static
    initializers and no-ops.

diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h
index 25fee4b..5251907 100644
--- a/src/cairo-mutex-impl-private.h
+++ b/src/cairo-mutex-impl-private.h
@@ -47,6 +47,11 @@
 #include "config.h"
 #endif
 
+#if HAVE_LOCKDEP
+#include <lockdep.h>
+#endif
+
+CAIRO_BEGIN_DECLS
 
 /* A fully qualified no-operation statement */
 #define CAIRO_MUTEX_IMPL_NOOP	do {/*no-op*/} while (0)
@@ -171,10 +176,19 @@
   typedef pthread_mutex_t cairo_mutex_impl_t;
 
 # define CAIRO_MUTEX_IMPL_PTHREAD 1
+#if HAVE_LOCKDEP
+/* expose all mutexes to the validator */
+# define CAIRO_MUTEX_IMPL_INIT(mutex) pthread_mutex_init (&(mutex), NULL)
+#endif
 # define CAIRO_MUTEX_IMPL_LOCK(mutex) pthread_mutex_lock (&(mutex))
 # define CAIRO_MUTEX_IMPL_UNLOCK(mutex) pthread_mutex_unlock (&(mutex))
+#if HAVE_LOCKDEP
+# define CAIRO_HOLDS_MUTEX(mutex) LOCKDEP_HOLDS_LOCK (&(mutex))
+#endif
 # define CAIRO_MUTEX_IMPL_FINI(mutex) pthread_mutex_destroy (&(mutex))
+#if ! HAVE_LOCKDEP
 # define CAIRO_MUTEX_IMPL_FINALIZE() CAIRO_MUTEX_IMPL_NOOP
+#endif
 # define CAIRO_MUTEX_IMPL_NIL_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 
 #elif defined(HAVE_WINDOWS_H) || defined(_MSC_VER) /*************************/
diff --git a/src/cairo-mutex-type-private.h b/src/cairo-mutex-type-private.h
index 2314be1..776b0db 100644
--- a/src/cairo-mutex-type-private.h
+++ b/src/cairo-mutex-type-private.h
@@ -44,6 +44,8 @@
 #include "cairo-compiler-private.h"
 #include "cairo-mutex-impl-private.h"
 
+CAIRO_BEGIN_DECLS
+
 /* Only the following three are mandatory at this point */
 #ifndef CAIRO_MUTEX_IMPL_LOCK
 # error "CAIRO_MUTEX_IMPL_LOCK not defined.  Check cairo-mutex-impl-private.h."
@@ -90,7 +92,6 @@
 
 #endif /* CAIRO_MUTEX_IMPL_INIT */
 
-
 #ifdef CAIRO_MUTEX_IMPL_FINI
 
 /* If %CAIRO_MUTEX_IMPL_FINI is defined, we may need to finalize all
@@ -169,6 +170,9 @@ typedef cairo_mutex_impl_t cairo_mutex_t;
 #define CAIRO_MUTEX_FINI		CAIRO_MUTEX_IMPL_FINI
 #define CAIRO_MUTEX_NIL_INITIALIZER	CAIRO_MUTEX_IMPL_NIL_INITIALIZER
 
+#ifndef CAIRO_HOLDS_MUTEX
+# define CAIRO_HOLDS_MUTEX(name) 1
+#endif
 
 
 /* Debugging support */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index bfe076a..fcea8aa 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -656,18 +656,24 @@ _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
 void
 _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
 {
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     _cairo_cache_freeze (scaled_font->glyphs);
 }
 
 void
 _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
 {
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     _cairo_cache_thaw (scaled_font->glyphs);
 }
 
 void
 _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
 {
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     _cairo_cache_destroy (scaled_font->glyphs);
     scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
 					       _cairo_scaled_glyph_destroy,
@@ -1689,6 +1695,8 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
     if (scaled_font->status)
 	return scaled_font->status;
 
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     for (i = 0; i < num_glyphs; i++) {
 	cairo_scaled_glyph_t	*scaled_glyph;
 	int			left, top;
@@ -1760,6 +1768,7 @@ _cairo_scaled_font_show_glyphs (cairo_scaled_font_t    *scaled_font,
     if (!num_glyphs)
 	return CAIRO_STATUS_SUCCESS;
 
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
     if (scaled_font->backend->show_glyphs != NULL) {
 	int remaining_glyphs = num_glyphs;
 	status = scaled_font->backend->show_glyphs (scaled_font,
@@ -2074,6 +2083,8 @@ _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font,
     if (status)
 	return status;
 
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     closure.path = path;
     _cairo_scaled_font_freeze_cache (scaled_font);
     for (i = 0; i < num_glyphs; i++) {
@@ -2284,6 +2295,8 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
     if (scaled_font->status)
 	return scaled_font->status;
 
+    assert (CAIRO_HOLDS_MUTEX (scaled_font->mutex));
+
     key.hash = index;
     /*
      * Check cache for glyph
commit bccfdf7d93c2a92a342127fc212770f4053cb2cf
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 13:47:27 2008 +0100

    [test] Add support for memfault.
    
    Add the core support to cairo-test for running the test-suite under a
    malloc fault injector. This commit contains the adjustments to
    cairo_test_run() to repeat the test if it detects a failure due to fault
    injection and complains if it detects unreported faults or memory leaks.

diff --git a/build/configure.ac.analysis b/build/configure.ac.analysis
index f0d41df..6ae2a97 100644
--- a/build/configure.ac.analysis
+++ b/build/configure.ac.analysis
@@ -76,3 +76,20 @@ dnl  PHP_ADD_MAKEFILE_FRAGMENT($abs_srcdir/Makefile.gcov, $abs_srcdir)
 fi
 AM_CONDITIONAL(CAIRO_HAS_LCOV, test "x$cairo_has_lcov" = "xyes")
 
+dnl ===========================================================================
+dnl Check for some custom valgrind modules
+PKG_CHECK_MODULES(VALGRIND, valgrind, [
+	safe_CFLAGS="$CFLAGS"
+	safe_CPPFLAGS="$CPPFLAGS"
+	CFLAGS="$CFLAGS $VALGRIND_CFLAGS"
+	CPPFLAGS="$CPPFLAGS $VALGRIND_CFLAGS"
+	AC_CHECK_HEADER([valgrind.h], [AC_DEFINE([HAVE_VALGRIND], [1],
+			[Define to 1 if you have Valgrind])])
+	AC_CHECK_HEADER([lockdep.h], [AC_DEFINE([HAVE_LOCKDEP], [1],
+			[Define to 1 if you have the Valgrind lockdep tool])])
+	AC_CHECK_HEADER([memfault.h], [AC_DEFINE([HAVE_MEMFAULT], [1],
+			[Define to 1 if you have the Valgrind memfault tool])])
+	CAIRO_CFLAGS="$VALGRIND_CFLAGS $CAIRO_CFLAGS"
+	CFLAGS="$safe_CFLAGS"
+	CPPFLAGS="$safe_CPPFLAGS"
+    ], AC_MSG_RESULT(no))
diff --git a/test/cairo-test.c b/test/cairo-test.c
index 7edb4fb..55c583a 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -38,7 +38,7 @@
 #include <fenv.h>
 #endif
 #include <assert.h>
-#ifdef HAVE_UNISTD_H
+#if HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 #include <errno.h>
@@ -46,13 +46,26 @@
 #if HAVE_FCFINI
 #include <fontconfig/fontconfig.h>
 #endif
-#ifdef HAVE_PTHREAD_H
+#if HAVE_PTHREAD_H
 #include <pthread.h>
 #endif
-#ifdef HAVE_SYS_STAT_H
+#if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 
+#if HAVE_VALGRIND
+#include <valgrind.h>
+#else
+#define RUNNING_ON_VALGRIND 0
+#endif
+
+#if HAVE_MEMFAULT
+#include <memfault.h>
+#define MF(x) x
+#else
+#define MF(x)
+#endif
+
 #include "cairo-test.h"
 
 #include "buffer-diff.h"
@@ -115,6 +128,8 @@ _cairo_test_init (cairo_test_context_t *ctx,
 {
     char *log_name;
 
+    MF (VALGRIND_DISABLE_FAULTS ());
+
 #if HAVE_FEENABLEEXCEPT
     feenableexcept (FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
 #endif
@@ -123,6 +138,14 @@ _cairo_test_init (cairo_test_context_t *ctx,
     ctx->test_name = test_name;
     ctx->expectation = expectation;
 
+    ctx->malloc_failure = 0;
+#if HAVE_MEMFAULT
+    if (getenv ("CAIRO_TEST_MALLOC_FAILURE"))
+	ctx->malloc_failure = atoi (getenv ("CAIRO_TEST_MALLOC_FAILURE"));
+    if (ctx->malloc_failure && ! RUNNING_ON_MEMFAULT ())
+	ctx->malloc_failure = 0;
+#endif
+
     xasprintf (&log_name, "%s%s", test_name, CAIRO_TEST_LOG_SUFFIX);
     _xunlink (NULL, log_name);
 
@@ -164,8 +187,12 @@ cairo_test_init (cairo_test_context_t *ctx,
 }
 
 static void
-cairo_test_init_thread (cairo_test_context_t *ctx, cairo_test_context_t *master, int thread)
+cairo_test_init_thread (cairo_test_context_t *ctx,
+			cairo_test_context_t *master,
+			int thread)
 {
+    MF (VALGRIND_DISABLE_FAULTS ());
+
     *ctx = *master;
     ctx->thread = thread;
 }
@@ -328,6 +355,7 @@ cairo_test_target_has_similar (const cairo_test_context_t *ctx,
     cairo_bool_t has_similar;
     cairo_t * cr;
     cairo_surface_t *similar;
+    cairo_status_t status;
     void *closure;
 
     /* ignore image intermediate targets */
@@ -337,30 +365,39 @@ cairo_test_target_has_similar (const cairo_test_context_t *ctx,
     if (getenv ("CAIRO_TEST_IGNORE_SIMILAR"))
 	return FALSE;
 
-    surface = (target->create_surface) (ctx->test->name,
-					target->content,
-					ctx->test->width,
-					ctx->test->height,
-					ctx->test->width + 25 * NUM_DEVICE_OFFSETS,
-					ctx->test->height + 25 * NUM_DEVICE_OFFSETS,
-					CAIRO_BOILERPLATE_MODE_TEST,
-					0,
-					&closure);
-    if (surface == NULL)
-	return FALSE;
-
-    cr = cairo_create (surface);
-    cairo_push_group_with_content (cr,
-				   cairo_boilerplate_content (target->content));
-    similar = cairo_get_group_target (cr);
-
-    has_similar = cairo_surface_get_type (similar) == cairo_surface_get_type (surface);
-
-    cairo_destroy (cr);
-    cairo_surface_destroy (surface);
-
-    if (target->cleanup)
-	target->cleanup (closure);
+    do {
+	do {
+	    surface = (target->create_surface) (ctx->test->name,
+						target->content,
+						ctx->test->width,
+						ctx->test->height,
+						ctx->test->width + 25 * NUM_DEVICE_OFFSETS,
+						ctx->test->height + 25 * NUM_DEVICE_OFFSETS,
+						CAIRO_BOILERPLATE_MODE_TEST,
+						0,
+						&closure);
+	    if (surface == NULL)
+		return FALSE;
+	} while (cairo_test_malloc_failure (ctx, cairo_surface_status (surface)));
+
+	if (cairo_surface_status (surface))
+	    return FALSE;
+
+
+	cr = cairo_create (surface);
+	cairo_push_group_with_content (cr,
+				       cairo_boilerplate_content (target->content));
+	similar = cairo_get_group_target (cr);
+	status = cairo_surface_status (similar);
+
+	has_similar = cairo_surface_get_type (similar) == cairo_surface_get_type (surface);
+
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+
+	if (target->cleanup)
+	    target->cleanup (closure);
+    } while (cairo_test_malloc_failure (ctx, status));
 
     return has_similar;
 }
@@ -436,7 +473,7 @@ static cairo_bool_t
 cairo_test_file_is_older (const char *filename,
 	                  const char *ref_filename)
 {
-#ifdef HAVE_SYS_STAT_H
+#if HAVE_SYS_STAT_H
     struct stat st, ref;
 
     if (stat (filename, &st) < 0)
@@ -536,8 +573,10 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
     const char *format;
     cairo_bool_t have_output = FALSE;
     cairo_bool_t have_result = FALSE;
+    int malloc_failure_iterations = ctx->malloc_failure;
     void *closure;
     int width, height;
+    int last_fault_count = 0;
 
     /* Get the strings ready that we'll need. */
     format = cairo_boilerplate_content_name (target->content);
@@ -590,6 +629,14 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 	height += dev_offset;
     }
 
+REPEAT:
+#if HAVE_MEMFAULT
+    VALGRIND_CLEAR_FAULTS ();
+    VALGRIND_RESET_LEAKS ();
+    ctx->last_fault_count = 0;
+    last_fault_count = VALGRIND_COUNT_FAULTS ();
+    VALGRIND_ENABLE_FAULTS ();
+#endif
     have_output = FALSE;
     have_result = FALSE;
 
@@ -609,7 +656,11 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 	goto UNWIND_STRINGS;
     }
 
+    if (cairo_test_malloc_failure (ctx, cairo_surface_status (surface)))
+	goto REPEAT;
+
     if (cairo_surface_status (surface)) {
+	MF (VALGRIND_PRINT_FAULTS ());
 	cairo_test_log (ctx, "Error: Created an error surface\n");
 	ret = CAIRO_TEST_FAILURE;
 	goto UNWIND_STRINGS;
@@ -617,6 +668,7 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 
     /* Check that we created a surface of the expected type. */
     if (cairo_surface_get_type (surface) != target->expected_type) {
+	MF (VALGRIND_PRINT_FAULTS ());
 	cairo_test_log (ctx, "Error: Created surface is of type %d (expected %d)\n",
 			cairo_surface_get_type (surface), target->expected_type);
 	ret = CAIRO_TEST_FAILURE;
@@ -629,6 +681,7 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
     expected_content = cairo_boilerplate_content (target->content);
 
     if (cairo_surface_get_content (surface) != expected_content) {
+	MF (VALGRIND_PRINT_FAULTS ());
 	cairo_test_log (ctx, "Error: Created surface has content %d (expected %d)\n",
 			cairo_surface_get_content (surface), expected_content);
 	ret = CAIRO_TEST_FAILURE;
@@ -639,8 +692,18 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 
     cr = cairo_create (surface);
     if (cairo_set_user_data (cr, &_cairo_test_context_key, (void*) ctx, NULL)) {
+#if HAVE_MEMFAULT
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+
+	if (target->cleanup)
+	    target->cleanup (closure);
+
+	goto REPEAT;
+#else
 	ret = CAIRO_TEST_FAILURE;
 	goto UNWIND_CAIRO;
+#endif
     }
 
     if (similar)
@@ -672,6 +735,35 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 	cairo_paint (cr);
     }
 
+#if HAVE_MEMFAULT
+    VALGRIND_DISABLE_FAULTS ();
+
+    /* repeat test after malloc failure injection */
+    if (ctx->malloc_failure &&
+	VALGRIND_COUNT_FAULTS () - last_fault_count > 0 &&
+	(status == CAIRO_TEST_NO_MEMORY ||
+	 cairo_status (cr) == CAIRO_STATUS_NO_MEMORY ||
+	 cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY))
+    {
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+	if (target->cleanup)
+	    target->cleanup (closure);
+	if (ctx->thread == 0) {
+	    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+	    FcFini ();
+#endif
+	    if (VALGRIND_COUNT_LEAKS () > 0) {
+		VALGRIND_PRINT_FAULTS ();
+		VALGRIND_PRINT_LEAKS ();
+	    }
+	}
+
+	goto REPEAT;
+    }
+#endif
+
     /* Then, check all the different ways it could fail. */
     if (status) {
 	cairo_test_log (ctx, "Error: Function under test failed\n");
@@ -686,6 +778,13 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
 	goto UNWIND_CAIRO;
     }
 
+#if HAVE_MEMFAULT
+    if (VALGRIND_COUNT_FAULTS () - last_fault_count > 0) {
+	VALGRIND_PRINTF ("Unreported memfaults...");
+	VALGRIND_PRINT_FAULTS ();
+    }
+#endif
+
     /* Skip image check for tests with no image (width,height == 0,0) */
     if (ctx->test->width != 0 && ctx->test->height != 0) {
 	cairo_surface_t *ref_image;
@@ -869,6 +968,23 @@ cairo_test_for_target (cairo_test_context_t		 *ctx,
     }
 
 UNWIND_CAIRO:
+    if (test_filename != NULL) {
+	free (test_filename);
+	test_filename = NULL;
+    }
+    if (fail_filename != NULL) {
+	free (fail_filename);
+	fail_filename = NULL;
+    }
+    if (pass_filename != NULL) {
+	free (pass_filename);
+	pass_filename = NULL;
+    }
+
+#if HAVE_MEMFAULT
+    if (ret == CAIRO_TEST_FAILURE && ctx->expectation != CAIRO_TEST_FAILURE)
+	VALGRIND_PRINT_FAULTS ();
+#endif
     cairo_destroy (cr);
 UNWIND_SURFACE:
     cairo_surface_destroy (surface);
@@ -876,6 +992,28 @@ UNWIND_SURFACE:
     if (target->cleanup)
 	target->cleanup (closure);
 
+#if HAVE_MEMFAULT
+    if (ctx->thread == 0) {
+	cairo_debug_reset_static_data ();
+
+#if HAVE_FCFINI
+	FcFini ();
+#endif
+
+	if (VALGRIND_COUNT_LEAKS () > 0) {
+	    if (ret != CAIRO_TEST_FAILURE ||
+		ctx->expectation == CAIRO_TEST_FAILURE)
+	    {
+		VALGRIND_PRINT_FAULTS ();
+	    }
+	    VALGRIND_PRINT_LEAKS ();
+	}
+    }
+
+    if (ret == CAIRO_TEST_SUCCESS && --malloc_failure_iterations > 0)
+	goto REPEAT;
+#endif
+
     if (ctx->thread == 0) {
 	if (have_output)
 	    cairo_test_log (ctx, "OUTPUT: %s\n", png_name);
@@ -888,12 +1026,6 @@ UNWIND_SURFACE:
     }
 
 UNWIND_STRINGS:
-    if (test_filename != NULL)
-	free (test_filename);
-    if (fail_filename != NULL)
-	free (fail_filename);
-    if (pass_filename != NULL)
-	free (pass_filename);
     if (png_name)
       free (png_name);
     if (ref_name)
@@ -930,7 +1062,7 @@ cairo_test_run (cairo_test_context_t *ctx)
     volatile cairo_bool_t print_fail_on_stdout = ctx->thread == 0;
     volatile cairo_test_status_t status, ret;
 
-#ifdef HAVE_UNISTD_H
+#if HAVE_UNISTD_H
     if (ctx->thread == 0 && isatty (2)) {
 	fail_face = "\033[41m\033[37m\033[1m";
 	normal_face = "\033[m";
@@ -976,7 +1108,7 @@ cairo_test_run (cairo_test_context_t *ctx)
 		}
 
 #if defined(HAVE_SIGNAL_H) && defined(HAVE_SETJMP_H)
-		if (ctx->thread == 0) {
+		if (ctx->thread == 0 && ! RUNNING_ON_VALGRIND) {
 		    void (* volatile old_segfault_handler)(int);
 		    void (* volatile old_sigpipe_handler)(int);
 
@@ -1037,6 +1169,7 @@ cairo_test_run (cairo_test_context_t *ctx)
 			ret = CAIRO_TEST_FAILURE;
 			break;
 		    default:
+		    case CAIRO_TEST_NO_MEMORY:
 		    case CAIRO_TEST_FAILURE:
 			if (ctx->expectation == CAIRO_TEST_FAILURE) {
 			    printf ("XFAIL\n");
@@ -1080,6 +1213,7 @@ cairo_test_run (cairo_test_context_t *ctx)
 			ret = CAIRO_TEST_FAILURE;
 			break;
 		    default:
+		    case CAIRO_TEST_NO_MEMORY:
 		    case CAIRO_TEST_FAILURE:
 			if (ctx->expectation == CAIRO_TEST_FAILURE) {
 			    printf ("XFAIL\n");
@@ -1102,7 +1236,7 @@ cairo_test_run (cairo_test_context_t *ctx)
     return ret;
 }
 
-#ifdef HAVE_PTHREAD_H
+#if HAVE_PTHREAD_H
 typedef struct _cairo_test_thread {
     pthread_t thread;
     cairo_test_context_t *ctx;
@@ -1141,7 +1275,7 @@ cairo_test_expecting (const cairo_test_t *test,
     if (expectation == CAIRO_TEST_FAILURE)
 	printf ("Expecting failure\n");
 
-#ifdef HAVE_PTHREAD_H
+#if HAVE_PTHREAD_H
     num_threads = 0;
     if (getenv ("CAIRO_TEST_NUM_THREADS"))
 	num_threads = atoi (getenv ("CAIRO_TEST_NUM_THREADS"));
@@ -1235,9 +1369,11 @@ cairo_test_create_surface_from_png (const cairo_test_context_t *ctx,
 	                            const char *filename)
 {
     cairo_surface_t *image;
+    cairo_status_t status;
 
     image = cairo_image_surface_create_from_png (filename);
-    if (cairo_surface_status(image)) {
+    status = cairo_surface_status (image);
+    if (status == CAIRO_STATUS_FILE_NOT_FOUND) {
         /* expect not found when running with srcdir != builddir
          * such as when 'make distcheck' is run
          */
@@ -1270,13 +1406,16 @@ cairo_test_create_pattern_from_png (const cairo_test_context_t *ctx,
     return pattern;
 }
 
-static cairo_status_t
-_draw_check (cairo_surface_t *surface, int width, int height)
+static cairo_surface_t *
+_draw_check (int width, int height)
 {
+    cairo_surface_t *surface;
     cairo_t *cr;
-    cairo_status_t status;
 
+    surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
     cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
     cairo_set_source_rgb (cr, 0.75, 0.75, 0.75); /* light gray */
     cairo_paint (cr);
 
@@ -1285,37 +1424,29 @@ _draw_check (cairo_surface_t *surface, int width, int height)
     cairo_rectangle (cr, 0, height / 2, width / 2, height / 2);
     cairo_fill (cr);
 
-    status = cairo_status (cr);
-
+    surface = cairo_surface_reference (cairo_get_target (cr));
     cairo_destroy (cr);
 
-    return status;
+    return surface;
 }
 
-cairo_status_t
+void
 cairo_test_paint_checkered (cairo_t *cr)
 {
     cairo_status_t status;
     cairo_surface_t *check;
 
-    check = cairo_image_surface_create (CAIRO_FORMAT_RGB24, 12, 12);
-    status = _draw_check (check, 12, 12);
-    if (status) {
-	cairo_surface_destroy (check);
-	return status;
-    }
+    check = _draw_check (12, 12);
 
     cairo_save (cr);
     cairo_set_source_surface (cr, check, 0, 0);
+    cairo_surface_destroy (check);
+
     cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
     cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
     cairo_paint (cr);
-    cairo_restore (cr);
-
-    status = cairo_surface_status (check);
-    cairo_surface_destroy (check);
 
-    return status;
+    cairo_restore (cr);
 }
 
 cairo_bool_t
@@ -1336,3 +1467,27 @@ cairo_test_is_target_enabled (const cairo_test_context_t *ctx, const char *targe
 
     return FALSE;
 }
+
+cairo_bool_t
+cairo_test_malloc_failure (const cairo_test_context_t *ctx,
+			   cairo_status_t status)
+{
+    int n_faults;
+
+    if (! ctx->malloc_failure)
+	return FALSE;
+
+    if (status != CAIRO_STATUS_NO_MEMORY)
+	return FALSE;
+
+#if HAVE_MEMFAULT
+    /* prevent infinite loops... */
+    n_faults = VALGRIND_COUNT_FAULTS ();
+    if (n_faults == ctx->last_fault_count)
+	return FALSE;
+
+    ((cairo_test_context_t *) ctx)->last_fault_count = n_faults;
+#endif
+
+    return TRUE;
+}
diff --git a/test/cairo-test.h b/test/cairo-test.h
index 69b3d85..79a5d71 100644
--- a/test/cairo-test.h
+++ b/test/cairo-test.h
@@ -61,6 +61,7 @@ typedef unsigned __int64 uint64_t;
 
 typedef enum cairo_test_status {
     CAIRO_TEST_SUCCESS = 0,
+    CAIRO_TEST_NO_MEMORY,
     CAIRO_TEST_FAILURE,
     CAIRO_TEST_CRASHED,
     CAIRO_TEST_UNTESTED = 77 /* match automake's skipped exit status */
@@ -128,6 +129,9 @@ struct _cairo_test_context {
     cairo_bool_t limited_targets;
     cairo_boilerplate_target_t **targets_to_test;
 
+    int malloc_failure;
+    int last_fault_count;
+
     int thread;
 };
 
@@ -175,7 +179,7 @@ cairo_pattern_t *
 cairo_test_create_pattern_from_png (const cairo_test_context_t *ctx,
 	                            const char *filename);
 
-cairo_status_t
+void
 cairo_test_paint_checkered (cairo_t *cr);
 
 #define CAIRO_TEST_DOUBLE_EQUALS(a,b)  (fabs((a)-(b)) < 0.00001)
@@ -184,6 +188,10 @@ cairo_bool_t
 cairo_test_is_target_enabled (const cairo_test_context_t *ctx,
 	                      const char *target);
 
+cairo_bool_t
+cairo_test_malloc_failure (const cairo_test_context_t *ctx,
+	                   cairo_status_t status);
+
 char *
 cairo_test_reference_image_filename (const cairo_test_context_t *ctx,
 	                             const char *base_name,
commit 974fabfe059f9744116d20fd9f8b59f1b8c34b70
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Oct 17 18:52:51 2008 +0100

    [ft] Add more comments about FT_Face lifetime issues.
    
    Currently we only have an example of how to call FT_Done after the last
    reference to the FT cairo_font_face_t has been dropped, but do not
    actually explain why this needs to be done. So add a couple of sentences
    to clarify the likely lifetime of the FT_Face once exposed to cairo.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index ebd419b..7075b5d 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2561,10 +2561,14 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
  * cairo_set_font_face() or cairo_scaled_font_create(). The
  * #cairo_scaled_font_t returned from cairo_scaled_font_create() is
  * also for the FreeType backend and can be used with functions such
- * as cairo_ft_scaled_font_lock_face().
+ * as cairo_ft_scaled_font_lock_face(). Note that Cairo may keep a reference
+ * to the FT_Face alive in a font-cache and the exact lifetime of the reference
+ * depends highly upon the exact usage pattern and is subject to external
+ * factors. You must not call FT_Done_Face() before the last reference to the
+ * #cairo_font_face_t has been dropped.
  *
- * As an example, here is how one might correctly couple the lifetime of
- * the FreeType face object to the #cairo_font_face_t:
+ * As an example, below is how one might correctly couple the lifetime of
+ * the FreeType face object to the #cairo_font_face_t.
  *
  * <informalexample><programlisting>
  * static const cairo_user_data_key_t key;
commit 5fcfc7ebc9a2d1d947898d269be7ba91d27c5261
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 17:13:45 2008 +0100

    [test/README] Document all fonts used.
    
    Do a little leg-work to answer a query in a bug report and document the
    results.

diff --git a/test/README b/test/README
index 6d372ff..4b1f93d 100644
--- a/test/README
+++ b/test/README
@@ -85,8 +85,12 @@ the software configuration used to generate the reference images.
 
 Here are some of the relevant details:
 
-  * Your system must have a copy of the Bitstream Vera font, (this is
-    a free software font that is often in a file named Vera.ttf).
+  * Your system must have a copy of the Bitstream Vera font. These are
+      "Bitstream Vera Sans" (Vera.ttf);
+      "Bitstream Vera Sans Mono" (VeraMono.ttf);
+      "Bitstream Vera Serif" (VeraSe.ttf);
+    and also
+      "Nimbus Sans L" (n019003l.pfb).
 
   * Currently, you must be using a build of cairo using freetype
     (cairo-ft) as the default font backend. Otherwise all tests
@@ -103,6 +107,7 @@ Here are some of the relevant details:
 
   * To test the ps backend, you will need ghostscript version 8.62.
 
+
 What if I can't make my system match?
 -------------------------------------
 For one reason or another, you may be unable to get a clean run of the
commit c1164a574d3e2cc4746384875d5c2781114c0ea4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Oct 16 08:49:13 2008 +0100

    [type1] Add comment to warn about read() as macro.
    
    In case we are ever tempted to simplify the function pointer, warn that it
    may alias with a macro.

diff --git a/src/cairo-type1-subset.c b/src/cairo-type1-subset.c
index ddcb660..ddc4ce7 100644
--- a/src/cairo-type1-subset.c
+++ b/src/cairo-type1-subset.c
@@ -1215,6 +1215,11 @@ cairo_type1_font_subset_generate (void       *abstract_font,
     }
 
     if (font->face->stream->read != NULL) {
+	/* Note that read() may be implemented as a macro, thanks POSIX!, so we
+	 * need to wrap the following usage in parentheses in order to
+	 * disambiguate it for the pre-processor - using the verbose function
+	 * pointer dereference for clarity.
+	 */
 	ret = (* font->face->stream->read) (font->face->stream, 0,
 					    (unsigned char *) font->type1_data,
 					    font->type1_length);


More information about the cairo-commit mailing list