[cairo-commit] 21 commits - boilerplate/cairo-boilerplate-system.c boilerplate/cairo-boilerplate-system.h perf/cairo-perf.c perf/cairo-perf-compare-backends.c perf/cairo-perf-cover.c perf/cairo-perf.h perf/cairo-perf-report.c perf/cairo-perf-trace.c perf/.gitignore perf/glyphs.c perf/Makefile.am perf/mask.c perf/mosaic.c perf/pattern_create_radial.c perf/pythagoras-tree.c perf/README perf/subimage_copy.c perf/unaligned-clip.c perf/world-map.c perf/zrusin.c src/cairo-array.c src/cairo.c src/cairo-cache.c src/cairo-cache-private.h src/cairo-debug.c src/cairo-directfb-surface.c src/cairo-glitz-surface.c src/cairo-gstate.c src/cairo-gstate-private.h src/cairo-image-surface.c src/cairoint.h src/cairo-pattern.c src/cairo-ps-surface.c src/cairo-scaled-font.c src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-surface-private.h src/cairo-types-private.h src/cairo-xcb-surface.c src/cairo-xlib-surface.c util/cairo-script

Chris Wilson ickle at kemper.freedesktop.org
Tue Jun 2 07:14:25 PDT 2009


 boilerplate/cairo-boilerplate-system.c     |   15 
 boilerplate/cairo-boilerplate-system.h     |    4 
 perf/.gitignore                            |    1 
 perf/Makefile.am                           |   19 +
 perf/README                                |   31 +
 perf/cairo-perf-compare-backends.c         |   32 +
 perf/cairo-perf-cover.c                    |   32 -
 perf/cairo-perf-report.c                   |   16 
 perf/cairo-perf-trace.c                    |  530 +++++++++++++++++++++++++++++
 perf/cairo-perf.c                          |   93 +++--
 perf/cairo-perf.h                          |    5 
 perf/glyphs.c                              |   97 +++++
 perf/mask.c                                |  290 +++++++++++++++
 perf/mosaic.c                              |    8 
 perf/pattern_create_radial.c               |    4 
 perf/pythagoras-tree.c                     |    2 
 perf/subimage_copy.c                       |    4 
 perf/unaligned-clip.c                      |    2 
 perf/world-map.c                           |    4 
 perf/zrusin.c                              |    4 
 src/cairo-array.c                          |    8 
 src/cairo-cache-private.h                  |   27 +
 src/cairo-cache.c                          |  104 +----
 src/cairo-debug.c                          |    2 
 src/cairo-directfb-surface.c               |    1 
 src/cairo-glitz-surface.c                  |    1 
 src/cairo-gstate-private.h                 |    1 
 src/cairo-gstate.c                         |   18 
 src/cairo-image-surface.c                  |   95 ++---
 src/cairo-pattern.c                        |   24 +
 src/cairo-ps-surface.c                     |    1 
 src/cairo-scaled-font.c                    |  130 ++++---
 src/cairo-surface-fallback.c               |   83 +---
 src/cairo-surface-private.h                |    2 
 src/cairo-surface.c                        |   58 ++-
 src/cairo-types-private.h                  |   14 
 src/cairo-xcb-surface.c                    |    4 
 src/cairo-xlib-surface.c                   |    5 
 src/cairo.c                                |    6 
 src/cairoint.h                             |   71 ++-
 util/cairo-script/cairo-script-operators.c |   39 --
 util/cairo-script/cairo-script-scanner.c   |   45 +-
 42 files changed, 1517 insertions(+), 415 deletions(-)

New commits:
commit 7ed050fd435f17d25c7b757b02cfe200f8779fc2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jun 2 13:08:25 2009 +0100

    [perf] Benchmark traces
    
    Add a variant of cairo-perf that measures the time to replay traces.

diff --git a/boilerplate/cairo-boilerplate-system.c b/boilerplate/cairo-boilerplate-system.c
index 3855b73..f13bd14 100644
--- a/boilerplate/cairo-boilerplate-system.c
+++ b/boilerplate/cairo-boilerplate-system.c
@@ -145,3 +145,18 @@ xunlink (const char *pathname)
 	exit (1);
     }
 }
+
+char *
+xstrdup (const char *str)
+{
+    if (str == NULL)
+	return NULL;
+
+    str = strdup (str);
+    if (str == NULL) {
+	fprintf (stderr, "Error: Out of memory. Exiting.\n");
+	exit (1);
+    }
+
+    return (char *) str;
+}
diff --git a/boilerplate/cairo-boilerplate-system.h b/boilerplate/cairo-boilerplate-system.h
index 51273a7..8c2dbe8 100644
--- a/boilerplate/cairo-boilerplate-system.h
+++ b/boilerplate/cairo-boilerplate-system.h
@@ -48,4 +48,8 @@ xasprintf (char **strp, const char *fmt, ...) CAIRO_BOILERPLATE_PRINTF_FORMAT(2,
 void
 xunlink (const char *path);
 
+#define xstrdup cairo_boilerplate_xstrdup
+char *
+xstrdup (const char *str);
+
 #endif
diff --git a/perf/.gitignore b/perf/.gitignore
index a5d452f..4716f5a 100644
--- a/perf/.gitignore
+++ b/perf/.gitignore
@@ -1,6 +1,7 @@
 TAGS
 tags
 cairo-perf
+cairo-perf-trace
 cairo-perf-compare-backends
 cairo-perf-diff-files
 cairo-perf-graph-files
diff --git a/perf/Makefile.am b/perf/Makefile.am
index 653d93d..bb2ce2e 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -4,12 +4,14 @@ AM_CPPFLAGS =					\
 	-I$(srcdir)				\
 	-I$(top_srcdir)/boilerplate		\
 	-I$(top_srcdir)/src			\
+	-I$(top_srcdir)/util/cairo-script	\
 	-I$(top_builddir)/src			\
 	$(CAIRO_CFLAGS)
 
 AM_LDFLAGS = $(CAIRO_LDFLAGS)
 
 EXTRA_PROGRAMS += cairo-perf \
+		  cairo-perf-trace \
 		  cairo-perf-diff-files \
 		  cairo-perf-compare-backends \
 		  cairo-perf-graph-files
@@ -70,6 +72,21 @@ libcairoperf_la_SOURCES = \
 	cairo-stats.c		\
 	cairo-stats.h
 
+cairo_perf_trace_SOURCES =		\
+	cairo-perf-trace.c
+if CAIRO_HAS_WIN32_SURFACE
+cairo_perf_trace_SOURCES += cairo-perf-win32.c
+else
+if CAIRO_HAS_OS2_SURFACE
+cairo_perf_trace_SOURCES += cairo-perf-os2.c
+else
+cairo_perf_trace_SOURCES += cairo-perf-posix.c
+endif
+endif
+cairo_perf_trace_LDADD =		\
+	$(top_builddir)/util/cairo-script/libcairo-script-interpreter.la \
+	$(LDADD)
+
 cairo_perf_diff_files_SOURCES =	\
 	cairo-perf-diff-files.c
 
diff --git a/perf/README b/perf/README
index ca5f2a1..ebc2daf 100644
--- a/perf/README
+++ b/perf/README
@@ -179,6 +179,37 @@ added:
     above, three tests would be performed at sizes of 16x16, 32x32 and
     64x64.
 
+
+How to benchmark traces
+-----------------------
+Using cairo-trace you can record the exact sequence of graphic operations
+made by an application and replay them later. These traces can then be
+used to benchmark the various backends and patches.
+
+To record a trace:
+$ cairo-trace --no-mark-dirty --no-callers $APPLICATION [$ARGV]
+
+--no-mark-dirty is useful for applications that are paranoid about
+surfaces being modified by external plugins outside of their control, the
+prime example here is firefox.
+--no-callers disables the symbolic caller lookup and so speeds tracing
+(dramatically for large c++ programs) and similarly speeds up the replay
+as the files are much smaller.
+
+The output file will be called $APPLICATION.$PID.trace, the actual path
+written to will be displayed on the terminal.
+
+Then to use cairo-perf-trace:
+$ ./cairo-perf-trace $APPLICATION.$PID.trace
+
+Alternatively you can put the trace into perf/traces, or set
+CAIRO_TRACE_DIR to point to your trace directory, and the trace will be
+included in the performance tests.
+
+If you record an interesting trace, please consider sharing it by compressing
+it, LZMA preferred, and posting a link to cairo at cairographics.org.
+
+
 How to run cairo-perf-diff on WINDOWS
 -------------------------------------
 This section explains the specifics of running cairo-perf-diff under
diff --git a/perf/cairo-perf-compare-backends.c b/perf/cairo-perf-compare-backends.c
index 3f280ce..5c8d2fc 100644
--- a/perf/cairo-perf-compare-backends.c
+++ b/perf/cairo-perf-compare-backends.c
@@ -122,9 +122,13 @@ test_diff_print (test_diff_t			*diff,
     double test_time;
     double change;
 
-    printf ("(%s, size: %d)\n",
-	    diff->tests[0]->name,
-	    diff->tests[0]->size);
+    if (diff->tests[0]->size != 0) {
+	printf ("(%s, size: %d)\n",
+		diff->tests[0]->name,
+		diff->tests[0]->size);
+    } else {
+	printf ("(%s\n", diff->tests[0]->name);
+    }
 
     for (i = 0; i < diff->num_tests; i++) {
 	test_time = diff->tests[i]->stats.min_ticks;
diff --git a/perf/cairo-perf-trace.c b/perf/cairo-perf-trace.c
new file mode 100644
index 0000000..b7441f9
--- /dev/null
+++ b/perf/cairo-perf-trace.c
@@ -0,0 +1,530 @@
+/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
+/*
+ * Copyright © 2006 Mozilla Corporation
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 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 authors not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The authors make no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHORS 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: Vladimir Vukicevic <vladimir at pobox.com>
+ *          Carl Worth <cworth at cworth.org>
+ *          Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#define _GNU_SOURCE 1	/* for sched_getaffinity() */
+
+#include "cairo-perf.h"
+#include "cairo-stats.h"
+
+#include "cairo-boilerplate-getopt.h"
+#include <cairo-script-interpreter.h>
+
+/* For basename */
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#if HAVE_FCFINI
+#include <fontconfig/fontconfig.h>
+#endif
+
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+#define CAIRO_PERF_ITERATIONS_DEFAULT	10
+#define CAIRO_PERF_LOW_STD_DEV		0.3
+#define CAIRO_PERF_STABLE_STD_DEV_COUNT	3
+
+/* Some targets just aren't that interesting for performance testing,
+ * (not least because many of these surface types use a meta-surface
+ * and as such defer the "real" rendering to later, so our timing
+ * loops wouldn't count the real work, just the recording by the
+ * meta-surface. */
+static cairo_bool_t
+target_is_measurable (cairo_boilerplate_target_t *target)
+{
+    if (target->content != CAIRO_CONTENT_COLOR_ALPHA)
+	return FALSE;
+
+    switch (target->expected_type) {
+    case CAIRO_SURFACE_TYPE_IMAGE:
+	if (strcmp (target->name, "pdf") == 0 ||
+	    strcmp (target->name, "ps") == 0)
+	{
+	    return FALSE;
+	}
+	else
+	{
+	    return TRUE;
+	}
+    case CAIRO_SURFACE_TYPE_XLIB:
+	if (strcmp (target->name, "xlib-fallback") == 0)
+	{
+	    return FALSE;
+	}
+	else
+	{
+	    return TRUE;
+	}
+    case CAIRO_SURFACE_TYPE_XCB:
+    case CAIRO_SURFACE_TYPE_GLITZ:
+    case CAIRO_SURFACE_TYPE_QUARTZ:
+    case CAIRO_SURFACE_TYPE_WIN32:
+    case CAIRO_SURFACE_TYPE_BEOS:
+    case CAIRO_SURFACE_TYPE_DIRECTFB:
+#if CAIRO_VERSION_MAJOR > 1 || (CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 2)
+    case CAIRO_SURFACE_TYPE_OS2:
+#endif
+	return TRUE;
+    case CAIRO_SURFACE_TYPE_PDF:
+    case CAIRO_SURFACE_TYPE_PS:
+    case CAIRO_SURFACE_TYPE_SVG:
+    default:
+	return FALSE;
+    }
+}
+
+cairo_bool_t
+cairo_perf_can_run (cairo_perf_t	*perf,
+		    const char		*name)
+{
+    unsigned int i;
+    char *copy, *dot;
+
+    if (perf->num_names == 0)
+	return TRUE;
+
+    copy = xstrdup (name);
+    dot = strchr (copy, '.');
+    if (dot != NULL)
+	*dot = '\0';
+
+    for (i = 0; i < perf->num_names; i++)
+	if (strstr (copy, perf->names[i]))
+	    break;
+
+    free (copy);
+
+    return i != perf->num_names;
+}
+
+static void
+clear_surface (cairo_surface_t *surface)
+{
+    cairo_t *cr = cairo_create (surface);
+    cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+    cairo_paint (cr);
+    cairo_destroy (cr);
+}
+
+static cairo_surface_t *
+_similar_surface_create (void *closure,
+			 cairo_content_t content,
+			 double width, double height)
+{
+    return cairo_surface_create_similar (closure, content, width, height);
+}
+
+static void
+execute (cairo_perf_t		 *perf,
+	 cairo_script_interpreter_t *csi,
+	 cairo_surface_t	 *target,
+	 const char		 *trace)
+{
+    static cairo_bool_t first_run = TRUE;
+    unsigned int i;
+    cairo_perf_ticks_t *times;
+    cairo_stats_t stats = {0.0, 0.0};
+    int low_std_dev_count;
+    char *trace_cpy, *name, *dot;
+    const cairo_script_interpreter_hooks_t hooks = {
+	.closure = target,
+	.surface_create = _similar_surface_create
+    };
+
+    trace_cpy = xstrdup (trace);
+    name = basename (trace_cpy);
+    dot = strchr (name, '.');
+    if (dot)
+	*dot = '\0';
+
+    if (perf->list_only) {
+	printf ("%s\n", name);
+	free (trace_cpy);
+	return;
+    }
+
+    if (first_run) {
+	if (perf->raw) {
+	    printf ("[ # ] %s.%-s %s %s %s ...\n",
+		    "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
+	}
+
+	if (perf->summary) {
+	    fprintf (perf->summary,
+		     "[ # ] %8s %28s %8s %8s %5s %5s %s\n",
+		     "backend", "test", "min(ticks)", "min(ms)", "median(ms)",
+		     "stddev.", "iterations");
+	}
+	first_run = FALSE;
+    }
+
+    times = perf->times;
+
+    if (perf->summary) {
+	fprintf (perf->summary,
+		 "[%3d] %8s %26s ",
+		 perf->test_number,
+		 perf->target->name,
+		 name);
+	fflush (perf->summary);
+    }
+
+    cairo_script_interpreter_install_hooks (csi, &hooks);
+
+    low_std_dev_count = 0;
+    for (i =0; i < perf->iterations; i++) {
+	cairo_perf_yield ();
+	cairo_perf_timer_start ();
+
+	cairo_script_interpreter_run (csi, trace);
+	clear_surface (target); /* queue a write to the sync'ed surface */
+
+	cairo_perf_timer_stop ();
+	times[i] = cairo_perf_timer_elapsed ();
+
+	if (perf->raw) {
+	    if (i == 0)
+		printf ("[*] %s.%s %s.%d %g",
+			perf->target->name,
+			"rgba",
+			name,
+			0,
+			cairo_perf_ticks_per_second () / 1000.0);
+	    printf (" %lld", (long long) times[i]);
+	} else if (! perf->exact_iterations) {
+	    if (i > 0) {
+		_cairo_stats_compute (&stats, times, i+1);
+
+		if (stats.std_dev <= CAIRO_PERF_LOW_STD_DEV) {
+		    low_std_dev_count++;
+		    if (low_std_dev_count >= CAIRO_PERF_STABLE_STD_DEV_COUNT)
+			break;
+		} else {
+		    low_std_dev_count = 0;
+		}
+	    }
+	}
+    }
+
+    if (perf->raw)
+	printf ("\n");
+
+    if (perf->summary) {
+	_cairo_stats_compute (&stats, times, i);
+	fprintf (perf->summary,
+		 "%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
+		 (long long) stats.min_ticks,
+		 (stats.min_ticks * 1000.0) / cairo_perf_ticks_per_second (),
+		 (stats.median_ticks * 1000.0) / cairo_perf_ticks_per_second (),
+		 stats.std_dev * 100.0, stats.iterations);
+	fflush (perf->summary);
+    }
+
+    perf->test_number++;
+    free (trace_cpy);
+}
+
+static void
+usage (const char *argv0)
+{
+    fprintf (stderr,
+"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ... | traces ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo performance test suite over the given tests (all by default)\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -r	raw; display each time measurement instead of summary statistics\n"
+"  -v	verbose; in raw mode also show the summaries\n"
+"  -i	iterations; specify the number of iterations per test case\n"
+"  -l	list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-perf-trace firefox\" can be used to run all firefox traces.\n"
+"Alternatively, you can specify a list of filenames to execute.\n",
+	     argv0, argv0);
+}
+
+static void
+parse_options (cairo_perf_t *perf, int argc, char *argv[])
+{
+    int c;
+    const char *iters;
+    char *end;
+    int verbose = 0;
+
+    if ((iters = getenv ("CAIRO_PERF_ITERATIONS")) && *iters)
+	perf->iterations = strtol (iters, NULL, 0);
+    else
+	perf->iterations = CAIRO_PERF_ITERATIONS_DEFAULT;
+    perf->exact_iterations = 0;
+
+    perf->raw = FALSE;
+    perf->list_only = FALSE;
+    perf->names = NULL;
+    perf->num_names = 0;
+    perf->summary = stdout;
+
+    while (1) {
+	c = _cairo_getopt (argc, argv, "i:lrv");
+	if (c == -1)
+	    break;
+
+	switch (c) {
+	case 'i':
+	    perf->exact_iterations = TRUE;
+	    perf->iterations = strtoul (optarg, &end, 10);
+	    if (*end != '\0') {
+		fprintf (stderr, "Invalid argument for -i (not an integer): %s\n",
+			 optarg);
+		exit (1);
+	    }
+	    break;
+	case 'l':
+	    perf->list_only = TRUE;
+	    break;
+	case 'r':
+	    perf->raw = TRUE;
+	    perf->summary = NULL;
+	    break;
+	case 'v':
+	    verbose = 1;
+	    break;
+	default:
+	    fprintf (stderr, "Internal error: unhandled option: %c\n", c);
+	    /* fall-through */
+	case '?':
+	    usage (argv[0]);
+	    exit (1);
+	}
+    }
+
+    if (verbose && perf->summary == NULL)
+	perf->summary = stderr;
+
+    if (optind < argc) {
+	perf->names = &argv[optind];
+	perf->num_names = argc - optind;
+    }
+}
+
+static int
+check_cpu_affinity (void)
+{
+#ifdef HAVE_SCHED_GETAFFINITY
+    cpu_set_t affinity;
+    int i, cpu_count;
+
+    if (sched_getaffinity (0, sizeof (affinity), &affinity)) {
+        perror ("sched_getaffinity");
+        return -1;
+    }
+
+    for (i = 0, cpu_count = 0; i < CPU_SETSIZE; ++i) {
+        if (CPU_ISSET (i, &affinity))
+            ++cpu_count;
+    }
+
+    if (cpu_count > 1) {
+	fputs ("WARNING: cairo-perf has not been bound to a single CPU.\n",
+	       stderr);
+        return -1;
+    }
+
+    return 0;
+#else
+    fputs ("WARNING: Cannot check CPU affinity for this platform.\n",
+	   stderr);
+    return -1;
+#endif
+}
+
+static void
+cairo_perf_fini (cairo_perf_t *perf)
+{
+    cairo_boilerplate_free_targets (perf->targets);
+    free (perf->times);
+#if 0 /* XXX */
+    cairo_debug_reset_static_data ();
+#if HAVE_FCFINI
+    FcFini ();
+#endif
+#endif
+}
+
+static cairo_bool_t
+have_trace_filenames (cairo_perf_t *perf)
+{
+    unsigned int i;
+
+    if (perf->num_names == 0)
+	return FALSE;
+
+    for (i = 0; i < perf->num_names; i++)
+	if (access (perf->names[i], R_OK) == 0)
+	    return TRUE;
+
+    return FALSE;
+}
+
+static void
+cairo_perf_trace (cairo_perf_t *perf,
+		  cairo_boilerplate_target_t *target,
+		  cairo_script_interpreter_t *csi,
+		  const char *trace)
+{
+    cairo_surface_t *surface;
+    void *closure;
+
+    surface = (target->create_surface) (NULL,
+					CAIRO_CONTENT_COLOR_ALPHA,
+					1, 1,
+					1, 1,
+					CAIRO_BOILERPLATE_MODE_PERF,
+					0,
+					&closure);
+    if (surface == NULL) {
+	fprintf (stderr,
+		 "Error: Failed to create target surface: %s\n",
+		 target->name);
+	return;
+    }
+
+    cairo_perf_timer_set_synchronize (target->synchronize, closure);
+
+    execute (perf, csi, surface, trace);
+
+    cairo_surface_destroy (surface);
+
+    if (target->cleanup)
+	target->cleanup (closure);
+}
+
+int
+main (int argc, char *argv[])
+{
+    cairo_perf_t perf;
+    cairo_script_interpreter_t *csi;
+    const char *trace_dir = "traces";
+    cairo_bool_t names_are_traces;
+    unsigned int n;
+    int i;
+
+    parse_options (&perf, argc, argv);
+
+    if (! perf.list_only && check_cpu_affinity ()) {
+        fputs ("NOTICE: cairo-perf and the X server should be bound to CPUs (either the same\n"
+	       "or separate) on SMP systems. Not doing so causes random results when the X\n"
+	       "server is moved to or from cairo-perf's CPU during the benchmarks:\n"
+	       "\n"
+	       "    $ sudo taskset -cp 0 $(pidof X)\n"
+	       "    $ taskset -cp 1 $$\n"
+	       "\n"
+	       "See taskset(1) for information about changing CPU affinity.\n\n",
+	       stderr);
+    }
+
+    if (getenv ("CAIRO_TRACE_DIR") != NULL)
+	trace_dir = getenv ("CAIRO_TRACE_DIR");
+
+    perf.targets = cairo_boilerplate_get_targets (&perf.num_targets, NULL);
+    perf.times = xmalloc (perf.iterations * sizeof (cairo_perf_ticks_t));
+
+    csi = cairo_script_interpreter_create ();
+
+    /* do we have a list of filenames? */
+    names_are_traces = have_trace_filenames (&perf);
+
+    for (i = 0; i < perf.num_targets; i++) {
+        cairo_boilerplate_target_t *target = perf.targets[i];
+
+	if (! perf.list_only && ! target_is_measurable (target))
+	    continue;
+
+	perf.target = target;
+	perf.test_number = 0;
+
+	if (names_are_traces) {
+	    for (n = 0; n < perf.num_names; n++) {
+		if (access (perf.names[n], R_OK) == 0)
+		    cairo_perf_trace (&perf, target, csi, perf.names[n]);
+	    }
+	} else {
+	    DIR *dir;
+	    struct dirent *de;
+
+	    dir = opendir (trace_dir);
+	    if (dir == NULL) {
+		fprintf (stderr,
+			 "Error: Failed to open trace directory '%s'.\n"
+			 "Have you cloned the trace repository?\n"
+			 "  git clone git://anongit.freedesktop.org/~ickle/traces\n"
+			 "  cd traces && make\n"
+			 "Or set the env.var CAIRO_TRACE_DIR to point to your traces?\n",
+			 trace_dir);
+
+		return 1;
+	    }
+
+	    while ((de = readdir (dir)) != NULL) {
+		char *trace;
+		const char *dot;
+
+		dot = strrchr (de->d_name, '.');
+		if (dot == NULL)
+		    continue;
+		if (strcmp (dot, ".trace"))
+		    continue;
+
+		if (! cairo_perf_can_run (&perf, de->d_name))
+		    continue;
+
+		xasprintf (&trace, "%s/%s", trace_dir, de->d_name);
+		cairo_perf_trace (&perf, target, csi, trace);
+		free (trace);
+
+	    }
+	    closedir (dir);
+	}
+
+	if (perf.list_only)
+	    break;
+    }
+
+    cairo_script_interpreter_destroy (csi);
+    cairo_perf_fini (&perf);
+
+    return 0;
+}
commit 403f780b292762aa45056c2fb5a48bb806521173
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jun 2 09:16:16 2009 +0100

    [perf] Add a verbose flag for summary output with raw
    
    Use 'cairo-perf -v -r' to have both the summary output along with the raw
    values. This gives a progress report whilst benchmarking, very reassuring
    with long running tests.

diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 72092fd..8fb71b1 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -174,13 +174,17 @@ cairo_perf_run (cairo_perf_t		*perf,
     }
 
     if (first_run) {
-	if (perf->raw)
+	if (perf->raw) {
 	    printf ("[ # ] %s.%-s %s %s %s ...\n",
 		    "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
-	else
-	    printf ("[ # ] %8s.%-4s %28s %8s %8s %5s %5s %s\n",
-		    "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
-		    "stddev.", "iterations");
+	}
+
+	if (perf->summary) {
+	    fprintf (perf->summary,
+		     "[ # ] %8s.%-4s %28s %8s %8s %5s %5s %s\n",
+		     "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
+		     "stddev.", "iterations");
+	}
 	first_run = FALSE;
     }
 
@@ -188,6 +192,15 @@ cairo_perf_run (cairo_perf_t		*perf,
 
     has_similar = cairo_perf_has_similar (perf);
     for (similar = 0; similar <= has_similar; similar++) {
+	if (perf->summary) {
+	    fprintf (perf->summary,
+		     "[%3d] %8s.%-5s %26s.%-3d ",
+		     perf->test_number, perf->target->name,
+		     _content_to_string (perf->target->content, similar),
+		     name, perf->size);
+	    fflush (perf->summary);
+	}
+
 	/* We run one iteration in advance to warm caches, etc. */
 	cairo_perf_yield ();
 	if (similar)
@@ -231,20 +244,18 @@ cairo_perf_run (cairo_perf_t		*perf,
 	    }
 	}
 
-	if (perf->raw) {
+	if (perf->raw)
 	    printf ("\n");
-	} else {
+
+	if (perf->summary) {
 	    _cairo_stats_compute (&stats, times, i);
-	    printf ("[%3d] %8s.%-5s %26s.%-3d ",
-		    perf->test_number, perf->target->name,
-		    _content_to_string (perf->target->content, similar),
-		    name, perf->size);
-
-	    printf ("%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
-		    (long long) stats.min_ticks,
-		    (stats.min_ticks * 1000.0) / cairo_perf_ticks_per_second (),
-		    (stats.median_ticks * 1000.0) / cairo_perf_ticks_per_second (),
-		    stats.std_dev * 100.0, stats.iterations);
+	    fprintf (perf->summary,
+		     "%10lld %#8.3f %#8.3f %#5.2f%% %3d\n",
+		     (long long) stats.min_ticks,
+		     (stats.min_ticks * 1000.0) / cairo_perf_ticks_per_second (),
+		     (stats.median_ticks * 1000.0) / cairo_perf_ticks_per_second (),
+		     stats.std_dev * 100.0, stats.iterations);
+	    fflush (perf->summary);
 	}
 
 	perf->test_number++;
@@ -255,18 +266,19 @@ static void
 usage (const char *argv0)
 {
     fprintf (stderr,
-	     "Usage: %s [-l] [-r] [-i iterations] [test-names ...]\n"
-	     "       %s -l\n"
-	     "\n"
-	     "Run the cairo performance test suite over the given tests (all by default)\n"
-	     "The command-line arguments are interpreted as follows:\n"
-	     "\n"
-	     "  -r	raw; display each time measurement instead of summary statistics\n"
-	     "  -i	iterations; specify the number of iterations per test case\n"
-	     "  -l	list only; just list selected test case names without executing\n"
-	     "\n"
-	     "If test names are given they are used as sub-string matches so a command\n"
-	     "such as \"cairo-perf text\" can be used to run all text test cases.\n",
+"Usage: %s [-l] [-r] [-v] [-i iterations] [test-names ...]\n"
+"       %s -l\n"
+"\n"
+"Run the cairo performance test suite over the given tests (all by default)\n"
+"The command-line arguments are interpreted as follows:\n"
+"\n"
+"  -r	raw; display each time measurement instead of summary statistics\n"
+"  -v	verbose; in raw mode also show the summaries\n"
+"  -i	iterations; specify the number of iterations per test case\n"
+"  -l	list only; just list selected test case names without executing\n"
+"\n"
+"If test names are given they are used as sub-string matches so a command\n"
+"such as \"cairo-perf text\" can be used to run all text test cases.\n",
 	     argv0, argv0);
 }
 
@@ -276,6 +288,7 @@ parse_options (cairo_perf_t *perf, int argc, char *argv[])
     int c;
     const char *iters;
     char *end;
+    int verbose = 0;
 
     if ((iters = getenv("CAIRO_PERF_ITERATIONS")) && *iters)
 	perf->iterations = strtol(iters, NULL, 0);
@@ -287,9 +300,10 @@ parse_options (cairo_perf_t *perf, int argc, char *argv[])
     perf->list_only = FALSE;
     perf->names = NULL;
     perf->num_names = 0;
+    perf->summary = stdout;
 
     while (1) {
-	c = _cairo_getopt (argc, argv, "i:lr");
+	c = _cairo_getopt (argc, argv, "i:lrv");
 	if (c == -1)
 	    break;
 
@@ -308,6 +322,10 @@ parse_options (cairo_perf_t *perf, int argc, char *argv[])
 	    break;
 	case 'r':
 	    perf->raw = TRUE;
+	    perf->summary = NULL;
+	    break;
+	case 'v':
+	    verbose = 1;
 	    break;
 	default:
 	    fprintf (stderr, "Internal error: unhandled option: %c\n", c);
@@ -317,6 +335,10 @@ parse_options (cairo_perf_t *perf, int argc, char *argv[])
 	    exit (1);
 	}
     }
+
+    if (verbose && perf->summary == NULL)
+	perf->summary = stderr;
+
     if (optind < argc) {
 	perf->names = &argv[optind];
 	perf->num_names = argc - optind;
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index 69eb639..05447fb 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -29,6 +29,7 @@
 #define _CAIRO_PERF_H_
 
 #include "cairo-boilerplate.h"
+#include <stdio.h>
 
 typedef uint64_t cairo_perf_ticks_t;
 
@@ -68,6 +69,8 @@ cairo_perf_yield (void);
 
 /* running a test case */
 typedef struct _cairo_perf {
+    FILE *summary;
+
     /* Options from command-line */
     unsigned int iterations;
     cairo_bool_t exact_iterations;
commit 70fd2bbce38fca65db6725c632ee591352dfe42c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jun 2 09:00:21 2009 +0100

    [perf] Exclude similar testing by default
    
    There are synchronisation issues with similar surfaces (as only the
    original target surface is synced) which interferes with making
    performance comparisons. (There still maybe some value should you be aware
    of the limitations...)

diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 67a4e51..72092fd 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -128,12 +128,13 @@ _content_to_string (cairo_content_t content, cairo_bool_t similar)
 static cairo_bool_t
 cairo_perf_has_similar (cairo_perf_t *perf)
 {
-    cairo_surface_t *target = cairo_get_target (perf->cr);
+    cairo_surface_t *target;
 
-    if (getenv ("CAIRO_TEST_IGNORE_SIMILAR"))
+    if (getenv ("CAIRO_TEST_SIMILAR") == NULL)
 	return FALSE;
 
     /* exclude the image backend */
+    target = cairo_get_target (perf->cr);
     if (cairo_surface_get_type (target) == CAIRO_SURFACE_TYPE_IMAGE)
 	return FALSE;
 
commit bc49cb377c70c4c6926ca52e34094e99a4591f14
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat May 30 12:28:23 2009 +0100

    [perf] Benchmark mixing different masks and sources

diff --git a/perf/Makefile.am b/perf/Makefile.am
index d33e511..653d93d 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -33,6 +33,7 @@ cairo_perf_SOURCES =		\
 	mosaic.h		\
 	paint.c			\
 	paint-with-alpha.c	\
+	mask.c			\
 	pattern_create_radial.c \
 	rectangles.c		\
 	rounded-rectangles.c	\
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 085a0c0..67a4e51 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -457,6 +457,7 @@ const cairo_perf_case_t perf_cases[] = {
     { stroke, 64, 512},
     { text,   64, 512},
     { glyphs,   64, 512},
+    { mask,  64, 512},
     { tessellate, 100, 100},
     { subimage_copy, 16, 512},
     { pattern_create_radial, 16, 16},
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index 0a3606b..69eb639 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -166,6 +166,7 @@ test_report_cmp_name (const void *a, const void *b);
 CAIRO_PERF_DECL (fill);
 CAIRO_PERF_DECL (paint);
 CAIRO_PERF_DECL (paint_with_alpha);
+CAIRO_PERF_DECL (mask);
 CAIRO_PERF_DECL (stroke);
 CAIRO_PERF_DECL (subimage_copy);
 CAIRO_PERF_DECL (tessellate);
diff --git a/perf/mask.c b/perf/mask.c
new file mode 100644
index 0000000..4dc305e
--- /dev/null
+++ b/perf/mask.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 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
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ *         Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_perf_ticks_t
+do_mask_solid (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_rgba (0, 0, 0, .5);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_surface_t *
+init_surface (cairo_surface_t *surface, int width, int height)
+{
+    cairo_t *cr;
+
+    cr = cairo_create (surface);
+    cairo_surface_destroy (surface);
+
+    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+    cairo_set_source_rgb (cr, 0, 0, 0); /* back */
+    cairo_paint (cr);
+
+    cairo_set_source_rgba (cr, 1, 1, 1, 0.5); /* 50% */
+    cairo_new_path (cr);
+    cairo_rectangle (cr, 0, 0, width/2.0, height/2.0);
+    cairo_rectangle (cr, width/2.0, height/2.0, width/2.0, height/2.0);
+    cairo_fill (cr);
+
+    surface = cairo_surface_reference (cairo_get_target (cr));
+    cairo_destroy (cr);
+
+    return surface;
+}
+
+static cairo_perf_ticks_t
+do_mask_image (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_image_half (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, .5, .5);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_image_double (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, 2., 2.);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_similar (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+					    CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_similar_half (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+					    CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, .5, .5);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_similar_double (cairo_t *cr, int width, int height)
+{
+    cairo_surface_t *surface;
+    cairo_pattern_t *mask;
+    cairo_matrix_t matrix;
+
+    surface = cairo_surface_create_similar (cairo_get_group_target (cr),
+					    CAIRO_CONTENT_ALPHA, width, height);
+    mask = cairo_pattern_create_for_surface (init_surface (surface,
+							   width,
+							   height));
+    cairo_surface_destroy (surface);
+    cairo_matrix_init_scale (&matrix, 2., 2.);
+    cairo_pattern_set_matrix (mask, &matrix);
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_linear (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_linear (0.0, 0.0, width, height);
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 0.5); /*  50% */
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 1.0); /* 100% */
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
+do_mask_radial (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *mask;
+
+    mask = cairo_pattern_create_radial (width/2.0, height/2.0, 0.0,
+					width/2.0, height/2.0, width/2.0);
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 0.5); /*  50% */
+    cairo_pattern_add_color_stop_rgba (mask, 0.0, 0, 0, 0, 1.0); /* 100% */
+
+    cairo_perf_timer_start ();
+
+    cairo_mask (cr, mask);
+
+    cairo_perf_timer_stop ();
+
+    cairo_pattern_destroy (mask);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+void
+mask (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    if (! cairo_perf_can_run (perf, "mask"))
+	return;
+
+    cairo_perf_cover_sources_and_operators (perf, "mask-solid",
+					    do_mask_solid);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image",
+					    do_mask_image);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image-half",
+					    do_mask_image_half);
+    cairo_perf_cover_sources_and_operators (perf, "mask-image-double",
+					    do_mask_image_double);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar",
+					    do_mask_similar);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar-half",
+					    do_mask_similar_half);
+    cairo_perf_cover_sources_and_operators (perf, "mask-similar-double",
+					    do_mask_similar_double);
+    cairo_perf_cover_sources_and_operators (perf, "mask-linear",
+					    do_mask_linear);
+    cairo_perf_cover_sources_and_operators (perf, "mask-radial",
+					    do_mask_radial);
+}
commit 4aca84ddb22bc178cbc9b132b9ce06af3f4b300b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 28 18:05:26 2009 +0100

    [perf] Add a pure glyphs performance metric
    
    Use the new API Behdad exposed in 1.8 to precompute a glyph string using
    Cairo and then benchmark cairo_show_glyphs(). This is then equivalent to
    the text benchmark but without the extra step of converting to glyphs on
    every call to cairo_show_text() i.e. it shows the underlying glyph
    rendering performance.

diff --git a/perf/Makefile.am b/perf/Makefile.am
index 08f9cc0..d33e511 100644
--- a/perf/Makefile.am
+++ b/perf/Makefile.am
@@ -40,6 +40,7 @@ cairo_perf_SOURCES =		\
 	subimage_copy.c		\
 	tessellate.c		\
 	text.c			\
+	glyphs.c		\
 	twin.c			\
 	unaligned-clip.c	\
 	world-map.c		\
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index 64136e0..085a0c0 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -456,6 +456,7 @@ const cairo_perf_case_t perf_cases[] = {
     { fill,   64, 512},
     { stroke, 64, 512},
     { text,   64, 512},
+    { glyphs,   64, 512},
     { tessellate, 100, 100},
     { subimage_copy, 16, 512},
     { pattern_create_radial, 16, 16},
diff --git a/perf/cairo-perf.h b/perf/cairo-perf.h
index 7e792ad..0a3606b 100644
--- a/perf/cairo-perf.h
+++ b/perf/cairo-perf.h
@@ -170,6 +170,7 @@ CAIRO_PERF_DECL (stroke);
 CAIRO_PERF_DECL (subimage_copy);
 CAIRO_PERF_DECL (tessellate);
 CAIRO_PERF_DECL (text);
+CAIRO_PERF_DECL (glyphs);
 CAIRO_PERF_DECL (pattern_create_radial);
 CAIRO_PERF_DECL (zrusin);
 CAIRO_PERF_DECL (world_map);
diff --git a/perf/glyphs.c b/perf/glyphs.c
new file mode 100644
index 0000000..fdd4d97
--- /dev/null
+++ b/perf/glyphs.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ * Copyright © 2009 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
+ * Red Hat, Inc. not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Red Hat, Inc. makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * RED HAT, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL RED HAT, INC. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ *         Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairo-perf.h"
+
+static cairo_perf_ticks_t
+do_glyphs (cairo_t *cr, int width, int height)
+{
+    const char text[] = "the jay, pig, fox, zebra and my wolves quack";
+    cairo_scaled_font_t *scaled_font;
+    cairo_glyph_t *glyphs = NULL, *glyphs_copy;
+    cairo_text_extents_t extents;
+    cairo_status_t status;
+    double x, y;
+    int num_glyphs, n;
+
+    cairo_set_font_size (cr, 9);
+    scaled_font = cairo_get_scaled_font (cr);
+    status = cairo_scaled_font_text_to_glyphs (scaled_font, 0., 0.,
+					       text, -1,
+					       &glyphs, &num_glyphs,
+					       NULL, NULL,
+					       NULL);
+    if (status)
+	return 0;
+
+    glyphs_copy = cairo_glyph_allocate (num_glyphs);
+    if (glyphs_copy == NULL) {
+	cairo_glyph_free (glyphs);
+	return 0;
+    }
+
+    cairo_scaled_font_glyph_extents (scaled_font,
+				     glyphs, num_glyphs,
+				     &extents);
+    y = 0;
+
+    cairo_perf_timer_start ();
+
+    do {
+	x = 0;
+	do {
+	    for (n = 0; n < num_glyphs; n++) {
+		glyphs_copy[n] = glyphs[n];
+		glyphs_copy[n].x += x;
+		glyphs_copy[n].y += y;
+	    }
+	    cairo_show_glyphs (cr, glyphs_copy, num_glyphs);
+	    if (cairo_status (cr) != CAIRO_STATUS_SUCCESS)
+		goto out;
+
+	    x += extents.width;
+	} while (x < width);
+	y += extents.height;
+    } while (y < height);
+out:
+
+    cairo_perf_timer_stop ();
+
+    cairo_glyph_free (glyphs);
+    cairo_glyph_free (glyphs_copy);
+
+    return cairo_perf_timer_elapsed ();
+}
+
+void
+glyphs (cairo_perf_t *perf, cairo_t *cr, int width, int height)
+{
+    if (! cairo_perf_can_run (perf, "glyphs"))
+	return;
+
+    cairo_perf_cover_sources_and_operators (perf, "glyphs", do_glyphs);
+}
commit 55f4e0e4e8c7df59bfc9e6ffea8daa065276e42f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sat May 30 16:50:17 2009 +0100

    [perf] Change seperators from '-' to '.'
    
    This allows the perf tests to use '-' in the name which is easier to read
    and differentiates with using '_' to separate source and operators.

diff --git a/perf/cairo-perf-compare-backends.c b/perf/cairo-perf-compare-backends.c
index e6ab37f..3f280ce 100644
--- a/perf/cairo-perf-compare-backends.c
+++ b/perf/cairo-perf-compare-backends.c
@@ -225,18 +225,20 @@ cairo_perf_reports_compare (cairo_perf_report_t		*reports,
 		    test_report_cmp_name (tests[i], min_test) == 0)
 	    {
 		test_time = tests[i]->stats.min_ticks;
-		if (options->use_ms)
-		    test_time /= tests[i]->stats.ticks_per_ms;
-		if (diff->num_tests == 0) {
-		    diff->min = test_time;
-		    diff->max = test_time;
-		} else {
-		    if (test_time < diff->min)
+		if (test_time > 0) {
+		    if (options->use_ms)
+			test_time /= tests[i]->stats.ticks_per_ms;
+		    if (diff->num_tests == 0) {
 			diff->min = test_time;
-		    if (test_time > diff->max)
 			diff->max = test_time;
+		    } else {
+			if (test_time < diff->min)
+			    diff->min = test_time;
+			if (test_time > diff->max)
+			    diff->max = test_time;
+		    }
+		    diff->tests[diff->num_tests++] = tests[i];
 		}
-		diff->tests[diff->num_tests++] = tests[i];
 		tests[i]++;
 	    }
 	}
@@ -366,7 +368,7 @@ main (int argc, const char *argv[])
     if (args.num_filenames < 1)
 	usage (argv[0]);
 
-    reports = xmalloc (args.num_filenames * sizeof (cairo_perf_report_t));
+    reports = xcalloc (args.num_filenames, sizeof (cairo_perf_report_t));
 
     for (i = 0; i < args.num_filenames; i++) {
 	cairo_perf_report_load (&reports[i], args.filenames[i],
diff --git a/perf/cairo-perf-cover.c b/perf/cairo-perf-cover.c
index 18a1588..48f13a8 100644
--- a/perf/cairo-perf-cover.c
+++ b/perf/cairo-perf-cover.c
@@ -301,22 +301,22 @@ cairo_perf_cover_sources_and_operators (cairo_perf_t		*perf,
     char *expanded_name;
 
     struct { set_source_func_t set_source; const char *name; } sources[] = {
-	{ set_source_solid_rgb, "solid_rgb" },
-	{ set_source_solid_rgba, "solid_rgba" },
-	{ set_source_image_surface_rgb, "image_rgb" },
-	{ set_source_image_surface_rgba, "image_rgba" },
-	{ set_source_image_surface_rgba_mag, "image_rgba_mag" },
-	{ set_source_image_surface_rgba_min, "image_rgba_min" },
-	{ set_source_similar_surface_rgb, "similar_rgb" },
-	{ set_source_similar_surface_rgba, "similar_rgba" },
-	{ set_source_similar_surface_rgba_mag, "similar_rgba_mag" },
-	{ set_source_similar_surface_rgba_min, "similar_rgba_min" },
-	{ set_source_linear_rgb, "linear_rgb" },
-	{ set_source_linear_rgba, "linear_rgba" },
-	{ set_source_linear3_rgb, "linear3_rgb" },
-	{ set_source_linear3_rgba, "linear3_rgba" },
-	{ set_source_radial_rgb, "radial_rgb" },
-	{ set_source_radial_rgba, "radial_rgba" }
+	{ set_source_solid_rgb, "solid-rgb" },
+	{ set_source_solid_rgba, "solid-rgba" },
+	{ set_source_image_surface_rgb, "image-rgb" },
+	{ set_source_image_surface_rgba, "image-rgba" },
+	{ set_source_image_surface_rgba_mag, "image-rgba-mag" },
+	{ set_source_image_surface_rgba_min, "image-rgba-min" },
+	{ set_source_similar_surface_rgb, "similar-rgb" },
+	{ set_source_similar_surface_rgba, "similar-rgba" },
+	{ set_source_similar_surface_rgba_mag, "similar-rgba-mag" },
+	{ set_source_similar_surface_rgba_min, "similar-rgba-min" },
+	{ set_source_linear_rgb, "linear-rgb" },
+	{ set_source_linear_rgba, "linear-rgba" },
+	{ set_source_linear3_rgb, "linear3-rgb" },
+	{ set_source_linear3_rgba, "linear3-rgba" },
+	{ set_source_radial_rgb, "radial-rgb" },
+	{ set_source_radial_rgba, "radial-rgba" }
     };
 
     struct { cairo_operator_t op; const char *name; } operators[] = {
diff --git a/perf/cairo-perf-report.c b/perf/cairo-perf-report.c
index 8a99b9d..a73538b 100644
--- a/perf/cairo-perf-report.c
+++ b/perf/cairo-perf-report.c
@@ -148,7 +148,7 @@ test_report_parse (test_report_t *report, char *line, char *configuration)
 
     report->configuration = configuration;
     parse_string (report->backend);
-    end = strrchr (report->backend, '-');
+    end = strrchr (report->backend, '.');
     if (*end)
 	*end++ = '\0';
     report->content = end;
@@ -156,7 +156,7 @@ test_report_parse (test_report_t *report, char *line, char *configuration)
     skip_space ();
 
     parse_string (report->name);
-    end = strrchr (report->name, '-');
+    end = strrchr (report->name, '.');
     if (*end)
 	*end++ = '\0';
     report->size = atoi (end);
@@ -173,13 +173,23 @@ test_report_parse (test_report_t *report, char *line, char *configuration)
 
 	report->samples_size = 5;
 	report->samples = xmalloc (report->samples_size * sizeof (cairo_perf_ticks_t));
+	report->stats.min_ticks = 0;
 	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++]);
+	    parse_long_long (report->samples[report->samples_count]);
+	    if (report->samples_count == 0) {
+		report->stats.min_ticks =
+		    report->samples[report->samples_count];
+	    } else if (report->stats.min_ticks >
+		       report->samples[report->samples_count]){
+		report->stats.min_ticks =
+		    report->samples[report->samples_count];
+	    }
+	    report->samples_count++;
 	    skip_space ();
 	} while (*s && *s != '\n');
 	report->stats.iterations = 0;
diff --git a/perf/cairo-perf.c b/perf/cairo-perf.c
index d7a7e88..64136e0 100644
--- a/perf/cairo-perf.c
+++ b/perf/cairo-perf.c
@@ -174,10 +174,10 @@ cairo_perf_run (cairo_perf_t		*perf,
 
     if (first_run) {
 	if (perf->raw)
-	    printf ("[ # ] %s-%-s %s %s %s ...\n",
+	    printf ("[ # ] %s.%-s %s %s %s ...\n",
 		    "backend", "content", "test-size", "ticks-per-ms", "time(ticks)");
 	else
-	    printf ("[ # ] %8s-%-4s %28s %8s %8s %5s %5s %s\n",
+	    printf ("[ # ] %8s.%-4s %28s %8s %8s %5s %5s %s\n",
 		    "backend", "content", "test-size", "min(ticks)", "min(ms)", "median(ms)",
 		    "stddev.", "iterations");
 	first_run = FALSE;
@@ -208,7 +208,7 @@ cairo_perf_run (cairo_perf_t		*perf,
 
 	    if (perf->raw) {
 		if (i == 0)
-		    printf ("[*] %s-%s %s-%d %g",
+		    printf ("[*] %s.%s %s.%d %g",
 			    perf->target->name,
 			    _content_to_string (perf->target->content, similar),
 			    name, perf->size,
@@ -234,7 +234,7 @@ cairo_perf_run (cairo_perf_t		*perf,
 	    printf ("\n");
 	} else {
 	    _cairo_stats_compute (&stats, times, i);
-	    printf ("[%3d] %8s-%-5s %26s-%-3d ",
+	    printf ("[%3d] %8s.%-5s %26s.%-3d ",
 		    perf->test_number, perf->target->name,
 		    _content_to_string (perf->target->content, similar),
 		    name, perf->size);
diff --git a/perf/mosaic.c b/perf/mosaic.c
index 257a362..bab6276 100644
--- a/perf/mosaic.c
+++ b/perf/mosaic.c
@@ -164,8 +164,8 @@ mosaic (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     if (! cairo_perf_can_run (perf, "mosaic"))
 	return;
 
-    cairo_perf_run (perf, "mosaic_fill_curves", mosaic_fill_curves);
-    cairo_perf_run (perf, "mosaic_fill_lines", mosaic_fill_lines);
-    cairo_perf_run (perf, "mosaic_tessellate_curves", mosaic_tessellate_curves);
-    cairo_perf_run (perf, "mosaic_tessellate_lines", mosaic_tessellate_lines);
+    cairo_perf_run (perf, "mosaic-fill-curves", mosaic_fill_curves);
+    cairo_perf_run (perf, "mosaic-fill-lines", mosaic_fill_lines);
+    cairo_perf_run (perf, "mosaic-tessellate-curves", mosaic_tessellate_curves);
+    cairo_perf_run (perf, "mosaic-tessellate-lines", mosaic_tessellate_lines);
 }
diff --git a/perf/pattern_create_radial.c b/perf/pattern_create_radial.c
index 8fa683b..b908151 100644
--- a/perf/pattern_create_radial.c
+++ b/perf/pattern_create_radial.c
@@ -82,7 +82,7 @@ pattern_create_radial (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
     int i;
 
-    if (! cairo_perf_can_run (perf, "pattern_create_radial"))
+    if (! cairo_perf_can_run (perf, "pattern-create-radial"))
 	return;
 
     srand (time (0));
@@ -96,6 +96,6 @@ pattern_create_radial (cairo_perf_t *perf, cairo_t *cr, int width, int height)
         radials[i].radius1 = generate_double_in_range (0.0, 1000.0);
     }
 
-    cairo_perf_run (perf, "pattern_create_radial",
+    cairo_perf_run (perf, "pattern-create-radial",
                           do_pattern_create_radial);
 }
diff --git a/perf/pythagoras-tree.c b/perf/pythagoras-tree.c
index 750e83b..a1cce5c 100644
--- a/perf/pythagoras-tree.c
+++ b/perf/pythagoras-tree.c
@@ -82,7 +82,7 @@ do_pythagoras_tree (cairo_t *cr, int width, int height)
 void
 pythagoras_tree (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "pythagoras_tree"))
+    if (! cairo_perf_can_run (perf, "pythagoras-tree"))
 	return;
 
     cairo_perf_run (perf, "pythagoras_tree", do_pythagoras_tree);
diff --git a/perf/subimage_copy.c b/perf/subimage_copy.c
index 722705b..25b16cc 100644
--- a/perf/subimage_copy.c
+++ b/perf/subimage_copy.c
@@ -55,7 +55,7 @@ subimage_copy (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     cairo_surface_t *image;
     cairo_t *cr2;
 
-    if (! cairo_perf_can_run (perf, "subimage_copy"))
+    if (! cairo_perf_can_run (perf, "subimage-copy"))
 	return;
 
     cairo_set_source_rgb (cr, 0, 0, 1); /* blue */
@@ -70,5 +70,5 @@ subimage_copy (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     cairo_set_source_surface (cr, image, 0, 0);
     cairo_surface_destroy (image);
 
-    cairo_perf_run (perf, "subimage_copy", do_subimage_copy);
+    cairo_perf_run (perf, "subimage-copy", do_subimage_copy);
 }
diff --git a/perf/unaligned-clip.c b/perf/unaligned-clip.c
index a757fa6..7fd5245 100644
--- a/perf/unaligned-clip.c
+++ b/perf/unaligned-clip.c
@@ -62,5 +62,5 @@ unaligned_clip (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     if (! cairo_perf_can_run (perf, "unaligned-clip"))
 	return;
 
-    cairo_perf_run (perf, "unaligned_clip", do_unaligned_clip);
+    cairo_perf_run (perf, "unaligned-clip", do_unaligned_clip);
 }
diff --git a/perf/world-map.c b/perf/world-map.c
index 5b8be45..5a53f30 100644
--- a/perf/world-map.c
+++ b/perf/world-map.c
@@ -105,8 +105,8 @@ do_world_map (cairo_t *cr, int width, int height)
 void
 world_map (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 {
-    if (! cairo_perf_can_run (perf, "world_map"))
+    if (! cairo_perf_can_run (perf, "world-map"))
 	return;
 
-    cairo_perf_run (perf, "world_map", do_world_map);
+    cairo_perf_run (perf, "world-map", do_world_map);
 }
diff --git a/perf/zrusin.c b/perf/zrusin.c
index 2195615..9393932 100644
--- a/perf/zrusin.c
+++ b/perf/zrusin.c
@@ -88,6 +88,6 @@ zrusin (cairo_perf_t *perf, cairo_t *cr, int width, int height)
     if (! cairo_perf_can_run (perf, "zrusin"))
 	return;
 
-    cairo_perf_run (perf, "zrusin_another_tessellate", zrusin_another_tessellate);
-    cairo_perf_run (perf, "zrusin_another_fill", zrusin_another_fill);
+    cairo_perf_run (perf, "zrusin-another-tessellate", zrusin_another_tessellate);
+    cairo_perf_run (perf, "zrusin-another-fill", zrusin_another_fill);
 }
commit b7c03d4e400245fc2b2d748fe808a9724e61f22f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 16:31:51 2009 +0100

    [ps] Trivial warning fix.

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 0037905..fde161d 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1896,6 +1896,7 @@ _cairo_ps_surface_emit_image (cairo_ps_surface_t    *surface,
 	return image->base.status;
 
     switch (filter) {
+    default:
     case CAIRO_FILTER_GOOD:
     case CAIRO_FILTER_BEST:
     case CAIRO_FILTER_BILINEAR:
commit 99482b17a50a8ae52b7627bf624e358ed3623540
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 18:34:54 2009 +0100

    [script] Improve caching of glyph advance
    
    The glyph advance cache was only enabled for glyph indices < 256,
    causing a large number of misses for non-ASCII text. Improve this by
    simply applying the modulus of the index to select the cache slot - which
    may cause some glyph advances to be overwritten and re-queried, but
    improves the hit rate.

diff --git a/util/cairo-script/cairo-script-operators.c b/util/cairo-script/cairo-script-operators.c
index 0ed3dd3..a1f848e 100644
--- a/util/cairo-script/cairo-script-operators.c
+++ b/util/cairo-script/cairo-script-operators.c
@@ -4705,7 +4705,7 @@ _show_glyphs (csi_t *ctx)
     double x,y;
     csi_integer_t nglyphs, i, j;
     double glyph_advance[256][2];
-    int have_glyph_advance[256];
+    unsigned long have_glyph_advance[256];
 
     check (2);
 
@@ -4746,7 +4746,7 @@ _show_glyphs (csi_t *ctx)
     scaled_font = cairo_get_scaled_font (cr);
 
     nglyphs = 0;
-    memset (have_glyph_advance, 0, sizeof (have_glyph_advance));
+    memset (have_glyph_advance, 0xff, sizeof (have_glyph_advance));
     x = y = 0;
     for (i = 0; i < array->stack.len; i++) {
 	obj = &array->stack.objects[i];
@@ -4755,6 +4755,7 @@ _show_glyphs (csi_t *ctx)
 	    glyph_array = obj->datum.array;
 	    for (j = 0; j < glyph_array->stack.len; j++) {
 		unsigned long g;
+		int gi;
 		cairo_bool_t have_advance;
 
 		obj = &glyph_array->stack.objects[j];
@@ -4766,35 +4767,23 @@ _show_glyphs (csi_t *ctx)
 		glyphs[nglyphs].x = x;
 		glyphs[nglyphs].y = y;
 
-		if (g < ARRAY_LENGTH (have_glyph_advance)) {
-		    if (! have_glyph_advance[g]) {
-			cairo_text_extents_t extents;
-
-			cairo_scaled_font_glyph_extents (scaled_font,
-							 &glyphs[nglyphs], 1,
-							 &extents);
-
-			glyph_advance[g][0] = extents.x_advance;
-			glyph_advance[g][1] = extents.y_advance;
-			have_glyph_advance[g] = TRUE;
-
-		    }
-
-		    have_advance = glyph_advance[g][0] != 0.0;
-		    x += glyph_advance[g][0];
-		    y += glyph_advance[g][1];
-		} else {
+		gi = g % ARRAY_LENGTH (have_glyph_advance);
+		if (have_glyph_advance[gi] != g) {
 		    cairo_text_extents_t extents;
 
 		    cairo_scaled_font_glyph_extents (scaled_font,
 						     &glyphs[nglyphs], 1,
 						     &extents);
 
-		    have_advance = extents.x_advance != 0.0;
-		    x += extents.x_advance;
-		    y += extents.y_advance;
+		    glyph_advance[gi][0] = extents.x_advance;
+		    glyph_advance[gi][1] = extents.y_advance;
+		    have_glyph_advance[gi] = g;
 		}
 
+		have_advance = glyph_advance[gi][0] != 0.0;
+		x += glyph_advance[gi][0];
+		y += glyph_advance[gi][1];
+
 		nglyphs += have_advance;
 	    }
 	    break;
@@ -4810,7 +4799,7 @@ _show_glyphs (csi_t *ctx)
 		glyphs[nglyphs].x = x;
 		glyphs[nglyphs].y = y;
 
-		if (! have_glyph_advance[g]) {
+		if (have_glyph_advance[g] != g) {
 		    cairo_text_extents_t extents;
 
 		    cairo_scaled_font_glyph_extents (scaled_font,
@@ -4819,7 +4808,7 @@ _show_glyphs (csi_t *ctx)
 
 		    glyph_advance[g][0] = extents.x_advance;
 		    glyph_advance[g][1] = extents.y_advance;
-		    have_glyph_advance[g] = TRUE;
+		    have_glyph_advance[g] = g;
 		}
 
 		have_advance = glyph_advance[g][0] != 0.0;
commit 51193f1610c0f6893768e43e90a3395ec96c28fb
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 28 14:16:58 2009 +0100

    [pattern] Pass flags to _cairo_pattern_acquire_surface()
    
    Allow the caller to choose whether or not various conversions take place.
    The first flag is used to disable the expansion of reflected patterns into a
    repeating surface.

diff --git a/src/cairo-directfb-surface.c b/src/cairo-directfb-surface.c
index df9c0e9..1747117 100644
--- a/src/cairo-directfb-surface.c
+++ b/src/cairo-directfb-surface.c
@@ -766,6 +766,7 @@ _directfb_prepare_composite (cairo_directfb_surface_t    *dst,
     status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
 					     CAIRO_CONTENT_COLOR_ALPHA,
 					     *src_x, *src_y, width, height,
+					     CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
 					     (cairo_surface_t **) &src,
 					     &src_attr);
     if (status)
diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c
index 9ee8a88..7cc2b23 100644
--- a/src/cairo-glitz-surface.c
+++ b/src/cairo-glitz-surface.c
@@ -719,6 +719,7 @@ _cairo_glitz_pattern_acquire_surface (const cairo_pattern_t	       *pattern,
 	status = _cairo_pattern_acquire_surface (pattern, &dst->base,
 						 CAIRO_CONTENT_COLOR_ALPHA,
 						 x, y, width, height,
+						 CAIRO_PATTERN_ACQUIRE_NONE,
 						 (cairo_surface_t **) &src,
 						 &attr->base);
 	if (status)
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 4298663..e37fa87 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -967,6 +967,7 @@ _cairo_image_surface_composite (cairo_operator_t	op,
 					      src_x, src_y,
 					      mask_x, mask_y,
 					      width, height,
+					      CAIRO_PATTERN_ACQUIRE_NONE,
 					      (cairo_surface_t **) &src,
 					      (cairo_surface_t **) &mask,
 					      &src_attr, &mask_attr);
@@ -1171,6 +1172,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     status = _cairo_pattern_acquire_surface (pattern, &dst->base,
 					     CAIRO_CONTENT_COLOR_ALPHA,
 					     src_x, src_y, width, height,
+					     CAIRO_PATTERN_ACQUIRE_NONE,
 					     (cairo_surface_t **) &src,
 					     &attributes);
     if (unlikely (status))
@@ -1402,6 +1404,7 @@ _cairo_image_surface_create_span_renderer (cairo_operator_t	 op,
 	CAIRO_CONTENT_COLOR_ALPHA,
 	rects->src.x, rects->src.y,
 	width, height,
+	CAIRO_PATTERN_ACQUIRE_NONE,
 	(cairo_surface_t **) &renderer->src,
 	&renderer->src_attributes);
     if (status)
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index cf41a87..b959aba 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1794,6 +1794,7 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t   *pat
 					    int			       y,
 					    unsigned int	       width,
 					    unsigned int	       height,
+					    unsigned int	       flags,
 					    cairo_surface_t	       **out,
 					    cairo_surface_attributes_t *attr)
 {
@@ -1855,7 +1856,9 @@ _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t   *pat
      * images such that the new image, when repeated, has the same effect
      * of reflecting the original pattern.
      */
-    if (attr->extend == CAIRO_EXTEND_REFLECT) {
+    if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT &&
+	attr->extend == CAIRO_EXTEND_REFLECT)
+    {
 	cairo_t *cr;
 	cairo_surface_t *src;
 	int w, h;
@@ -2081,6 +2084,7 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t	   *pattern,
 				int			   y,
 				unsigned int		   width,
 				unsigned int		   height,
+				unsigned int		   flags,
 				cairo_surface_t		   **surface_out,
 				cairo_surface_attributes_t *attributes)
 {
@@ -2170,7 +2174,9 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t	   *pattern,
 
 	status = _cairo_pattern_acquire_surface_for_surface (src, dst,
 							     content,
-							     x, y, width, height,
+							     x, y,
+							     width, height,
+							     flags,
 							     surface_out,
 							     attributes);
     } break;
@@ -2209,6 +2215,7 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t	    *src,
 				 int			    mask_y,
 				 unsigned int		    width,
 				 unsigned int		    height,
+				 unsigned int		    flags,
 				 cairo_surface_t	    **src_out,
 				 cairo_surface_t	    **mask_out,
 				 cairo_surface_attributes_t *src_attributes,
@@ -2249,6 +2256,7 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t	    *src,
 					     src_content,
 					     src_x, src_y,
 					     width, height,
+					     flags,
 					     src_out, src_attributes);
     if (unlikely (status))
 	goto BAIL;
@@ -2262,6 +2270,7 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t	    *src,
 					     CAIRO_CONTENT_ALPHA,
 					     mask_x, mask_y,
 					     width, height,
+					     flags,
 					     mask_out, mask_attributes);
     if (unlikely (status))
 	_cairo_pattern_release_surface (src, *src_out, src_attributes);
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index c21e600..077e1c4 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -1124,6 +1124,7 @@ _cairo_xcb_surface_composite (cairo_operator_t		op,
 					      src_x, src_y,
 					      mask_x, mask_y,
 					      width, height,
+					      CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
 					      (cairo_surface_t **) &src,
 					      (cairo_surface_t **) &mask,
 					      &src_attr, &mask_attr);
@@ -1411,6 +1412,7 @@ _cairo_xcb_surface_composite_trapezoids (cairo_operator_t	op,
     status = _cairo_pattern_acquire_surface (pattern, &dst->base,
 					     CAIRO_CONTENT_COLOR_ALPHA,
 					     src_x, src_y, width, height,
+					     CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
 					     (cairo_surface_t **) &src,
 					     &attributes);
     if (status)
@@ -2504,6 +2506,7 @@ _cairo_xcb_surface_show_glyphs (void			*abstract_dst,
         status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
 						 CAIRO_CONTENT_COLOR_ALPHA,
                                                  0, 0, 1, 1,
+						 CAIRO_PATTERN_ACQUIRE_NONE,
                                                  (cairo_surface_t **) &src,
                                                  &attributes);
     } else {
@@ -2520,6 +2523,7 @@ _cairo_xcb_surface_show_glyphs (void			*abstract_dst,
 						 CAIRO_CONTENT_COLOR_ALPHA,
                                                  glyph_extents.x, glyph_extents.y,
                                                  glyph_extents.width, glyph_extents.height,
+						 CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
                                                  (cairo_surface_t **) &src,
                                                  &attributes);
     }
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index e456691..ce34061 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1752,6 +1752,7 @@ _cairo_xlib_surface_composite (cairo_operator_t		op,
 					      src_x, src_y,
 					      mask_x, mask_y,
 					      width, height,
+					      CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
 					      (cairo_surface_t **) &src,
 					      (cairo_surface_t **) &mask,
 					      &src_attr, &mask_attr);
@@ -1914,6 +1915,7 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t    *surface,
 					     0, 0,
 					     ARRAY_LENGTH (dither_pattern[0]),
 					     ARRAY_LENGTH (dither_pattern),
+					     CAIRO_PATTERN_ACQUIRE_NONE,
 					     &solid_surface,
 					     &attrs);
     if (unlikely (status))
@@ -2163,6 +2165,7 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t	op,
     status = _cairo_pattern_acquire_surface (pattern, &dst->base,
 					     CAIRO_CONTENT_COLOR_ALPHA,
 					     src_x, src_y, width, height,
+					     CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
 					     (cairo_surface_t **) &src,
 					     &attributes);
     if (unlikely (status))
@@ -4093,6 +4096,7 @@ _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
         status = _cairo_pattern_acquire_surface (src_pattern, &dst->base,
 						 CAIRO_CONTENT_COLOR_ALPHA,
                                                  0, 0, 1, 1,
+						 CAIRO_PATTERN_ACQUIRE_NONE,
                                                  (cairo_surface_t **) &src,
                                                  &attributes);
 	if (unlikely (status))
@@ -4111,6 +4115,7 @@ _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
 						 CAIRO_CONTENT_COLOR_ALPHA,
                                                  glyph_extents.x, glyph_extents.y,
                                                  glyph_extents.width, glyph_extents.height,
+						 CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
                                                  (cairo_surface_t **) &src,
                                                  &attributes);
         if (unlikely (status))
diff --git a/src/cairoint.h b/src/cairoint.h
index 5e543d0..fe2a1af 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2491,6 +2491,10 @@ _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 cairo_private cairo_bool_t
 _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern);
 
+enum {
+    CAIRO_PATTERN_ACQUIRE_NONE = 0x0,
+    CAIRO_PATTERN_ACQUIRE_NO_REFLECT = 0x1,
+};
 cairo_private cairo_int_status_t
 _cairo_pattern_acquire_surface (const cairo_pattern_t	   *pattern,
 				cairo_surface_t		   *dst,
@@ -2499,6 +2503,7 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t	   *pattern,
 				int			   y,
 				unsigned int		   width,
 				unsigned int		   height,
+				unsigned int		   flags,
 				cairo_surface_t		   **surface_out,
 				cairo_surface_attributes_t *attributes);
 
@@ -2518,6 +2523,7 @@ _cairo_pattern_acquire_surfaces (const cairo_pattern_t	    *src,
 				 int			    mask_y,
 				 unsigned int		    width,
 				 unsigned int		    height,
+				 unsigned int		    flags,
 				 cairo_surface_t	    **src_out,
 				 cairo_surface_t	    **mask_out,
 				 cairo_surface_attributes_t *src_attributes,
commit 4ec451a2fa4050a60a81d01473d23ff2973f47e6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 17:35:54 2009 +0100

    [scaled-font] Cache repeated glyphs during probing device_extents
    
    Maintain a local on-stack cache of recently queried glyphs to avoid
    relatively expensive hash table queries.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 611e95d..89fc8b0 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1878,37 +1878,48 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
     int i;
     cairo_point_int_t min = { CAIRO_RECT_INT_MAX, CAIRO_RECT_INT_MAX };
     cairo_point_int_t max = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN };
+    cairo_scaled_glyph_t *glyph_cache[64];
 
-    if (scaled_font->status)
+    if (unlikely (scaled_font->status))
 	return scaled_font->status;
 
     _cairo_scaled_font_freeze_cache (scaled_font);
 
+    memset (glyph_cache, 0, sizeof (glyph_cache));
+
     for (i = 0; i < num_glyphs; i++) {
 	cairo_scaled_glyph_t	*scaled_glyph;
 	int			left, top;
 	int			right, bottom;
 	int			x, y;
+	int cache_index = glyphs[i].index % ARRAY_LENGTH (glyph_cache);
 
-	status = _cairo_scaled_glyph_lookup (scaled_font,
-					     glyphs[i].index,
-					     CAIRO_SCALED_GLYPH_INFO_METRICS,
-					     &scaled_glyph);
-	if (unlikely (status))
-	    break;
+	scaled_glyph = glyph_cache[cache_index];
+	if (scaled_glyph == NULL ||
+	    _cairo_scaled_glyph_index (scaled_glyph) != glyphs[i].index)
+	{
+	    status = _cairo_scaled_glyph_lookup (scaled_font,
+						 glyphs[i].index,
+						 CAIRO_SCALED_GLYPH_INFO_METRICS,
+						 &scaled_glyph);
+	    if (unlikely (status))
+		break;
+
+	    glyph_cache[cache_index] = scaled_glyph;
+	}
 
 	/* XXX glyph images are snapped to pixel locations */
 	x = _cairo_lround (glyphs[i].x);
 	y = _cairo_lround (glyphs[i].y);
 
-	left   = x + _cairo_fixed_integer_floor(scaled_glyph->bbox.p1.x);
+	left   = x + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x);
 	top    = y + _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y);
-	right  = x + _cairo_fixed_integer_ceil(scaled_glyph->bbox.p2.x);
+	right  = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
 	bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
 
-	if (left < min.x) min.x = left;
-	if (right > max.x) max.x = right;
-	if (top < min.y) min.y = top;
+	if (left   < min.x) min.x = left;
+	if (right  > max.x) max.x = right;
+	if (top    < min.y) min.y = top;
 	if (bottom > max.y) max.y = bottom;
     }
 
commit ec22ae5b1d7e8ec364813f3b1c73f538018ecb57
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 11:11:06 2009 +0100

    [scaled-font] Compare most recently used font directly
    
    Avoid the relatively expensive _cairo_scaled_font_init_key() operation
    when checking against the most recently used font.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 92d7b5b..611e95d 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -644,6 +644,23 @@ _cairo_scaled_font_keys_equal (const void *abstract_key_a,
 }
 
 static cairo_bool_t
+_cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font,
+	                    const cairo_font_face_t *font_face,
+			    const cairo_matrix_t *font_matrix,
+			    const cairo_matrix_t *ctm,
+			    const cairo_font_options_t *options)
+{
+    return scaled_font->font_face == font_face &&
+	    memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
+		    (unsigned char *)(&font_matrix->xx),
+		    sizeof(cairo_matrix_t)) == 0 &&
+	    memcmp ((unsigned char *)(&scaled_font->ctm.xx),
+		    (unsigned char *)(&ctm->xx),
+		    sizeof(cairo_matrix_t)) == 0 &&
+	    cairo_font_options_equal (&scaled_font->options, options);
+}
+
+static cairo_bool_t
 _cairo_scaled_glyphs_equal (const void *abstract_a, const void *abstract_b)
 {
     const cairo_scaled_glyph_t *a = abstract_a;
@@ -892,16 +909,14 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     if (unlikely (font_map == NULL))
 	return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
 
-    _cairo_scaled_font_init_key (&key, font_face,
-				 font_matrix, ctm, options);
     scaled_font = font_map->mru_scaled_font;
     if (scaled_font != NULL &&
-	scaled_font->hash_entry.hash == key.hash_entry.hash &&
-	_cairo_scaled_font_keys_equal (scaled_font, &key))
+	_cairo_scaled_font_matches (scaled_font,
+	                            font_face, font_matrix, ctm, options))
     {
 	assert (! scaled_font->placeholder);
 
-	if (scaled_font->status == CAIRO_STATUS_SUCCESS) {
+	if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
 	    /* We increment the reference count manually here, (rather
 	     * than calling into cairo_scaled_font_reference), since we
 	     * must modify the reference count while our lock is still
@@ -918,6 +933,9 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     }
     else
     {
+	_cairo_scaled_font_init_key (&key, font_face,
+				     font_matrix, ctm, options);
+
 	while ((scaled_font = _cairo_hash_table_lookup (font_map->hash_table,
 							&key.hash_entry)))
 	{
commit 16d128c15edf36a6e285fe4fbd6a828b64fd7f87
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 21:10:04 2009 +0100

    [scaled-font] Lazily acquire the font_map_lock on font destruction.
    
    We can defer taking the cairo_scaled_font_map_lock until we drop the
    last reference to the scaled font so long as we double check the reference
    count after waiting for the lock and not making assumptions about
    unreferenced fonts during construction. This is significant as even
    acquiring the uncontended cairo_scaled_font_map_lock during
    cairo_scaled_font_destroy() was showing up as a couple of percent on text
    heavy profiles (e.g. gnome-terminal).

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index b406108..92d7b5b 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -934,25 +934,26 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 	    /* If the original reference count is 0, then this font must have
 	     * been found in font_map->holdovers, (which means this caching is
 	     * actually working). So now we remove it from the holdovers
-	     * array. */
+	     * array, unless we caught the font in the middle of destruction.
+	     */
 	    if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
 		int i;
 
 		for (i = 0; i < font_map->num_holdovers; i++)
 		    if (font_map->holdovers[i] == scaled_font)
 			break;
-		assert (i < font_map->num_holdovers);
-
-		font_map->num_holdovers--;
-		memmove (&font_map->holdovers[i],
-			 &font_map->holdovers[i+1],
-			 (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
+		if (i < font_map->num_holdovers) {
+		    font_map->num_holdovers--;
+		    memmove (&font_map->holdovers[i],
+			     &font_map->holdovers[i+1],
+			     (font_map->num_holdovers - i) * sizeof (cairo_scaled_font_t*));
+		}
 
 		/* reset any error status */
 		scaled_font->status = CAIRO_STATUS_SUCCESS;
 	    }
 
-	    if (scaled_font->status == CAIRO_STATUS_SUCCESS) {
+	    if (likely (scaled_font->status == CAIRO_STATUS_SUCCESS)) {
 		/* We increment the reference count manually here, (rather
 		 * than calling into cairo_scaled_font_reference), since we
 		 * must modify the reference count while our lock is still
@@ -1130,10 +1131,14 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
 
     assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
 
+    if (! _cairo_reference_count_dec_and_test (&scaled_font->ref_count))
+	return;
+
     font_map = _cairo_scaled_font_map_lock ();
     assert (font_map != NULL);
 
-    if (_cairo_reference_count_dec_and_test (&scaled_font->ref_count)) {
+    /* Another thread may have resurrected the font whilst we waited */
+    if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
 	if (! scaled_font->placeholder &&
 	    scaled_font->hash_entry.hash != ZOMBIE)
 	{
@@ -1143,9 +1148,7 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
 	     * the reference count). To make room for it, we do actually
 	     * destroy the least-recently-used holdover.
 	     */
-
-	    if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS)
-	    {
+	    if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) {
 		lru = font_map->holdovers[0];
 		assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
 
@@ -1158,8 +1161,7 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
 			 font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
 	    }
 
-	    font_map->holdovers[font_map->num_holdovers] = scaled_font;
-	    font_map->num_holdovers++;
+	    font_map->holdovers[font_map->num_holdovers++] = scaled_font;
 	} else
 	    lru = scaled_font;
     }
@@ -1172,7 +1174,7 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
      * safely call fini on it without any lock held. This is desirable
      * as we never want to call into any backend function with a lock
      * held. */
-    if (lru) {
+    if (lru != NULL) {
 	_cairo_scaled_font_fini_internal (lru);
 	free (lru);
     }
commit dc083ab30a5b781e205354c525ee054982364abd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 14:54:34 2009 +0100

    [cairo] Track the MRU scaled font
    
    When observing applications two patterns emerge. The first is due to
    Pango, which wraps each glyph run within a context save/restore. This
    causes the scaled font to be evicted after every run and reloaded on the
    next. This is caught by the MRU slot on the cairo_scaled_font_map and
    prevents a relatively costly traversal of the hash table and holdovers.
    
    The second pattern is by applications that directly manage the rendering
    of their own glyphs. The prime example of this is gnome-terminal/vte. Here
    the application frequently alternates between a few scaled fonts - which
    requires a hash table retrieval every time.
    
    By introducing a MRU slot on the gstate we are able to directly recover
    the scaled font around 90% of the time.
    
    Of 110,000 set-scaled-fonts:
      4,000 were setting the current font
     96,000 were setting to the previous font
      2,500 were recovered from the MRU on the cairo_scaled_font_map
      7,500 needed a hash retrieval
    which compares to ~106,000 hash lookups without the additional MRU slot on
    the gstate.
    
    This translates to an elapsed time saving of ~5% when replaying a
    gnome-terminal trace using the drm backend.

diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h
index 2590048..cb278e8 100644
--- a/src/cairo-gstate-private.h
+++ b/src/cairo-gstate-private.h
@@ -50,6 +50,7 @@ struct _cairo_gstate {
 
     cairo_font_face_t *font_face;
     cairo_scaled_font_t *scaled_font;	/* Specific to the current CTM */
+    cairo_scaled_font_t *previous_scaled_font;	/* holdover */
     cairo_matrix_t font_matrix;
     cairo_font_options_t font_options;
 
diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index c7c4cf5..6a1fa72 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -90,6 +90,7 @@ _cairo_gstate_init (cairo_gstate_t  *gstate,
 
     gstate->font_face = NULL;
     gstate->scaled_font = NULL;
+    gstate->previous_scaled_font = NULL;
 
     cairo_matrix_init_scale (&gstate->font_matrix,
 			     CAIRO_GSTATE_DEFAULT_FONT_SIZE,
@@ -153,6 +154,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
 
     gstate->font_face = cairo_font_face_reference (other->font_face);
     gstate->scaled_font = cairo_scaled_font_reference (other->scaled_font);
+    gstate->previous_scaled_font = cairo_scaled_font_reference (other->previous_scaled_font);
 
     gstate->font_matrix = other->font_matrix;
 
@@ -163,6 +165,7 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
 	_cairo_stroke_style_fini (&gstate->stroke_style);
 	cairo_font_face_destroy (gstate->font_face);
 	cairo_scaled_font_destroy (gstate->scaled_font);
+	cairo_scaled_font_destroy (gstate->previous_scaled_font);
 	return status;
     }
 
@@ -190,6 +193,9 @@ _cairo_gstate_fini (cairo_gstate_t *gstate)
     cairo_font_face_destroy (gstate->font_face);
     gstate->font_face = NULL;
 
+    cairo_scaled_font_destroy (gstate->previous_scaled_font);
+    gstate->previous_scaled_font = NULL;
+
     cairo_scaled_font_destroy (gstate->scaled_font);
     gstate->scaled_font = NULL;
 
@@ -1293,10 +1299,14 @@ _cairo_gstate_copy_clip_rectangle_list (cairo_gstate_t *gstate)
 static void
 _cairo_gstate_unset_scaled_font (cairo_gstate_t *gstate)
 {
-    if (gstate->scaled_font) {
-	cairo_scaled_font_destroy (gstate->scaled_font);
-	gstate->scaled_font = NULL;
-    }
+    if (gstate->scaled_font == NULL)
+	return;
+
+    if (gstate->previous_scaled_font != NULL)
+	cairo_scaled_font_destroy (gstate->previous_scaled_font);
+
+    gstate->previous_scaled_font = gstate->scaled_font;
+    gstate->scaled_font = NULL;
 }
 
 cairo_status_t
diff --git a/src/cairo.c b/src/cairo.c
index ddc8d4a..3e9709f 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -2910,6 +2910,7 @@ cairo_set_scaled_font (cairo_t                   *cr,
 		       const cairo_scaled_font_t *scaled_font)
 {
     cairo_status_t status;
+    cairo_bool_t was_previous;
 
     if (cr->status)
 	return;
@@ -2926,6 +2927,8 @@ cairo_set_scaled_font (cairo_t                   *cr,
     if (scaled_font == cr->gstate->scaled_font)
 	return;
 
+    was_previous = scaled_font == cr->gstate->previous_scaled_font;
+
     status = _cairo_gstate_set_font_face (cr->gstate, scaled_font->font_face);
     if (unlikely (status))
         goto BAIL;
@@ -2936,6 +2939,9 @@ cairo_set_scaled_font (cairo_t                   *cr,
 
     _cairo_gstate_set_font_options (cr->gstate, &scaled_font->options);
 
+    if (was_previous)
+	cr->gstate->scaled_font = cairo_scaled_font_reference ((cairo_scaled_font_t *) scaled_font);
+
     return;
 
 BAIL:
commit fda89c56ff484a8cd33cd780e8b9396d3538284d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 15:59:16 2009 +0100

    Markup a few more functions as const/pure

diff --git a/src/cairoint.h b/src/cairoint.h
index adc9f92..5e543d0 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -174,7 +174,7 @@ do {					\
  * in libgcc in case a target does not have one, which should be just as
  * good as the open-coded solution below, (which is "HACKMEM 169").
  */
-static inline int
+static inline int cairo_const
 _cairo_popcount (uint32_t mask)
 {
 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
@@ -203,25 +203,25 @@ _cairo_popcount (uint32_t mask)
 
 #else
 
-static inline uint16_t
+static inline uint16_t cairo_const
 cpu_to_be16(uint16_t v)
 {
     return (v << 8) | (v >> 8);
 }
 
-static inline uint16_t
+static inline uint16_t cairo_const
 be16_to_cpu(uint16_t v)
 {
     return cpu_to_be16 (v);
 }
 
-static inline uint32_t
+static inline uint32_t cairo_const
 cpu_to_be32(uint32_t v)
 {
     return (cpu_to_be16 (v) << 16) | cpu_to_be16 (v >> 16);
 }
 
-static inline uint32_t
+static inline uint32_t cairo_const
 be32_to_cpu(uint32_t v)
 {
     return cpu_to_be32 (v);
@@ -257,10 +257,12 @@ _cairo_rectangle_intersect (cairo_rectangle_int_t *dst,
 			    const cairo_rectangle_int_t *src);
 
 cairo_private cairo_bool_t
-_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line);
+_cairo_box_intersects_line_segment (cairo_box_t *box,
+	                            cairo_line_t *line) cairo_pure;
 
 cairo_private cairo_bool_t
-_cairo_box_contains_point (cairo_box_t *box, const cairo_point_t *point);
+_cairo_box_contains_point (cairo_box_t *box,
+	                   const cairo_point_t *point) cairo_pure;
 
 cairo_private void
 _cairo_composite_rectangles_init (cairo_composite_rectangles_t	*rects,
@@ -1038,7 +1040,7 @@ typedef struct _cairo_stroke_face {
 
 /* cairo.c */
 
-static inline double
+static inline double cairo_const
 _cairo_restrict_value (double value, double min, double max)
 {
     if (value < min)
@@ -1052,14 +1054,14 @@ _cairo_restrict_value (double value, double min, double max)
 /* C99 round() rounds to the nearest integral value with halfway cases rounded
  * away from 0. _cairo_round rounds halfway cases toward negative infinity.
  * This matches the rounding behaviour of _cairo_lround. */
-static inline double
+static inline double cairo_const
 _cairo_round (double r)
 {
     return floor (r + .5);
 }
 
 cairo_private int
-_cairo_lround (double d);
+_cairo_lround (double d) cairo_const;
 
 /* cairo-gstate.c */
 cairo_private cairo_status_t
@@ -1353,21 +1355,21 @@ cairo_private cairo_antialias_t
 _cairo_gstate_get_antialias (cairo_gstate_t *gstate);
 
 cairo_private cairo_bool_t
-_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_pure;
+_cairo_operator_bounded_by_mask (cairo_operator_t op) cairo_const;
 
 cairo_private cairo_bool_t
-_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_pure;
+_cairo_operator_bounded_by_source (cairo_operator_t op) cairo_const;
 
 /* cairo-color.c */
 cairo_private const cairo_color_t *
-_cairo_stock_color (cairo_stock_t stock);
+_cairo_stock_color (cairo_stock_t stock) cairo_pure;
 
 #define CAIRO_COLOR_WHITE       _cairo_stock_color (CAIRO_STOCK_WHITE)
 #define CAIRO_COLOR_BLACK       _cairo_stock_color (CAIRO_STOCK_BLACK)
 #define CAIRO_COLOR_TRANSPARENT _cairo_stock_color (CAIRO_STOCK_TRANSPARENT)
 
 cairo_private uint16_t
-_cairo_color_double_to_short (double d) cairo_pure;
+_cairo_color_double_to_short (double d) cairo_const;
 
 cairo_private void
 _cairo_color_init (cairo_color_t *color);
@@ -1401,7 +1403,7 @@ _cairo_color_get_rgba_premultiplied (cairo_color_t *color,
 
 cairo_private cairo_bool_t
 _cairo_color_equal (const cairo_color_t *color_a,
-                    const cairo_color_t *color_b);
+                    const cairo_color_t *color_b) cairo_pure;
 
 /* cairo-font-face.c */
 
@@ -2078,7 +2080,7 @@ _cairo_surface_set_device_scale (cairo_surface_t *surface,
 				 double		  sy);
 
 cairo_private cairo_bool_t
-_cairo_surface_has_device_transform (cairo_surface_t *surface);
+_cairo_surface_has_device_transform (cairo_surface_t *surface) cairo_pure;
 
 /* cairo-image-surface.c */
 
@@ -2127,13 +2129,13 @@ _cairo_surface_has_device_transform (cairo_surface_t *surface);
 				       == 0))
 
 cairo_private int
-_cairo_format_bits_per_pixel (cairo_format_t format) cairo_pure;
+_cairo_format_bits_per_pixel (cairo_format_t format) cairo_const;
 
 cairo_private cairo_format_t
-_cairo_format_from_content (cairo_content_t content) cairo_pure;
+_cairo_format_from_content (cairo_content_t content) cairo_const;
 
 cairo_private cairo_content_t
-_cairo_content_from_format (cairo_format_t format) cairo_pure;
+_cairo_content_from_format (cairo_format_t format) cairo_const;
 
 cairo_private cairo_surface_t *
 _cairo_image_surface_create_for_pixman_image (pixman_image_t		*pixman_image,
@@ -2197,10 +2199,10 @@ cairo_private cairo_image_transparency_t
 _cairo_image_analyze_transparency (cairo_image_surface_t      *image);
 
 cairo_private cairo_bool_t
-_cairo_surface_is_image (const cairo_surface_t *surface);
+_cairo_surface_is_image (const cairo_surface_t *surface) cairo_pure;
 
 cairo_private cairo_bool_t
-_cairo_surface_is_meta (const cairo_surface_t *surface);
+_cairo_surface_is_meta (const cairo_surface_t *surface) cairo_pure;
 
 /* cairo-pen.c */
 cairo_private cairo_status_t
@@ -2325,31 +2327,31 @@ _cairo_matrix_transform_bounding_box_fixed (const cairo_matrix_t *matrix,
 					    cairo_bool_t         *is_tight);
 
 cairo_private cairo_bool_t
-_cairo_matrix_is_invertible (const cairo_matrix_t *matrix);
+_cairo_matrix_is_invertible (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private double
-_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix);
+_cairo_matrix_compute_determinant (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private cairo_status_t
 _cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
 					   double *sx, double *sy, int x_major);
 
 cairo_private cairo_bool_t
-_cairo_matrix_is_identity (const cairo_matrix_t *matrix);
+_cairo_matrix_is_identity (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private cairo_bool_t
-_cairo_matrix_is_translation (const cairo_matrix_t *matrix);
+_cairo_matrix_is_translation (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private cairo_bool_t
 _cairo_matrix_is_integer_translation(const cairo_matrix_t *matrix,
 				     int *itx, int *ity);
 
 cairo_private cairo_bool_t
-_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix);
+_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
 
 cairo_private double
 _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
-					     double radius);
+					     double radius) cairo_pure;
 
 cairo_private void
 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
@@ -2437,7 +2439,8 @@ _cairo_slope_init (cairo_slope_t *slope,
 		   const cairo_point_t *b);
 
 cairo_private int
-_cairo_slope_compare (const cairo_slope_t *a, const cairo_slope_t *b);
+_cairo_slope_compare (const cairo_slope_t *a,
+	              const cairo_slope_t *b) cairo_pure;
 
 /* cairo-pattern.c */
 
commit e05097c604c607d27b341cd33426001bd2d1f690
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed May 27 16:45:20 2009 +0100

    [surface] Assign a unique id to the surface.
    
    Allocate an ever-increasing, non-zero, unique identifier to each surface.
    True for the first 4-billion...

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 654989f..cf41a87 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2424,7 +2424,10 @@ static unsigned long
 _cairo_surface_pattern_hash (unsigned long hash,
 			     const cairo_pattern_t *pattern)
 {
-    /* XXX requires cow-snapshots */
+    const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
+
+    hash ^= surface->surface->unique_id;
+
     return hash;
 }
 
@@ -2578,8 +2581,10 @@ static cairo_bool_t
 _cairo_surface_pattern_equal (const cairo_pattern_t *A,
 			      const cairo_pattern_t *B)
 {
-    /* XXX requires cow-snapshots */
-    return FALSE;
+    const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A;
+    const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B;
+
+    return a->surface->unique_id == b->surface->unique_id;
 }
 
 cairo_bool_t
diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h
index 12b38f0..b07f780 100644
--- a/src/cairo-surface-private.h
+++ b/src/cairo-surface-private.h
@@ -56,6 +56,8 @@ struct _cairo_surface {
     cairo_reference_count_t ref_count;
     cairo_status_t status;
     cairo_bool_t finished;
+    unsigned int unique_id;
+
     cairo_user_data_array_t user_data;
     cairo_user_data_array_t mime_data;
 
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index d42e1ef..d21ebf2 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -50,6 +50,7 @@ const cairo_surface_t name = {					\
     CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */		\
     status,				/* status */		\
     FALSE,				/* finished */		\
+    0,					/* unique id */		\
     { 0, 0, 0, NULL, },			/* user_data */		\
     { 0, 0, 0, NULL, },			/* mime_data */         \
     { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },   /* device_transform */	\
@@ -180,6 +181,29 @@ cairo_surface_status (cairo_surface_t *surface)
 }
 slim_hidden_def (cairo_surface_status);
 
+static unsigned long
+_cairo_surface_allocate_unique_id (void)
+{
+    static unsigned int unique_id;
+
+#if CAIRO_NO_MUTEX
+    if (++unique_id == 0)
+	unique_id = 1;
+    return unique_id;
+#else
+    unsigned int old, id;
+
+    do {
+	old = _cairo_atomic_int_get (&unique_id);
+	id = old + 1;
+	if (id == 0)
+	    id = 1;
+    } while (! _cairo_atomic_int_cmpxchg (&unique_id, old, id));
+
+    return id;
+#endif
+}
+
 void
 _cairo_surface_init (cairo_surface_t			*surface,
 		     const cairo_surface_backend_t	*backend,
@@ -194,6 +218,7 @@ _cairo_surface_init (cairo_surface_t			*surface,
     CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1);
     surface->status = CAIRO_STATUS_SUCCESS;
     surface->finished = FALSE;
+    surface->unique_id = _cairo_surface_allocate_unique_id ();
 
     _cairo_user_data_array_init (&surface->user_data);
     _cairo_user_data_array_init (&surface->mime_data);
commit 45835f623f5ddda6e3258361b9d6ab27860a9198
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 26 12:46:46 2009 +0100

    [cache] Expose init/fini methods so that caches can be embedded.
    
    The structure is already exposed, so just expose the
    constructors/destructors in order to enable caches to be embedded and
    remove a superfluous malloc.

diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h
index 25858e5..49ff69c 100644
--- a/src/cairo-cache-private.h
+++ b/src/cairo-cache-private.h
@@ -88,6 +88,20 @@ typedef struct _cairo_cache_entry {
     unsigned long size;
 } cairo_cache_entry_t;
 
+typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry);
+
+struct _cairo_cache {
+    cairo_hash_table_t *hash_table;
+
+    cairo_cache_predicate_func_t predicate;
+    cairo_destroy_func_t entry_destroy;
+
+    unsigned long max_size;
+    unsigned long size;
+
+    int freeze_count;
+};
+
 typedef cairo_bool_t
 (*cairo_cache_keys_equal_func_t) (const void *key_a, const void *key_b);
 
@@ -95,14 +109,15 @@ typedef void
 (*cairo_cache_callback_func_t) (void *entry,
 				void *closure);
 
-cairo_private cairo_cache_t *
-_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal,
-		     cairo_cache_predicate_func_t  predicate,
-		     cairo_destroy_func_t	   entry_destroy,
-		     unsigned long		   max_size);
+cairo_private cairo_status_t
+_cairo_cache_init (cairo_cache_t *cache,
+	           cairo_cache_keys_equal_func_t keys_equal,
+		   cairo_cache_predicate_func_t  predicate,
+		   cairo_destroy_func_t	   entry_destroy,
+		   unsigned long		   max_size);
 
 cairo_private void
-_cairo_cache_destroy (cairo_cache_t *cache);
+_cairo_cache_fini (cairo_cache_t *cache);
 
 cairo_private void
 _cairo_cache_freeze (cairo_cache_t *cache);
diff --git a/src/cairo-cache.c b/src/cairo-cache.c
index 7542242..025dd9f 100644
--- a/src/cairo-cache.c
+++ b/src/cairo-cache.c
@@ -48,48 +48,10 @@ _cairo_cache_entry_is_non_zero (const void *entry)
     return ((const cairo_cache_entry_t *) entry)->size;
 }
 
-static cairo_status_t
-_cairo_cache_init (cairo_cache_t		*cache,
-		   cairo_cache_keys_equal_func_t keys_equal,
-		   cairo_cache_predicate_func_t  predicate,
-		   cairo_destroy_func_t		 entry_destroy,
-		   unsigned long		 max_size)
-{
-    cache->hash_table = _cairo_hash_table_create (keys_equal);
-    if (unlikely (cache->hash_table == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-    if (predicate == NULL)
-	predicate = _cairo_cache_entry_is_non_zero;
-    cache->predicate = predicate;
-    cache->entry_destroy = entry_destroy;
-
-    cache->max_size = max_size;
-    cache->size = 0;
-
-    cache->freeze_count = 0;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static void
-_cairo_cache_pluck (void *entry, void *closure)
-{
-    _cairo_cache_remove (closure, entry);
-}
-
-static void
-_cairo_cache_fini (cairo_cache_t *cache)
-{
-    _cairo_hash_table_foreach (cache->hash_table,
-			       _cairo_cache_pluck,
-			       cache);
-    assert (cache->size == 0);
-    _cairo_hash_table_destroy (cache->hash_table);
-}
 
 /**
- * _cairo_cache_create:
+ * _cairo_cache_init:
+ * @cache: the #cairo_cache_t to initialise
  * @keys_equal: a function to return %TRUE if two keys are equal
  * @entry_destroy: destroy notifier for cache entries
  * @max_size: the maximum size for this cache
@@ -122,49 +84,53 @@ _cairo_cache_fini (cairo_cache_t *cache)
  * used to establish a window during which no automatic removal of
  * entries will occur.
  **/
-cairo_cache_t *
-_cairo_cache_create (cairo_cache_keys_equal_func_t keys_equal,
-		     cairo_cache_predicate_func_t  predicate,
-		     cairo_destroy_func_t	   entry_destroy,
-		     unsigned long		   max_size)
+cairo_status_t
+_cairo_cache_init (cairo_cache_t		*cache,
+		   cairo_cache_keys_equal_func_t keys_equal,
+		   cairo_cache_predicate_func_t  predicate,
+		   cairo_destroy_func_t		 entry_destroy,
+		   unsigned long		 max_size)
 {
-    cairo_status_t status;
-    cairo_cache_t *cache;
+    cache->hash_table = _cairo_hash_table_create (keys_equal);
+    if (unlikely (cache->hash_table == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    cache = malloc (sizeof (cairo_cache_t));
-    if (unlikely (cache == NULL)) {
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	return NULL;
-    }
+    if (predicate == NULL)
+	predicate = _cairo_cache_entry_is_non_zero;
+    cache->predicate = predicate;
+    cache->entry_destroy = entry_destroy;
 
-    status = _cairo_cache_init (cache,
-				keys_equal,
-				predicate,
-				entry_destroy,
-				max_size);
-    if (unlikely (status)) {
-	free (cache);
-	return NULL;
-    }
+    cache->max_size = max_size;
+    cache->size = 0;
 
-    return cache;
+    cache->freeze_count = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+_cairo_cache_pluck (void *entry, void *closure)
+{
+    _cairo_cache_remove (closure, entry);
 }
 
 /**
- * _cairo_cache_destroy:
+ * _cairo_cache_fini:
  * @cache: a cache to destroy
  *
  * Immediately destroys the given cache, freeing all resources
  * associated with it. As part of this process, the entry_destroy()
- * function, (as passed to _cairo_cache_create()), will be called for
+ * function, (as passed to _cairo_cache_init()), will be called for
  * each entry in the cache.
  **/
 void
-_cairo_cache_destroy (cairo_cache_t *cache)
+_cairo_cache_fini (cairo_cache_t *cache)
 {
-    _cairo_cache_fini (cache);
-
-    free (cache);
+    _cairo_hash_table_foreach (cache->hash_table,
+			       _cairo_cache_pluck,
+			       cache);
+    assert (cache->size == 0);
+    _cairo_hash_table_destroy (cache->hash_table);
 }
 
 /**
@@ -222,7 +188,7 @@ _cairo_cache_thaw (cairo_cache_t *cache)
  *
  * Performs a lookup in @cache looking for an entry which has a key
  * that matches @key, (as determined by the keys_equal() function
- * passed to _cairo_cache_create()).
+ * passed to _cairo_cache_init()).
  *
  * Return value: %TRUE if there is an entry in the cache that matches
  * @key, (which will now be in *entry_return). %FALSE otherwise, (in
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index dbe2de8..b406108 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -64,7 +64,7 @@
 
 /* XXX: This number is arbitrary---we've never done any measurement of this. */
 #define MAX_GLYPH_PAGES_CACHED 512
-static cairo_cache_t *cairo_scaled_glyph_page_cache;
+static cairo_cache_t cairo_scaled_glyph_page_cache;
 
 #define CAIRO_SCALED_GLYPH_PAGE_SIZE 32
 struct _cairo_scaled_glyph_page {
@@ -745,7 +745,7 @@ _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
 
     if (scaled_font->global_cache_frozen) {
 	CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
-	_cairo_cache_thaw (cairo_scaled_glyph_page_cache);
+	_cairo_cache_thaw (&cairo_scaled_glyph_page_cache);
 	CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
 
 	scaled_font->global_cache_frozen = FALSE;
@@ -761,7 +761,7 @@ _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
 
     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
     while (scaled_font->glyph_pages != NULL) {
-	_cairo_cache_remove (cairo_scaled_glyph_page_cache,
+	_cairo_cache_remove (&cairo_scaled_glyph_page_cache,
 			     &scaled_font->glyph_pages->cache_entry);
     }
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
@@ -1072,9 +1072,9 @@ _cairo_scaled_font_reset_static_data (void)
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_error_mutex);
 
     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
-    if (cairo_scaled_glyph_page_cache != NULL) {
-	_cairo_cache_destroy (cairo_scaled_glyph_page_cache);
-	cairo_scaled_glyph_page_cache = NULL;
+    if (cairo_scaled_glyph_page_cache.hash_table != NULL) {
+	_cairo_cache_fini (&cairo_scaled_glyph_page_cache);
+	cairo_scaled_glyph_page_cache.hash_table = NULL;
     }
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
 }
@@ -2469,24 +2469,24 @@ _cairo_scaled_font_allocate_glyph (cairo_scaled_font_t *scaled_font,
 
     CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
     if (scaled_font->global_cache_frozen == FALSE) {
-	if (unlikely (cairo_scaled_glyph_page_cache == NULL)) {
-	    cairo_scaled_glyph_page_cache =
-		_cairo_cache_create (NULL,
-				     _cairo_scaled_glyph_page_can_remove,
-				     _cairo_scaled_glyph_page_destroy,
-				     MAX_GLYPH_PAGES_CACHED);
-	    if (unlikely (cairo_scaled_glyph_page_cache == NULL)) {
+	if (unlikely (cairo_scaled_glyph_page_cache.hash_table == NULL)) {
+	    status = _cairo_cache_init (&cairo_scaled_glyph_page_cache,
+					NULL,
+					_cairo_scaled_glyph_page_can_remove,
+					_cairo_scaled_glyph_page_destroy,
+					MAX_GLYPH_PAGES_CACHED);
+	    if (unlikely (status)) {
 		CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
 		free (page);
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		return status;
 	    }
 	}
 
-	_cairo_cache_freeze (cairo_scaled_glyph_page_cache);
+	_cairo_cache_freeze (&cairo_scaled_glyph_page_cache);
 	scaled_font->global_cache_frozen = TRUE;
     }
 
-    status = _cairo_cache_insert (cairo_scaled_glyph_page_cache,
+    status = _cairo_cache_insert (&cairo_scaled_glyph_page_cache,
 				  &page->cache_entry);
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
     if (unlikely (status)) {
@@ -2516,7 +2516,8 @@ _cairo_scaled_font_free_last_glyph (cairo_scaled_font_t *scaled_font,
     _cairo_scaled_glyph_fini (scaled_font, scaled_glyph);
 
     if (--page->num_glyphs == 0) {
-	_cairo_cache_remove (cairo_scaled_glyph_page_cache, &page->cache_entry);
+	_cairo_cache_remove (&cairo_scaled_glyph_page_cache,
+		             &page->cache_entry);
 	assert (scaled_font->glyph_pages != page);
     }
 }
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index bb62351..1edd24f 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -122,20 +122,6 @@ struct _cairo_font_options {
     cairo_hint_metrics_t hint_metrics;
 };
 
-typedef cairo_bool_t (*cairo_cache_predicate_func_t) (const void *entry);
-
-struct _cairo_cache {
-    cairo_hash_table_t *hash_table;
-
-    cairo_cache_predicate_func_t predicate;
-    cairo_destroy_func_t entry_destroy;
-
-    unsigned long max_size;
-    unsigned long size;
-
-    int freeze_count;
-};
-
 /* XXX: Right now, the _cairo_color structure puts unpremultiplied
    color in the doubles and premultiplied color in the shorts. Yes,
    this is crazy insane, (but at least we don't export this
commit c3aac9cf49362b726a54a33a46bd8511a10f644f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 26 10:37:30 2009 +0100

    [image] Eliminate trapezoid array allocation
    
    By simply iterating over the array cairo_trapezoid_t, converting each one
    separately to a pixman_trapezoid_t and rasterizing each one individually
    we can avoid the common heap allocation. pixman performs exactly the same
    iteration internally so there is no efficiency loss.

diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index cf23393..4298663 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1083,12 +1083,40 @@ _cairo_image_surface_fill_rectangles (void		      *abstract_surface,
     return status;
 }
 
-static cairo_format_t
-_cairo_mask_format_from_antialias (cairo_antialias_t antialias)
+static pixman_format_code_t
+_pixman_mask_format_from_antialias (cairo_antialias_t antialias)
 {
     if (antialias == CAIRO_ANTIALIAS_NONE)
-	return CAIRO_FORMAT_A1;
-    return CAIRO_FORMAT_A8;
+	return PIXMAN_a1;
+    return PIXMAN_a8;
+}
+
+static void
+_pixman_add_trapezoids (pixman_image_t *image,
+	                int dst_x, int dst_y,
+			const cairo_trapezoid_t *traps,
+			int num_traps)
+{
+    while (num_traps--) {
+	pixman_trapezoid_t trap;
+
+	trap.top = _cairo_fixed_to_16_16 (traps->top);
+	trap.bottom = _cairo_fixed_to_16_16 (traps->bottom);
+
+	trap.left.p1.x = _cairo_fixed_to_16_16 (traps->left.p1.x);
+	trap.left.p1.y = _cairo_fixed_to_16_16 (traps->left.p1.y);
+	trap.left.p2.x = _cairo_fixed_to_16_16 (traps->left.p2.x);
+	trap.left.p2.y = _cairo_fixed_to_16_16 (traps->left.p2.y);
+
+	trap.right.p1.x = _cairo_fixed_to_16_16 (traps->right.p1.x);
+	trap.right.p1.y = _cairo_fixed_to_16_16 (traps->right.p1.y);
+	trap.right.p2.x = _cairo_fixed_to_16_16 (traps->right.p2.x);
+	trap.right.p2.y = _cairo_fixed_to_16_16 (traps->right.p2.y);
+
+	pixman_rasterize_trapezoid (image, &trap, -dst_x, -dst_y);
+
+	traps++;
+    }
 }
 
 static cairo_int_status_t
@@ -1109,10 +1137,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     cairo_image_surface_t	*dst = abstract_dst;
     cairo_image_surface_t	*src;
     cairo_int_status_t		 status;
-    cairo_image_surface_t	*mask = NULL;
-    pixman_trapezoid_t		 stack_traps[CAIRO_STACK_ARRAY_LENGTH (pixman_trapezoid_t)];
-    pixman_trapezoid_t		*pixman_traps = stack_traps;
-    int				 i;
+    pixman_image_t		*mask;
 
     if (height == 0 || width == 0)
 	return CAIRO_STATUS_SUCCESS;
@@ -1120,26 +1145,6 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     if (CAIRO_INJECT_FAULT ())
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    /* Convert traps to pixman traps */
-    if (num_traps > ARRAY_LENGTH (stack_traps)) {
-	pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t));
-	if (unlikely (pixman_traps == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    }
-
-    for (i = 0; i < num_traps; i++) {
-	pixman_traps[i].top = _cairo_fixed_to_16_16 (traps[i].top);
-	pixman_traps[i].bottom = _cairo_fixed_to_16_16 (traps[i].bottom);
-	pixman_traps[i].left.p1.x = _cairo_fixed_to_16_16 (traps[i].left.p1.x);
-	pixman_traps[i].left.p1.y = _cairo_fixed_to_16_16 (traps[i].left.p1.y);
-	pixman_traps[i].left.p2.x = _cairo_fixed_to_16_16 (traps[i].left.p2.x);
-	pixman_traps[i].left.p2.y = _cairo_fixed_to_16_16 (traps[i].left.p2.y);
-	pixman_traps[i].right.p1.x = _cairo_fixed_to_16_16 (traps[i].right.p1.x);
-	pixman_traps[i].right.p1.y = _cairo_fixed_to_16_16 (traps[i].right.p1.y);
-	pixman_traps[i].right.p2.x = _cairo_fixed_to_16_16 (traps[i].right.p2.x);
-	pixman_traps[i].right.p2.y = _cairo_fixed_to_16_16 (traps[i].right.p2.y);
-    }
-
     /* Special case adding trapezoids onto a mask surface; we want to avoid
      * creating an intermediate temporary mask unnecessarily.
      *
@@ -1159,10 +1164,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
 	! dst->has_clip &&
 	antialias != CAIRO_ANTIALIAS_NONE)
     {
-	pixman_add_trapezoids (dst->pixman_image, 0, 0,
-			       num_traps, pixman_traps);
-	status = CAIRO_STATUS_SUCCESS;
-	goto finish;
+	_pixman_add_trapezoids (dst->pixman_image, 0, 0, traps, num_traps);
+	return CAIRO_STATUS_SUCCESS;
     }
 
     status = _cairo_pattern_acquire_surface (pattern, &dst->base,
@@ -1171,7 +1174,7 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
 					     (cairo_surface_t **) &src,
 					     &attributes);
     if (unlikely (status))
-	goto finish;
+	return status;
 
     status = _cairo_image_surface_set_attributes (src, &attributes,
 						  dst_x + width / 2.,
@@ -1179,20 +1182,18 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
     if (unlikely (status))
 	goto CLEANUP_SOURCE;
 
-    mask = (cairo_image_surface_t *)
-	cairo_image_surface_create (
-	    _cairo_mask_format_from_antialias (antialias),
-	    width, height);
-    status = mask->base.status;
-    if (unlikely (status))
+    mask = pixman_image_create_bits (_pixman_mask_format_from_antialias (antialias),
+	                             width, height, NULL, 0);
+    if (unlikely (mask == NULL)) {
+	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto CLEANUP_SOURCE;
+    }
 
-    pixman_add_trapezoids (mask->pixman_image, - dst_x, - dst_y,
-			   num_traps, pixman_traps);
+    _pixman_add_trapezoids (mask, dst_x, dst_y, traps, num_traps);
 
     pixman_image_composite (_pixman_operator (op),
 			    src->pixman_image,
-			    mask->pixman_image,
+			    mask,
 			    dst->pixman_image,
 			    src_x + attributes.x_offset,
 			    src_y + attributes.y_offset,
@@ -1200,6 +1201,8 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
 			    dst_x, dst_y,
 			    width, height);
 
+    pixman_image_unref (mask);
+
     if (! _cairo_operator_bounded_by_mask (op))
 	status = _cairo_surface_composite_shape_fixup_unbounded (&dst->base,
 								 &attributes,
@@ -1208,15 +1211,10 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t	op,
 								 src_x, src_y,
 								 0, 0,
 								 dst_x, dst_y, width, height);
-    cairo_surface_destroy (&mask->base);
 
  CLEANUP_SOURCE:
     _cairo_pattern_release_surface (pattern, &src->base, &attributes);
 
- finish:
-    if (pixman_traps != stack_traps)
-	free (pixman_traps);
-
     return status;
 }
 
commit 7b2bc441387abcf3967587f571dc5741d0a53938
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 26 10:41:53 2009 +0100

    [surface-fallback] Tidy pattern handling.
    
    Make the treatment of replacing the NULL source pattern with WHITE
    consistent. As it is a solid pattern, we can skip _cairo_pattern_fini()
    and so make the code more readable, and consistent along the error paths.

diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 469e9b3..e1a87ee 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -398,9 +398,6 @@ _clip_and_composite (cairo_clip_t                  *clip,
 			       extents);
     }
 
-    if (src == &solid_pattern.base)
-	_cairo_pattern_fini (&solid_pattern.base);
-
     return status;
 }
 
@@ -421,6 +418,9 @@ _composite_trap_region (cairo_clip_t            *clip,
     unsigned int clip_serial;
     cairo_surface_t *clip_surface = clip ? clip->surface : NULL;
 
+    if (num_rects == 0)
+	return CAIRO_STATUS_SUCCESS;
+
     if (clip_surface && op == CAIRO_OPERATOR_CLEAR) {
 	_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
 				   CAIRO_CONTENT_COLOR);
@@ -428,9 +428,6 @@ _composite_trap_region (cairo_clip_t            *clip,
 	op = CAIRO_OPERATOR_DEST_OUT;
     }
 
-    if (num_rects == 0)
-	return CAIRO_STATUS_SUCCESS;
-
     if (num_rects > 1) {
       if (_cairo_surface_get_clip_mode (dst) != CAIRO_CLIP_MODE_REGION)
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -466,9 +463,6 @@ _composite_trap_region (cairo_clip_t            *clip,
     if (clip_surface)
       _cairo_pattern_fini (&mask.base);
 
-    if (src == &solid_pattern.base)
-	_cairo_pattern_fini (&solid_pattern.base);
-
     return status;
 }
 
@@ -488,26 +482,23 @@ _composite_traps_draw_func (void                          *closure,
 {
     cairo_composite_traps_info_t *info = closure;
     cairo_solid_pattern_t pattern;
-    cairo_status_t status;
 
     if (dst_x != 0 || dst_y != 0)
 	_cairo_traps_translate (info->traps, - dst_x, - dst_y);
 
-    _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE,
-			       CAIRO_CONTENT_COLOR);
-    if (!src)
+    if (src == NULL) {
+	_cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE,
+				   CAIRO_CONTENT_COLOR);
 	src = &pattern.base;
+    }
 
-    status = _cairo_surface_composite_trapezoids (op,
-						  src, dst, info->antialias,
-						  extents->x,         extents->y,
-						  extents->x - dst_x, extents->y - dst_y,
-						  extents->width,     extents->height,
-						  info->traps->traps,
-						  info->traps->num_traps);
-    _cairo_pattern_fini (&pattern.base);
-
-    return status;
+    return _cairo_surface_composite_trapezoids (op,
+						src, dst, info->antialias,
+						extents->x,         extents->y,
+						extents->x - dst_x, extents->y - dst_y,
+						extents->width,     extents->height,
+						info->traps->traps,
+						info->traps->num_traps);
 }
 
 /* Warning: This call modifies the coordinates of traps */
@@ -684,8 +675,7 @@ _composite_spans_fill_func (void                          *closure,
 {
     cairo_composite_rectangles_t rects;
     cairo_composite_spans_fill_info_t *info = closure;
-    cairo_pattern_union_t pattern;
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
+    cairo_solid_pattern_t pattern;
 
     _cairo_composite_rectangles_init (
 	&rects, extents->x, extents->y,
@@ -699,18 +689,16 @@ _composite_spans_fill_func (void                          *closure,
 
     /* We're called without a source pattern from
      * _create_composite_mask_pattern(). */
-    _cairo_pattern_init_solid (&pattern.solid, CAIRO_COLOR_WHITE,
-			       CAIRO_CONTENT_COLOR);
-    if (src == NULL)
+    if (src == NULL) {
+	_cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE,
+				   CAIRO_CONTENT_COLOR);
 	src = &pattern.base;
+    }
 
-    status = _cairo_path_fixed_fill_using_spans (
+    return _cairo_path_fixed_fill_using_spans (
 	op, src, info->path, dst,
 	info->fill_rule, info->tolerance, info->antialias,
 	&rects);
-
-    _cairo_pattern_fini (&pattern.base);
-    return status;
 }
 
 cairo_status_t
@@ -1027,10 +1015,11 @@ _cairo_surface_old_show_glyphs_draw_func (void                          *closure
 	}
     }
 
-    _cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE,
-			       CAIRO_CONTENT_COLOR);
-    if (!src)
+    if (src == NULL) {
+	_cairo_pattern_init_solid (&pattern, CAIRO_COLOR_WHITE,
+				   CAIRO_CONTENT_COLOR);
 	src = &pattern.base;
+    }
 
     status = _cairo_surface_old_show_glyphs (glyph_info->font, op, src,
 					     dst,
@@ -1041,24 +1030,18 @@ _cairo_surface_old_show_glyphs_draw_func (void                          *closure
 					     extents->height,
 					     glyph_info->glyphs,
 					     glyph_info->num_glyphs);
-
     if (status != CAIRO_INT_STATUS_UNSUPPORTED)
 	return status;
 
-    status = _cairo_scaled_font_show_glyphs (glyph_info->font,
-					     op,
-					     src, dst,
-					     extents->x,         extents->y,
-					     extents->x - dst_x,
-					     extents->y - dst_y,
-					     extents->width,     extents->height,
-					     glyph_info->glyphs,
-					     glyph_info->num_glyphs);
-
-    if (src == &pattern.base)
-	_cairo_pattern_fini (&pattern.base);
-
-    return status;
+    return _cairo_scaled_font_show_glyphs (glyph_info->font,
+					   op,
+					   src, dst,
+					   extents->x,         extents->y,
+					   extents->x - dst_x,
+					   extents->y - dst_y,
+					   extents->width,     extents->height,
+					   glyph_info->glyphs,
+					   glyph_info->num_glyphs);
 }
 
 cairo_status_t
commit e83e113eae9e7cb3e09719bfc0ad68450faf3ecd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon May 25 23:25:38 2009 +0100

    [surface] Speed up cairo_surface_get_mime_data().
    
    The number of mime-types attached to a surface is usually small,
    typically zero. Therefore it is quicker to do a strcmp() against
    each key in the private mime-data array than it is to intern the
    string (i.e. compute a hash, search the hash table, and do a final
    strcmp).

diff --git a/src/cairo-array.c b/src/cairo-array.c
index 77e575f..fcd1246 100644
--- a/src/cairo-array.c
+++ b/src/cairo-array.c
@@ -352,14 +352,6 @@ _cairo_array_size (cairo_array_t *array)
     return array->size;
 }
 
-/* #cairo_user_data_array_t */
-
-typedef struct {
-    const cairo_user_data_key_t *key;
-    void *user_data;
-    cairo_destroy_func_t destroy;
-} cairo_user_data_slot_t;
-
 /**
  * _cairo_user_data_array_init:
  * @array: a #cairo_user_data_array_t
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index fc994b0..d42e1ef 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -611,27 +611,30 @@ cairo_surface_get_mime_data (cairo_surface_t		*surface,
                              const unsigned char       **data,
                              unsigned int		*length)
 {
-    cairo_status_t status;
-    cairo_mime_data_t *mime_data;
+    cairo_user_data_slot_t *slots;
+    int i, num_slots;
 
     *data = NULL;
     *length = 0;
-    if (surface->status)
+    if (unlikely (surface->status))
 	return;
 
-    status = _cairo_intern_string (&mime_type, -1);
-    if (unlikely (status)) {
-	status = _cairo_surface_set_error (surface, status);
-	return;
+    /* The number of mime-types attached to a surface is usually small,
+     * typically zero. Therefore it is quicker to do a strcmp() against
+     * each key than it is to intern the string (i.e. compute a hash,
+     * search the hash table, and do a final strcmp).
+     */
+    num_slots = surface->mime_data.num_elements;
+    slots = _cairo_array_index (&surface->mime_data, 0);
+    for (i = 0; i < num_slots; i++) {
+	if (strcmp ((char *) slots[i].key, mime_type) == 0) {
+	    cairo_mime_data_t *mime_data = slots[i].user_data;
+
+	    *data = mime_data->data;
+	    *length = mime_data->length;
+	    return;
+	}
     }
-
-    mime_data = _cairo_user_data_array_get_data (&surface->mime_data,
-						 (cairo_user_data_key_t *) mime_type);
-    if (mime_data == NULL)
-	return;
-
-    *data = mime_data->data;
-    *length = mime_data->length;
 }
 slim_hidden_def (cairo_surface_get_mime_data);
 
diff --git a/src/cairoint.h b/src/cairoint.h
index d694ae4..adc9f92 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -312,6 +312,12 @@ _cairo_array_num_elements (cairo_array_t *array);
 cairo_private int
 _cairo_array_size (cairo_array_t *array);
 
+typedef struct {
+    const cairo_user_data_key_t *key;
+    void *user_data;
+    cairo_destroy_func_t destroy;
+} cairo_user_data_slot_t;
+
 cairo_private void
 _cairo_user_data_array_init (cairo_user_data_array_t *array);
 
commit 247e76b923d54f639e2ca50c8da945c15b691158
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 26 08:53:45 2009 +0100

    [script] Speed up floating-point scanner
    
    Hard-code frequent exponents to reduce number of calls to pow().

diff --git a/util/cairo-script/cairo-script-scanner.c b/util/cairo-script/cairo-script-scanner.c
index 22d1721..538aad4 100644
--- a/util/cairo-script/cairo-script-scanner.c
+++ b/util/cairo-script/cairo-script-scanner.c
@@ -214,32 +214,32 @@ parse_number (csi_object_t *obj, const char *s, int len)
     while (++s < end) {
 	if (*s < '0') {
 	    if (*s == '.') {
-		if (radix)
+		if (_csi_unlikely (radix))
 		    return FALSE;
-		if (decimal != -1)
+		if (_csi_unlikely (decimal != -1))
 		    return FALSE;
-		if (exponent_sign)
+		if (_csi_unlikely (exponent_sign))
 		    return FALSE;
 
 		decimal = 0;
 	    } else if (*s == '!') {
-		if (radix)
+		if (_csi_unlikely (radix))
 		    return FALSE;
-		if (decimal != -1)
+		if (_csi_unlikely (decimal != -1))
 		    return FALSE;
-		if (exponent_sign)
+		if (_csi_unlikely (exponent_sign))
 		    return FALSE;
 
 		radix = mantissa;
 		mantissa = 0;
 
-		if (radix < 2 || radix > 36)
+		if (_csi_unlikely (radix < 2 || radix > 36))
 		    return FALSE;
 	    } else
 		return FALSE;
 	} else if (*s <= '9') {
 	    int v = *s - '0';
-	    if (radix && v >= radix)
+	    if (_csi_unlikely (radix && v >= radix))
 		return FALSE;
 
 	    if (exponent_sign) {
@@ -254,7 +254,7 @@ parse_number (csi_object_t *obj, const char *s, int len)
 	    }
 	} else if (*s == 'E' || * s== 'e') {
 	    if (radix == 0) {
-		if (s + 1 == end)
+		if (_csi_unlikely (s + 1 == end))
 		    return FALSE;
 
 		exponent_sign = 1;
@@ -266,7 +266,7 @@ parse_number (csi_object_t *obj, const char *s, int len)
 	    } else {
 		int v = 0xe;
 
-		if (v >= radix)
+		if (_csi_unlikely (v >= radix))
 		    return FALSE;
 
 		mantissa = radix * mantissa + v;
@@ -276,7 +276,7 @@ parse_number (csi_object_t *obj, const char *s, int len)
 	} else if (*s <= 'Z') {
 	    int v = *s - 'A' + 0xA;
 
-	    if (v >= radix)
+	    if (_csi_unlikely (v >= radix))
 		return FALSE;
 
 	    mantissa = radix * mantissa + v;
@@ -285,7 +285,7 @@ parse_number (csi_object_t *obj, const char *s, int len)
 	} else if (*s <= 'z') {
 	    int v = *s - 'a' + 0xa;
 
-	    if (v >= radix)
+	    if (_csi_unlikely (v >= radix))
 		return FALSE;
 
 	    mantissa = radix * mantissa + v;
@@ -306,8 +306,25 @@ parse_number (csi_object_t *obj, const char *s, int len)
 	    e = exponent * exponent_sign;
 	    if (decimal != -1)
 		e -= decimal;
-	    if (e != 0)
-		v *= pow (10, e); /* XXX */
+	    switch (e) {
+	    case -7: v *= 0.0000001; break;
+	    case -6: v *= 0.000001; break;
+	    case -5: v *= 0.00001; break;
+	    case -4: v *= 0.0001; break;
+	    case -3: v *= 0.001; break;
+	    case -2: v *= 0.01; break;
+	    case -1: v *= 0.1; break;
+	    case  0: break;
+	    case  1: v *= 10; break;
+	    case  2: v *= 100; break;
+	    case  3: v *= 1000; break;
+	    case  4: v *= 10000; break;
+	    case  5: v *= 100000; break;
+	    case  6: v *= 1000000; break;
+	    default:
+		    v *= pow (10, e); /* XXX */
+		    break;
+	    }
 
 	    obj->type = CSI_OBJECT_TYPE_REAL;
 	    obj->datum.real = sign * v;
commit c5e85835b29c7654e1c28a90b2e587afba7b6f0f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 26 15:52:30 2009 +0100

    [debug] Relax ASSERT_NOT_REACHED
    
    Need to actually handle random pixman image formats...
    For the time being, ignore them.

diff --git a/src/cairo-debug.c b/src/cairo-debug.c
index d463be2..2270249 100644
--- a/src/cairo-debug.c
+++ b/src/cairo-debug.c
@@ -107,7 +107,7 @@ _cairo_debug_check_image_surface_is_defined (const cairo_surface_t *surface)
 	width = image->width*4;
 	break;
     default:
-	ASSERT_NOT_REACHED;
+	/* XXX compute width from pixman bpp */
 	return;
     }
 


More information about the cairo-commit mailing list