[cairo-commit] 30 commits - boilerplate/cairo-boilerplate.c perf/cairo-perf-cover.c perf/rectangles.c src/cairo-analysis-surface.c src/cairo-bentley-ottmann.c src/cairo-cache.c src/cairo-cache-private.h src/cairo-cff-subset.c src/cairo-clip.c src/cairo-ft-font.c src/cairo-glitz-surface.c src/cairo-gstate.c src/cairo.h src/cairo-hash.c src/cairo-hash-private.h src/cairoint.h src/cairo-matrix.c src/cairo-misc.c src/cairo-mutex-impl-private.h src/cairo-mutex-list-private.h src/cairo-os2-surface.c src/cairo-path-bounds.c src/cairo-path-fixed.c src/cairo-pdf-operators.c src/cairo-png.c src/cairo-ps-surface.c src/cairo-ps-surface-private.h src/cairo-quartz-image-surface.c src/cairo-quartz-surface.c src/cairo-scaled-font.c src/cairo-scaled-font-private.h src/cairo-scaled-font-subsets-private.h src/cairo-spans.c src/cairo-spline.c src/cairo-surface.c src/cairo-surface-fallback.c src/cairo-toy-font-face.c src/cairo-truetype-subset.c src/cairo-xlib-surface.c test/cairo-test.c test/create-from-png-stream.c test/mime-data.c util/cairo-trace

Chris Wilson ickle at kemper.freedesktop.org
Thu Jan 29 02:26:41 PST 2009


 boilerplate/cairo-boilerplate.c         |    5 
 perf/cairo-perf-cover.c                 |    7 
 perf/rectangles.c                       |   20 +
 src/cairo-analysis-surface.c            |    2 
 src/cairo-bentley-ottmann.c             |    4 
 src/cairo-cache-private.h               |    8 
 src/cairo-cache.c                       |   36 ++-
 src/cairo-cff-subset.c                  |    6 
 src/cairo-clip.c                        |    4 
 src/cairo-ft-font.c                     |  111 ++++++----
 src/cairo-glitz-surface.c               |    2 
 src/cairo-gstate.c                      |   15 +
 src/cairo-hash-private.h                |    4 
 src/cairo-hash.c                        |  117 ++++++++---
 src/cairo-matrix.c                      |    2 
 src/cairo-misc.c                        |    4 
 src/cairo-mutex-impl-private.h          |    4 
 src/cairo-mutex-list-private.h          |    1 
 src/cairo-os2-surface.c                 |    9 
 src/cairo-path-bounds.c                 |   25 ++
 src/cairo-path-fixed.c                  |    2 
 src/cairo-pdf-operators.c               |    2 
 src/cairo-png.c                         |    2 
 src/cairo-ps-surface-private.h          |    3 
 src/cairo-ps-surface.c                  |   18 +
 src/cairo-quartz-image-surface.c        |    5 
 src/cairo-quartz-surface.c              |    4 
 src/cairo-scaled-font-private.h         |    6 
 src/cairo-scaled-font-subsets-private.h |    9 
 src/cairo-scaled-font.c                 |  336 +++++++++++++++++++++++---------
 src/cairo-spans.c                       |    2 
 src/cairo-spline.c                      |    8 
 src/cairo-surface-fallback.c            |    4 
 src/cairo-surface.c                     |    9 
 src/cairo-toy-font-face.c               |   49 ++--
 src/cairo-truetype-subset.c             |    3 
 src/cairo-xlib-surface.c                |    7 
 src/cairo.h                             |    6 
 src/cairoint.h                          |   15 -
 test/cairo-test.c                       |   11 -
 test/create-from-png-stream.c           |   36 ++-
 test/mime-data.c                        |    4 
 util/cairo-trace/trace.c                |   12 +
 43 files changed, 664 insertions(+), 275 deletions(-)

New commits:
commit 7f95288c03a400bf770165d427ef623d924b3b47
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 29 09:47:01 2009 +0000

    [ft] Improve error status propagation.
    
    Propagate the error status from deep within the bowels, in order to reduce
    the number of duplicate _cairo_error() and generally clean up the return
    values.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 19ba074..4295d25 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -183,7 +183,7 @@ _font_map_release_face_lock_held (cairo_ft_unscaled_font_map_t *font_map,
     }
 }
 
-static void
+static cairo_status_t
 _cairo_ft_unscaled_font_map_create (void)
 {
     cairo_ft_unscaled_font_map_t *font_map;
@@ -194,10 +194,8 @@ _cairo_ft_unscaled_font_map_create (void)
     assert (cairo_ft_unscaled_font_map == NULL);
 
     font_map = malloc (sizeof (cairo_ft_unscaled_font_map_t));
-    if (unlikely (font_map == NULL)) {
-	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-	goto FAIL;
-    }
+    if (unlikely (font_map == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     font_map->hash_table =
 	_cairo_hash_table_create (_cairo_ft_unscaled_font_keys_equal);
@@ -205,21 +203,20 @@ _cairo_ft_unscaled_font_map_create (void)
     if (unlikely (font_map->hash_table == NULL))
 	goto FAIL;
 
-    if (FT_Init_FreeType (&font_map->ft_library))
+    if (unlikely (FT_Init_FreeType (&font_map->ft_library)))
 	goto FAIL;
 
     font_map->num_open_faces = 0;
 
     cairo_ft_unscaled_font_map = font_map;
-    return;
+    return CAIRO_STATUS_SUCCESS;
 
 FAIL:
-    if (font_map) {
-	if (font_map->hash_table)
-	    _cairo_hash_table_destroy (font_map->hash_table);
-	free (font_map);
-    }
-    cairo_ft_unscaled_font_map = NULL;
+    if (font_map->hash_table)
+	_cairo_hash_table_destroy (font_map->hash_table);
+    free (font_map);
+
+    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 }
 
 
@@ -266,13 +263,9 @@ _cairo_ft_unscaled_font_map_lock (void)
 {
     CAIRO_MUTEX_LOCK (_cairo_ft_unscaled_font_map_mutex);
 
-    if (cairo_ft_unscaled_font_map == NULL)
-    {
-	_cairo_ft_unscaled_font_map_create ();
-
-	if (unlikely (cairo_ft_unscaled_font_map == NULL)) {
+    if (unlikely (cairo_ft_unscaled_font_map == NULL)) {
+	if (unlikely (_cairo_ft_unscaled_font_map_create ())) {
 	    CAIRO_MUTEX_UNLOCK (_cairo_ft_unscaled_font_map_mutex);
-	    _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	    return NULL;
 	}
     }
@@ -2132,8 +2125,9 @@ static const cairo_scaled_font_backend_t _cairo_ft_scaled_font_backend = {
 
 /* #cairo_ft_font_face_t */
 
-static cairo_font_face_t *
-_cairo_ft_font_face_create_for_pattern (FcPattern *pattern);
+static cairo_status_t
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
+					cairo_font_face_t **out);
 
 static cairo_status_t
 _cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
@@ -2190,7 +2184,7 @@ _cairo_ft_font_face_create_for_toy (cairo_toy_font_face_t   *toy_face,
 	goto FREE_PATTERN;
     }
 
-    *font_face = _cairo_ft_font_face_create_for_pattern (pattern);
+    status = _cairo_ft_font_face_create_for_pattern (pattern, font_face);
 
  FREE_PATTERN:
     FcPatternDestroy (pattern);
@@ -2320,30 +2314,29 @@ const cairo_font_face_backend_t _cairo_ft_font_face_backend = {
     _cairo_ft_font_face_scaled_font_create
 };
 
-static cairo_font_face_t *
-_cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
+static cairo_status_t
+_cairo_ft_font_face_create_for_pattern (FcPattern *pattern,
+					cairo_font_face_t **out)
 {
     cairo_ft_font_face_t *font_face;
 
     font_face = malloc (sizeof (cairo_ft_font_face_t));
-    if (unlikely (!font_face)) {
-	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-	return (cairo_font_face_t *)&_cairo_font_face_nil;
-    }
+    if (unlikely (font_face == NULL))
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     font_face->unscaled = NULL;
     font_face->next = NULL;
 
     font_face->pattern = FcPatternDuplicate (pattern);
-    if (unlikely (!pattern)) {
+    if (unlikely (pattern == NULL)) {
 	free (font_face);
-	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-	return (cairo_font_face_t *)&_cairo_font_face_nil;
+	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
     _cairo_font_face_init (&font_face->base, &_cairo_ft_font_face_backend);
 
-    return &font_face->base;
+    *out = &font_face->base;
+    return CAIRO_STATUS_SUCCESS;
 }
 
 static cairo_font_face_t *
@@ -2623,9 +2616,15 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern)
 
     unscaled = _cairo_ft_unscaled_font_create_for_pattern (pattern);
     if (unlikely (unscaled == NULL)) {
+	cairo_status_t status;
 	/* Store the pattern.  We will resolve it and create unscaled
 	 * font when creating scaled fonts */
-	return _cairo_ft_font_face_create_for_pattern (pattern);
+	status = _cairo_ft_font_face_create_for_pattern (pattern,
+							 &font_face);
+	if (unlikely (status))
+	    return (cairo_font_face_t *) &_cairo_font_face_nil;
+
+	return font_face;
     }
 
     _get_pattern_ft_options (pattern, &ft_options);
commit 53bd2ae2ce27f9b954f34bc9921d798c9a074125
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 21:36:27 2009 +0000

    [ft] Distinguish fatal backend errors whilst constructing scaled fonts.
    
    We now have the ability to distinguish an error case where the backend is
    left in an inconsistent state from a transitory error. For the former we
    need to report the error condition via the return value, which will be
    propagated to the font-face. For the latter we just construct an in-error
    scaled font nil-object which is passed back to the user.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 39cf164..19ba074 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -1523,7 +1523,7 @@ _cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t	 *unscaled,
     cairo_status_t status;
 
     face = _cairo_ft_unscaled_font_lock_face (unscaled);
-    if (!face)
+    if (unlikely (face == NULL)) /* backend error */
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     scaled_font = malloc (sizeof(cairo_ft_scaled_font_t));
@@ -1542,18 +1542,18 @@ _cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t	 *unscaled,
 			              font_face,
 				      font_matrix, ctm, options,
 				      &_cairo_ft_scaled_font_backend);
-    if (unlikely (status)) {
-	_cairo_unscaled_font_destroy (&unscaled->base);
-	free (scaled_font);
-	goto FAIL;
-    }
+    if (unlikely (status))
+	goto CLEANUP_SCALED_FONT;
 
     status = _cairo_ft_unscaled_font_set_scale (unscaled,
 				                &scaled_font->base.scale);
     if (unlikely (status)) {
+	/* This can only fail if we encounter an error with the underlying
+	 * font, so propagate the error back to the font-face. */
+	_cairo_ft_unscaled_font_unlock_face (unscaled);
 	_cairo_unscaled_font_destroy (&unscaled->base);
 	free (scaled_font);
-	goto FAIL;
+	return status;
     }
 
 
@@ -1607,13 +1607,21 @@ _cairo_ft_scaled_font_create (cairo_ft_unscaled_font_t	 *unscaled,
     }
 
     status = _cairo_scaled_font_set_metrics (&scaled_font->base, &fs_metrics);
+    if (unlikely (status))
+	goto CLEANUP_SCALED_FONT;
+
+    _cairo_ft_unscaled_font_unlock_face (unscaled);
 
     *font_out = &scaled_font->base;
+    return CAIRO_STATUS_SUCCESS;
 
- FAIL:
+  CLEANUP_SCALED_FONT:
+    _cairo_unscaled_font_destroy (&unscaled->base);
+    free (scaled_font);
+  FAIL:
     _cairo_ft_unscaled_font_unlock_face (unscaled);
-
-    return status;
+    *font_out = _cairo_scaled_font_create_in_error (status);
+    return CAIRO_STATUS_SUCCESS; /* non-backend error */
 }
 
 cairo_bool_t
@@ -2284,8 +2292,14 @@ _cairo_ft_font_face_scaled_font_create (void                     *abstract_face,
 					    options,
 					    &unscaled,
 					    &ft_options);
-	if (unlikely (status))
-		return status;
+	if (unlikely (status)) {
+	    /* XXX It is possible for a failure to generate the unscaled font
+	     * here could indicate that the font_face itself is broken - for
+	     * which we should propagate the error.
+	     */
+	    *scaled_font = _cairo_scaled_font_create_in_error (status);
+	    return CAIRO_STATUS_SUCCESS;
+	}
 
     } else {
 	unscaled = font_face->unscaled;
commit f17aeedab31753974cce027f92571107425b1bcd
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 21:11:41 2009 +0000

    [scaled-font] Differentiate fatal error when creating fonts
    
    We only want to set the error state on the backend when it implies that
    the font-face is in an inconsistent state. For example, this may be due to
    a locking error in the backend or that we have detected a corrupt font.
    
    In contrast, if we merely fail to allocated the scaled font then we just
    wish to return that error to the user, without making the font-face itself
    inert.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 864219a..7096a01 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1039,15 +1039,21 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     /* Otherwise create it and insert it into the hash table. */
     status = font_face->backend->scaled_font_create (font_face, font_matrix,
 						     ctm, options, &scaled_font);
+    /* Did we leave the backend in an error state? */
     if (unlikely (status)) {
 	_cairo_scaled_font_map_unlock ();
 	status = _cairo_font_face_set_error (font_face, status);
 	return _cairo_scaled_font_create_in_error (status);
     }
+    /* Or did we encounter an error whilst constructing the scaled font? */
+    if (unlikely (scaled_font->status)) {
+	_cairo_scaled_font_map_unlock ();
+	return scaled_font;
+    }
 
     status = _cairo_hash_table_insert (font_map->hash_table,
 				       &scaled_font->hash_entry);
-    if (status == CAIRO_STATUS_SUCCESS) {
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
 	old = font_map->mru_scaled_font;
 	font_map->mru_scaled_font = scaled_font;
 	_cairo_reference_count_inc (&scaled_font->ref_count);
commit 58cab06c4c2c49bbecb11efaae6b41d30c06eff0
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 20:52:29 2009 +0000

    [scaled-font] Guard against invalid matrices when creating the scaled font.
    
    Check the user input for validity before passing the values on to the
    backend. Currently the error is detected by the backend and the error is
    propagated onto the font-face.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 727ab2e..864219a 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -38,9 +38,17 @@
  *      Chris Wilson <chris at chris-wilson.co.uk>
  */
 
+#define _GNU_SOURCE
+
 #include "cairoint.h"
 #include "cairo-scaled-font-private.h"
 
+#if _XOPEN_SOURCE >= 600 || defined (_ISOC99_SOURCE)
+#define ISFINITE(x) isfinite (x)
+#else
+#define ISFINITE(x) ((x) * (x) >= 0.) /* check for NaNs */
+#endif
+
 #define CAIRO_SCALED_GLYPH_PAGE_SHIFT 7
 #define CAIRO_SCALED_GLYPH_PAGE_SIZE (1 << CAIRO_SCALED_GLYPH_PAGE_SHIFT)
 #define CAIRO_SCALED_GLYPH_PAGE_INDEX(I) \
@@ -909,9 +917,19 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     cairo_status_t status;
     cairo_scaled_font_map_t *font_map;
     cairo_scaled_font_t key, *old = NULL, *scaled_font = NULL;
+    double det;
+
+    status = font_face->status;
+    if (unlikely (status))
+	return _cairo_scaled_font_create_in_error (status);
+
+    det = _cairo_matrix_compute_determinant (font_matrix);
+    if (! ISFINITE (det))
+	return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
 
-    if (font_face->status)
-	return _cairo_scaled_font_create_in_error (font_face->status);
+    det = _cairo_matrix_compute_determinant (ctm);
+    if (! ISFINITE (det))
+	return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_MATRIX));
 
     status = cairo_font_options_status ((cairo_font_options_t *) options);
     if (unlikely (status))
commit 97c88f2af04b6bc5161fa2b567b5e922d7fd326a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 20:51:02 2009 +0000

    [surface] Fix memleak of along set_mime_data() error path
    
    Free the mime_data holder if we fail to attach it to the surface.

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index d68e135..3b1dfe2 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -675,7 +675,7 @@ cairo_surface_set_mime_data (cairo_surface_t		*surface,
     cairo_status_t status;
     cairo_mime_data_t *mime_data;
 
-    if (surface->status)
+    if (unlikely (surface->status))
 	return surface->status;
 
     status = _cairo_intern_string (&mime_type, -1);
@@ -700,8 +700,12 @@ cairo_surface_set_mime_data (cairo_surface_t		*surface,
 					      (cairo_user_data_key_t *) mime_type,
 					      mime_data,
 					      _cairo_mime_data_destroy);
-    if (unlikely (status))
+    if (unlikely (status)) {
+	if (mime_data != NULL)
+	    free (mime_data);
+
 	return _cairo_surface_set_error (surface, status);
+    }
 
     return CAIRO_STATUS_SUCCESS;
 }
commit d20e5fc2d95c61ab04e085bf3a99d2cb958421a5
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 15:08:58 2009 +0000

    [ps] Free images after emitting patterns.
    
    Ensure that the temporary images are freed after we finish with the
    pattern.
    
    Note that we are using 3 members of the surface for temporary storage
    whilst emitting patterns, this should be reviewed.

diff --git a/src/cairo-ps-surface-private.h b/src/cairo-ps-surface-private.h
index 98d2750..8567aff 100644
--- a/src/cairo-ps-surface-private.h
+++ b/src/cairo-ps-surface-private.h
@@ -67,9 +67,12 @@ typedef struct cairo_ps_surface {
     double height;
     int bbox_x1, bbox_y1, bbox_x2, bbox_y2;
     cairo_matrix_t cairo_to_ps;
+
+    /* XXX These 3 are used as temporary storage whilst emitting patterns */
     cairo_image_surface_t *image;
     cairo_image_surface_t *acquired_image;
     void *image_extra;
+
     cairo_bool_t use_string_datasource;
 
     cairo_bool_t current_pattern_is_solid_color;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 8a039f6..a2dd68b 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -2333,6 +2333,9 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t      *surface,
     int x = 0;
     int y = 0;
 
+    surface->acquired_image = NULL;
+    surface->image = NULL;
+
     if (_cairo_surface_is_meta (pattern->surface)) {
 	cairo_surface_t *meta_surface = pattern->surface;
 	cairo_rectangle_int_t pattern_extents;
@@ -2384,8 +2387,12 @@ _cairo_ps_surface_acquire_surface (cairo_ps_surface_t      *surface,
 					       rect.width,
 					       rect.height);
 	    _cairo_pattern_fini (&pad_pattern.base);
-	    if (unlikely (status))
+	    if (unlikely (status)) {
+		if (pad_image != &surface->acquired_image->base)
+		    cairo_surface_destroy (pad_image);
+
 		goto BAIL;
+	    }
 	}
 
 	surface->image = (cairo_image_surface_t *) pad_image;
@@ -2436,10 +2443,17 @@ static void
 _cairo_ps_surface_release_surface (cairo_ps_surface_t      *surface,
 				   cairo_surface_pattern_t *pattern)
 {
-    if (!_cairo_surface_is_meta (pattern->surface))
+    if (surface->image != &surface->acquired_image->base)
+	cairo_surface_destroy (surface->image);
+
+    if (! _cairo_surface_is_meta (pattern->surface)) {
 	_cairo_surface_release_source_image (pattern->surface,
 					     surface->acquired_image,
 					     surface->image_extra);
+    }
+
+    surface->acquired_image = NULL;
+    surface->image = NULL;
 }
 
 static cairo_status_t
commit e6102dbe028ca93db936b2f4cd6368e2ba0a2209
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 14:00:13 2009 +0000

    [png] Avoid a double free of the memory stream after error.
    
    _cairo_memory_stream_destroy() finalizes the stream even if the stream was
    in error and that error is reported back to the caller - so ensure we
    don't try to free the stream again.

diff --git a/src/cairo-png.c b/src/cairo-png.c
index fd16c4d..58e38cb 100644
--- a/src/cairo-png.c
+++ b/src/cairo-png.c
@@ -655,12 +655,12 @@ read_png (struct png_read_closure_t *png_closure)
     status = _cairo_memory_stream_destroy (png_closure->png_data,
 					   &mime_data,
 					   &mime_data_length);
+    png_closure->png_data = NULL;
     if (unlikely (status)) {
 	cairo_surface_destroy (surface);
 	surface = _cairo_surface_create_in_error (status);
 	goto BAIL;
     }
-    png_closure->png_data = NULL;
 
     status = cairo_surface_set_mime_data (surface,
 					  CAIRO_MIME_TYPE_PNG,
commit 0f3e366f8bbbaa80b518eb1b0297a6122901ce66
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 26 10:54:45 2009 +0000

    [font-face] Close a race when resurrecting fonts.
    
    Paul Messmer provided a thorough analysis of a race between destroying the
    final reference on a font and a concurrent recreation of the font -
    demonstrating how it is possible for the create() to return the font that
    was in the process of being freed.
    
    To stop the race, we need to recheck the reference count upon taking the
    mutex guarding the hash table.

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 383b6e0..39cf164 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -513,6 +513,12 @@ _cairo_ft_unscaled_font_destroy (void *abstract_font)
     /* All created objects must have been mapped in the font map. */
     assert (font_map != NULL);
 
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled->base.ref_count)) {
+	/* somebody recreated the font whilst we waited for the lock */
+	_cairo_ft_unscaled_font_map_unlock ();
+	return;
+    }
+
     _cairo_hash_table_remove (font_map->hash_table,
 			      &unscaled->base.hash_entry);
 
diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c
index 5a60b14..05ad495 100644
--- a/src/cairo-toy-font-face.c
+++ b/src/cairo-toy-font-face.c
@@ -358,6 +358,12 @@ _cairo_toy_font_face_destroy (void *abstract_face)
     /* All created objects must have been mapped in the hash table. */
     assert (hash_table != NULL);
 
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->base.ref_count)) {
+	/* somebody recreated the font whilst we waited for the lock */
+	_cairo_toy_font_face_hash_table_unlock ();
+	return;
+    }
+
     if (font_face->base.hash_entry.hash != 0)
 	_cairo_hash_table_remove (hash_table, &font_face->base.hash_entry);
 
commit 312b5680a5754c8e7ee1332206b81449cf9bf8a3
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 21:13:15 2009 +0000

    [cff-subset] Free ps_name.
    
    Ensure ps_name is freed along error paths and by the normal destructor.

diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
index 4697a18..1f4cb92 100644
--- a/src/cairo-cff-subset.c
+++ b/src/cairo-cff-subset.c
@@ -1869,6 +1869,7 @@ cairo_cff_font_destroy (cairo_cff_font_t *font)
     free (font->widths);
     if (font->font_name)
 	free (font->font_name);
+    free (font->ps_name);
     free (font->subset_font_name);
     _cairo_array_fini (&font->output);
     cff_dict_fini (font->top_dict);
@@ -2032,7 +2033,7 @@ _cairo_cff_font_fallback_create (cairo_scaled_font_subset_t  *scaled_font_subset
     }
 
     font->ps_name = strdup (subset_name);
-    if (unlikely (font->subset_font_name == NULL)) {
+    if (unlikely (font->ps_name == NULL)) {
         status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
 	goto fail2;
     }
@@ -2088,6 +2089,7 @@ fail4:
 fail3:
     if (font->font_name)
 	free (font->font_name);
+    free (font->ps_name);
 fail2:
     free (font->subset_font_name);
 fail1:
commit 5176507fcb61ae1ec1143aa0b6b098bc92575c48
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 13:37:54 2009 +0000

    [truetype] Free font name.
    
    Remember to free the font name on destruction.

diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
index 2633e87..d01b089 100644
--- a/src/cairo-truetype-subset.c
+++ b/src/cairo-truetype-subset.c
@@ -1135,6 +1135,8 @@ void
 _cairo_truetype_subset_fini (cairo_truetype_subset_t *subset)
 {
     free (subset->ps_name);
+    if (subset->font_name)
+	free (subset->font_name);
     free (subset->widths);
     free (subset->data);
     free (subset->string_offsets);
commit ab0ac1b8a84b0d259602f0029a3b5552466f35a6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 29 09:45:11 2009 +0000

    [truetype] Initialise font_name
    
    Ensure the font_name is initialized to NULL.

diff --git a/src/cairo-cff-subset.c b/src/cairo-cff-subset.c
index baa210e..4697a18 100644
--- a/src/cairo-cff-subset.c
+++ b/src/cairo-cff-subset.c
@@ -1773,6 +1773,7 @@ _cairo_cff_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
     font->ascent = (int16_t) be16_to_cpu (hhea.ascender);
     font->descent = (int16_t) be16_to_cpu (hhea.descender);
 
+    font->font_name = NULL;
     status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
 					     &font->ps_name,
 					     &font->font_name);
@@ -2202,6 +2203,7 @@ _cairo_cff_fallback_init (cairo_cff_subset_t          *cff_subset,
     if (unlikely (status))
 	goto fail2;
 
+    cff_subset->font_name = NULL;
     cff_subset->ps_name = strdup (font->ps_name);
     if (unlikely (cff_subset->ps_name == NULL)) {
 	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
diff --git a/src/cairo-truetype-subset.c b/src/cairo-truetype-subset.c
index a8c495f..2633e87 100644
--- a/src/cairo-truetype-subset.c
+++ b/src/cairo-truetype-subset.c
@@ -201,6 +201,7 @@ _cairo_truetype_font_create (cairo_scaled_font_subset_t  *scaled_font_subset,
     if (font->base.units_per_em == 0)
         font->base.units_per_em = 2048;
 
+    font->base.font_name = NULL;
     status = _cairo_truetype_read_font_name (scaled_font_subset->scaled_font,
 					     &font->base.ps_name,
 					     &font->base.font_name);
commit 8388af137841679b7c510980daf3cec1427b6e6b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 29 09:50:38 2009 +0000

    [test] Trivial fixes for error paths.
    
    Kill a few leaks along error paths in the test code.

diff --git a/test/create-from-png-stream.c b/test/create-from-png-stream.c
index 3d1957d..a5c5395 100644
--- a/test/create-from-png-stream.c
+++ b/test/create-from-png-stream.c
@@ -27,6 +27,7 @@
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <errno.h>
 
 #define WIDTH 2
 #define HEIGHT 2
@@ -51,15 +52,24 @@ draw (cairo_t *cr, int width, int height)
     char *filename;
     FILE *file;
     cairo_surface_t *surface;
+    cairo_status_t status;
 
     xasprintf (&filename, "%s/%s", ctx->srcdir,
 	       "create-from-png-stream.ref.png");
 
     file = fopen (filename, "rb");
     if (file == NULL) {
-	cairo_test_log (ctx, "Error: failed to open file: %s\n", filename);
+	cairo_test_status_t ret;
+
+	ret = CAIRO_TEST_FAILURE;
+	if (errno == ENOMEM)
+	    ret = cairo_test_status_from_status (ctx, CAIRO_STATUS_NO_MEMORY);
+
+	if (ret != CAIRO_TEST_NO_MEMORY)
+	    cairo_test_log (ctx, "Error: failed to open file: %s\n", filename);
+
 	free (filename);
-	return CAIRO_TEST_FAILURE;
+	return ret;
     }
 
     surface = cairo_image_surface_create_from_png_stream (read_png_from_file,
@@ -67,13 +77,23 @@ draw (cairo_t *cr, int width, int height)
 
     fclose (file);
 
-    if (cairo_surface_status (surface)) {
-	cairo_test_log (ctx,
-			"Error: failed to create surface from PNG: %s - %s\n",
-			filename,
-			cairo_status_to_string (cairo_surface_status (surface)));
+    status = cairo_surface_status (surface);
+    if (status) {
+	cairo_test_status_t ret;
+
+	cairo_surface_destroy (surface);
+
+	ret = cairo_test_status_from_status (ctx, status);
+	if (ret != CAIRO_TEST_NO_MEMORY) {
+	    cairo_test_log (ctx,
+			    "Error: failed to create surface from PNG: %s - %s\n",
+			    filename,
+			    cairo_status_to_string (status));
+	}
+
 	free (filename);
-	return CAIRO_TEST_FAILURE;
+
+	return ret;
     }
 
     free (filename);
diff --git a/test/mime-data.c b/test/mime-data.c
index febc322..b1074cd 100644
--- a/test/mime-data.c
+++ b/test/mime-data.c
@@ -44,6 +44,9 @@ read_file (const cairo_test_context_t *ctx,
     if (file == NULL) {
 	char path[4096];
 
+	if (errno == ENOMEM)
+	    return CAIRO_STATUS_NO_MEMORY;
+
 	/* try again with srcdir */
 	snprintf (path, sizeof (path),
 		  "%s/%s", ctx->srcdir, filename);
@@ -100,6 +103,7 @@ paint_file (cairo_t *cr,
 					  free, mime_data);
     if (status) {
 	cairo_surface_destroy (image);
+	free (mime_data);
 	return cairo_test_status_from_status (ctx, status);
     }
 
commit 3752f690b467432ab5b1058d450cb79d719a794a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 29 09:49:41 2009 +0000

    [test] Suppress suppressed memfault report
    
    Check to see if there are any *unsuppressed* memfaults before declaring
    unreported faults.

diff --git a/test/cairo-test.c b/test/cairo-test.c
index 6dca6f8..48daa7f 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -762,8 +762,12 @@ REPEAT:
 	goto UNWIND_STRINGS;
     }
 
-    if (cairo_test_malloc_failure (ctx, cairo_surface_status (surface)))
+    if (ctx->malloc_failure &&
+	VALGRIND_COUNT_FAULTS () - last_fault_count > 0 &&
+	cairo_surface_status (surface) == CAIRO_STATUS_NO_MEMORY)
+    {
 	goto REPEAT;
+    }
 
     if (cairo_surface_status (surface)) {
 	MF (VALGRIND_PRINT_FAULTS ());
@@ -885,7 +889,9 @@ REPEAT:
     }
 
 #if HAVE_MEMFAULT
-    if (VALGRIND_COUNT_FAULTS () - last_fault_count > 0) {
+    if (VALGRIND_COUNT_FAULTS () - last_fault_count > 0 &&
+	VALGRIND_HAS_FAULTS ())
+    {
 	VALGRIND_PRINTF ("Unreported memfaults...");
 	VALGRIND_PRINT_FAULTS ();
     }
@@ -1494,6 +1500,7 @@ cairo_test_create_surface_from_png (const cairo_test_context_t *ctx,
 	if (ctx->srcdir) {
 	    char *srcdir_filename;
 	    xasprintf (&srcdir_filename, "%s/%s", ctx->srcdir, filename);
+	    cairo_surface_destroy (image);
 	    image = cairo_image_surface_create_from_png (srcdir_filename);
 	    free (srcdir_filename);
 	}
commit 8dc4c0da9b13b16c593e874d59c13a89a77a2481
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 13:17:24 2009 +0000

    [toy-font] Fix unwind behaviour following error during construction.
    
    We failed to cleanup the font face correctly after an allocation failure
    during _cairo_toy_font_face_init() leading to memleaks and live entries
    being left in the font-face hash tables.

diff --git a/src/cairo-toy-font-face.c b/src/cairo-toy-font-face.c
index fb9b909..5a60b14 100644
--- a/src/cairo-toy-font-face.c
+++ b/src/cairo-toy-font-face.c
@@ -151,37 +151,29 @@ _cairo_toy_font_face_init_key (cairo_toy_font_face_t *key,
     key->base.hash_entry.hash = hash;
 }
 
-static cairo_font_face_t *
-_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face)
+static cairo_status_t
+_cairo_toy_font_face_create_impl_face (cairo_toy_font_face_t *font_face,
+				       cairo_font_face_t **impl_font_face)
 {
     const cairo_font_face_backend_t * backend = CAIRO_FONT_FACE_BACKEND_DEFAULT;
-    cairo_font_face_t *impl_font_face;
     cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
 
-    if (font_face->base.status)
-	return NULL;
+    if (unlikely (font_face->base.status))
+	return font_face->base.status;
 
     if (backend->create_for_toy != NULL &&
 	0 != strncmp (font_face->family, CAIRO_USER_FONT_FAMILY_DEFAULT,
 		      strlen (CAIRO_USER_FONT_FAMILY_DEFAULT)))
     {
-	status = backend->create_for_toy (font_face, &impl_font_face);
+	status = backend->create_for_toy (font_face, impl_font_face);
     }
 
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 	backend = &_cairo_user_font_face_backend;
-	status = backend->create_for_toy (font_face, &impl_font_face);
-    }
-
-    if (_cairo_font_face_set_error (&font_face->base, status))
-	return NULL;
-
-    if (_cairo_font_face_set_error (&font_face->base, impl_font_face->status)) {
-	cairo_font_face_destroy (impl_font_face);
-	return NULL;
+	status = backend->create_for_toy (font_face, impl_font_face);
     }
 
-    return impl_font_face;
+    return status;
 }
 
 static cairo_status_t
@@ -191,20 +183,23 @@ _cairo_toy_font_face_init (cairo_toy_font_face_t *font_face,
 			   cairo_font_weight_t	  weight)
 {
     char *family_copy;
+    cairo_status_t status;
 
     family_copy = strdup (family);
     if (unlikely (family_copy == NULL))
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    _cairo_toy_font_face_init_key (font_face, family_copy,
-				      slant, weight);
+    _cairo_toy_font_face_init_key (font_face, family_copy, slant, weight);
     font_face->owns_family = TRUE;
 
-    font_face->impl_face = NULL;
-
     _cairo_font_face_init (&font_face->base, &_cairo_toy_font_face_backend);
 
-    font_face->impl_face = _cairo_toy_font_face_create_impl_face (font_face);
+    status = _cairo_toy_font_face_create_impl_face (font_face,
+						    &font_face->impl_face);
+    if (unlikely (status)) {
+	free (family_copy);
+	return status;
+    }
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -499,11 +494,15 @@ static const cairo_font_face_backend_t _cairo_toy_font_face_backend = {
 void
 _cairo_toy_font_face_reset_static_data (void)
 {
+    cairo_hash_table_t *hash_table;
+
     /* We manually acquire the lock rather than calling
      * cairo_toy_font_face_hash_table_lock simply to avoid
      * creating the table only to destroy it again. */
     CAIRO_MUTEX_LOCK (_cairo_toy_font_face_mutex);
-    _cairo_hash_table_destroy (cairo_toy_font_face_hash_table);
+    hash_table = cairo_toy_font_face_hash_table;
     cairo_toy_font_face_hash_table = NULL;
     CAIRO_MUTEX_UNLOCK (_cairo_toy_font_face_mutex);
+
+    _cairo_hash_table_destroy (hash_table);
 }
commit 1d52fbc8f4f70e9e2419a6ed66cd907552d1d13b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 12:51:52 2009 +0000

    [tessellator] Memleak on error path.
    
    Add a missing _cairo_skip_list_fini() after failure to allocate the
    events.

diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c
index c115419..4096b9a 100644
--- a/src/cairo-bentley-ottmann.c
+++ b/src/cairo-bentley-ottmann.c
@@ -982,8 +982,10 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t	*event_queue,
 				      sizeof (cairo_bo_event_t) +
 				      sizeof (cairo_bo_event_t *),
 				      sizeof (cairo_bo_event_t *));
-    if (unlikely (events == NULL))
+    if (unlikely (events == NULL)) {
+	_cairo_skip_list_fini (&event_queue->intersection_queue);
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    }
 
     sorted_event_ptrs = (cairo_bo_event_t **) (events + num_events);
     event_queue->startstop_events = events;
commit 6b5d2bf1a742b34a58d65f188fe15ffbf2f83118
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 10:42:42 2009 +0000

    [trace] Comment out the redundant wrapping of FT_Open_Face()
    
    Remove the left-over debugging spew, but leave a comment to hopefully
    clarify the situation with wrapping FT_Open_Face().

diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 53a8bb6..029a720 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -3598,6 +3598,14 @@ FT_New_Memory_Face (FT_Library library, const FT_Byte *mem, FT_Long size, FT_Lon
     return ret;
 }
 
+/* XXX
+ * FT_New_Memory_Face() and FT_New_Face() appear to wrap FT_Open_Face() so we
+ * get a redundant call to FT_Open_Face() from those paths (no PLT hiding
+ * within FT, naughty library!) but we do not intercept a direct call to
+ * FT_Open_Face(). So far this has not caused any issues, but it will one
+ * day...
+ */
+#if 0
 FT_Error
 FT_Open_Face (FT_Library library, const FT_Open_Args *args, FT_Long index, FT_Face *face)
 {
@@ -3617,6 +3625,7 @@ FT_Open_Face (FT_Library library, const FT_Open_Args *args, FT_Long index, FT_Fa
 
     return ret;
 }
+#endif
 
 FT_Error
 FT_Done_Face (FT_Face face)
commit faa004033cde21cd81890c7f82abae8eb766bb4b
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Jan 22 11:52:54 2009 +1100

    cairo-trace: Print name of trace file.
    
    It's just a lot easier to use cairo-trace if it tells you
    what file it just created.

diff --git a/util/cairo-trace/trace.c b/util/cairo-trace/trace.c
index 350c673..53a8bb6 100644
--- a/util/cairo-trace/trace.c
+++ b/util/cairo-trace/trace.c
@@ -729,6 +729,9 @@ _init_logfile (void)
 	return false;
     }
 
+    fprintf (stderr, "cairo-trace: Recording cairo trace data to %s\n",
+	     filename);
+
 done:
     atexit (_close_trace);
     _emit_header ();
commit d108b2777fcd6ef4fa45aeeef457dc58522e325b
Author: Daniel Holbert <dholbert at mozilla.com>
Date:   Fri Jan 23 10:18:48 2009 +0000

    Spelling corrections: s/it's/its/
    
    As a fun itch to scratch, I've been fixing incorrect uses of the
    contraction "it's" in comments within the mozilla source tree (tracked
    in https://bugzilla.mozilla.org/show_bug.cgi?id=458167 ), and I ran
    across 6 instances of this typo in mozilla's snapshot of cairo.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 17699de..383b6e0 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2706,7 +2706,7 @@ cairo_ft_font_face_create_for_ft_face (FT_Face         face,
  * threaded application, because freetype's design makes it unsafe to
  * call freetype functions simultaneously from multiple threads, (even
  * if using distinct FT_Face objects). Because of this, application
- * code that acquires an FT_Face object with this call must add it's
+ * code that acquires an FT_Face object with this call must add its
  * own locking to protect any use of that object, (and which also must
  * protect any other calls into cairo as almost any cairo function
  * might result in a call into the freetype library).
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index 6dfe537..b617472 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -513,7 +513,7 @@ _cairo_matrix_compute_adjoint (cairo_matrix_t *matrix)
  * cairo_matrix_invert:
  * @matrix: a #cairo_matrix_t
  *
- * Changes @matrix to be the inverse of it's original value. Not
+ * Changes @matrix to be the inverse of its original value. Not
  * all transformation matrices have inverses; if the matrix
  * collapses points together (it is <firstterm>degenerate</firstterm>),
  * then it has no inverse and this function will fail.
diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h
index 9df14a4..a956b52 100644
--- a/src/cairo-mutex-impl-private.h
+++ b/src/cairo-mutex-impl-private.h
@@ -53,7 +53,7 @@
 
 /* A fully qualified no-operation statement */
 #define CAIRO_MUTEX_IMPL_NOOP	do {/*no-op*/} while (0)
-/* And one that evaluates it's argument once */
+/* And one that evaluates its argument once */
 #define CAIRO_MUTEX_IMPL_NOOP1(expr)        do { (void)(expr); } while (0)
 /* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the
  * result of __attribute__((warn_used_result)) functions. */
diff --git a/src/cairo-pdf-operators.c b/src/cairo-pdf-operators.c
index 256b08e..22c0a88 100644
--- a/src/cairo-pdf-operators.c
+++ b/src/cairo-pdf-operators.c
@@ -120,7 +120,7 @@ _cairo_pdf_operators_enable_actual_text (cairo_pdf_operators_t *pdf_operators,
  * pdf_operators may leave the emitted PDF for some operations
  * unfinished in case subsequent operations can be merged. This
  * function will finish off any incomplete operation so the stream
- * will be in a state where the surface may emit it's own PDF
+ * will be in a state where the surface may emit its own PDF
  * operations (eg changing patterns).
  *
  */
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 96ed2f9..bcbc984 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -633,7 +633,7 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src,
              * If we have a clip surface, we set it as the mask; this only works
              * for bounded operators other than SOURCE; for unbounded operators,
              * clip and mask cannot be interchanged. For SOURCE, the operator
-             * as implemented by the backends is different in it's handling
+             * as implemented by the backends is different in its handling
              * of the mask then what we want.
              *
              * CAIRO_INT_STATUS_UNSUPPORTED will be returned if the region has
commit 6394ec3048f31b867d9588853fa400c6c630c6f1
Author: Paolo Bonzini <bonzini at gnu.org>
Date:   Mon Dec 15 09:32:43 2008 +0100

    [surface] add CAIRO_STATUS_INVALID_SIZE
    
    Adds an error code replacing CAIRO_STATUS_NO_MEMORY in one case where it
    is not really appropriate.  CAIRO_STATUS_INVALID_SIZE is used by several
    backends that do not support image sizes beyond 2^15 pixels on each side.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-misc.c b/src/cairo-misc.c
index 708ba52..729e6d5 100644
--- a/src/cairo-misc.c
+++ b/src/cairo-misc.c
@@ -120,7 +120,9 @@ cairo_status_to_string (cairo_status_t status)
     case CAIRO_STATUS_INVALID_SLANT:
 	return "invalid value for an input #cairo_font_slant_t";
     case CAIRO_STATUS_INVALID_WEIGHT:
-	return "input value for an input #cairo_font_weight_t";
+	return "invalid value for an input #cairo_font_weight_t";
+    case CAIRO_STATUS_INVALID_SIZE:
+	return "invalid value for the size of the input (surface, pattern, etc.)";
     }
 
     return "<unknown error status>";
diff --git a/src/cairo-os2-surface.c b/src/cairo-os2-surface.c
index 62b4ccd..fa678bd 100644
--- a/src/cairo-os2-surface.c
+++ b/src/cairo-os2-surface.c
@@ -770,7 +770,7 @@ cairo_os2_surface_create (HPS hps_client_window,
         (height <= 0))
     {
         /* Invalid window size! */
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
     }
 
     local_os2_surface = (cairo_os2_surface_t *) malloc (sizeof (cairo_os2_surface_t));
@@ -875,8 +875,9 @@ cairo_os2_surface_create (HPS hps_client_window,
  *
  * Return value: %CAIRO_STATUS_SUCCESS if the surface could be resized,
  * %CAIRO_STATUS_SURFACE_TYPE_MISMATCH if the surface is not an OS/2 surface,
- * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, for invalid
- * sizes, or if the timeout happened before all the buffers were released
+ * %CAIRO_STATUS_INVALID_SIZE for invalid sizes
+ * %CAIRO_STATUS_NO_MEMORY if the new size could not be allocated, or if the
+ * timeout happened before all the buffers were released
  *
  * Since: 1.4
  **/
@@ -903,7 +904,7 @@ cairo_os2_surface_set_size (cairo_surface_t *surface,
         (new_height <= 0))
     {
         /* Invalid size! */
-        return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+        return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
     }
 
     /* Allocate memory for new stuffs */
diff --git a/src/cairo-quartz-image-surface.c b/src/cairo-quartz-image-surface.c
index 3bfd9e2..dab3dc8 100644
--- a/src/cairo-quartz-image-surface.c
+++ b/src/cairo-quartz-image-surface.c
@@ -41,6 +41,7 @@
 
 #define SURFACE_ERROR_NO_MEMORY (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)))
 #define SURFACE_ERROR_TYPE_MISMATCH (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_SURFACE_TYPE_MISMATCH)))
+#define SURFACE_ERROR_INVALID_SIZE (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)))
 #define SURFACE_ERROR_INVALID_FORMAT (_cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_FORMAT)))
 
 static void
@@ -227,10 +228,10 @@ cairo_quartz_image_surface_create (cairo_surface_t *surface)
     data = image_surface->data;
 
     if (!_cairo_quartz_verify_surface_size(width, height))
-	return SURFACE_ERROR_NO_MEMORY;
+	return SURFACE_ERROR_INVALID_SIZE;
 
     if (width == 0 || height == 0)
-	return SURFACE_ERROR_NO_MEMORY;
+	return SURFACE_ERROR_INVALID_SIZE;
 
     if (format != CAIRO_FORMAT_ARGB32 && format != CAIRO_FORMAT_RGB24)
 	return SURFACE_ERROR_INVALID_FORMAT;
diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index 9dae70f..c827f97 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -1589,7 +1589,7 @@ _cairo_quartz_surface_create_similar (void *abstract_surface,
     // verify width and height of surface
     if (!_cairo_quartz_verify_surface_size(width, height)) {
 	return _cairo_surface_create_in_error (_cairo_error
-					       (CAIRO_STATUS_NO_MEMORY));
+					       (CAIRO_STATUS_INVALID_SIZE));
     }
 
     return cairo_quartz_surface_create (format, width, height);
@@ -2586,7 +2586,7 @@ cairo_quartz_surface_create (cairo_format_t format,
 
     // verify width and height of surface
     if (!_cairo_quartz_verify_surface_size(width, height))
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_SIZE));
 
     if (width == 0 || height == 0) {
 	return (cairo_surface_t*) _cairo_quartz_surface_create_internal (NULL, _cairo_content_from_format (format),
diff --git a/src/cairo-spans.c b/src/cairo-spans.c
index a30da61..b6ac2c5 100644
--- a/src/cairo-spans.c
+++ b/src/cairo-spans.c
@@ -288,6 +288,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
     case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
     case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
     default:
 	break;
     }
@@ -391,6 +392,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_SLANT: RETURN_NIL;
     case CAIRO_STATUS_INVALID_WEIGHT: RETURN_NIL;
     case CAIRO_STATUS_NO_MEMORY: RETURN_NIL;
+    case CAIRO_STATUS_INVALID_SIZE: RETURN_NIL;
     default:
 	break;
     }
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 4dd034e..d68e135 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -2973,6 +2973,7 @@ _cairo_surface_create_in_error (cairo_status_t status)
     case CAIRO_STATUS_INVALID_CLUSTERS:
     case CAIRO_STATUS_INVALID_SLANT:
     case CAIRO_STATUS_INVALID_WEIGHT:
+    case CAIRO_STATUS_INVALID_SIZE:
     default:
 	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
 	return (cairo_surface_t *) &_cairo_surface_nil;
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 1826ed9..a18fc03 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -136,8 +136,7 @@ _cairo_xlib_surface_create_similar_with_format (void	       *abstract_src,
     cairo_xlib_surface_t *surface;
     XRenderPictFormat *xrender_format;
 
-    if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
-	return NULL;
+    assert (width <= XLIB_COORD_MAX && height <= XLIB_COORD_MAX);
 
     /* As a good first approximation, if the display doesn't have even
      * the most elementary RENDER operation, then we're better off
@@ -209,7 +208,7 @@ _cairo_xlib_surface_create_similar (void	       *abstract_src,
     Pixmap pix;
 
     if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
-	return _cairo_surface_create_in_error (_cairo_error(CAIRO_STATUS_NO_MEMORY));
+	return _cairo_surface_create_in_error (_cairo_error(CAIRO_STATUS_INVALID_SIZE));
 
     _cairo_xlib_display_notify (src->display);
 
@@ -1200,7 +1199,7 @@ _cairo_xlib_surface_clone_similar (void			*abstract_surface,
 	    return CAIRO_INT_STATUS_UNSUPPORTED;
 
 	if (width > XLIB_COORD_MAX || height > XLIB_COORD_MAX)
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
 
 	clone = (cairo_xlib_surface_t *)
 	    _cairo_xlib_surface_create_similar_with_format (surface,
diff --git a/src/cairo.h b/src/cairo.h
index 8f57ae0..856f7af 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -239,6 +239,7 @@ typedef struct _cairo_user_data_key {
  * @CAIRO_STATUS_INVALID_CLUSTERS: input clusters do not represent the accompanying text and glyph array (Since 1.8)
  * @CAIRO_STATUS_INVALID_SLANT: invalid value for an input #cairo_font_slant_t (Since 1.8)
  * @CAIRO_STATUS_INVALID_WEIGHT: invalid value for an input #cairo_font_weight_t (Since 1.8)
+ * @CAIRO_STATUS_INVALID_SIZE: invalid value (typically too big) for a size (Since 1.10)
  *
  * #cairo_status_t is used to indicate errors that can occur when
  * using Cairo. In some cases it is returned directly by functions.
@@ -280,8 +281,9 @@ typedef enum _cairo_status {
     CAIRO_STATUS_NEGATIVE_COUNT,
     CAIRO_STATUS_INVALID_CLUSTERS,
     CAIRO_STATUS_INVALID_SLANT,
-    CAIRO_STATUS_INVALID_WEIGHT
-    /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h */
+    CAIRO_STATUS_INVALID_WEIGHT,
+    CAIRO_STATUS_INVALID_SIZE
+    /* after adding a new error: update CAIRO_STATUS_LAST_STATUS in cairoint.h.  */
 } cairo_status_t;
 
 /**
diff --git a/src/cairoint.h b/src/cairoint.h
index e3869d3..fe9ea5f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -125,7 +125,7 @@ _cairo_win32_tmpfile (void);
  * a bit of a pain, but it should be easy to always catch as long as
  * one adds a new test case to test a trigger of the new status value.
  */
-#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_WEIGHT
+#define CAIRO_STATUS_LAST_STATUS CAIRO_STATUS_INVALID_SIZE
 
 #ifdef __GNUC__
 #define cairo_container_of(ptr, type, member) ({ \
commit 46acfd2e85dd6f7a73e1172d363d509c769376f2
Author: Paolo Bonzini <bonzini at gnu.org>
Date:   Tue Dec 23 08:31:30 2008 +0100

    [glitz] use image fallback if the cairo_content_t is unsupported
    
    The agreement on the mailing list was that returning NULL is the right
    thing to do, and indeed the callers of _cairo_glitz_surface_create_similar
    are prepared to receive NULL and return CAIRO_STATUS_INT_UNSUPPORTED in
    that case.
    
    Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

diff --git a/src/cairo-glitz-surface.c b/src/cairo-glitz-surface.c
index f049f40..284198b 100644
--- a/src/cairo-glitz-surface.c
+++ b/src/cairo-glitz-surface.c
@@ -89,7 +89,7 @@ _cairo_glitz_surface_create_similar (void	    *abstract_src,
 	glitz_find_standard_format (drawable,
 				    _glitz_format_from_content (content));
     if (!gformat)
-	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+	return NULL;
 
     surface = glitz_surface_create (drawable, gformat,
 				    width <= 0 ? 1 : width,
commit 01d20b79daf0abe0f69ccec4ecd5122c5bfe9a4e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jan 23 10:09:37 2009 +0000

    [scaled-font] Fix up syntax in doc comments
    
    The old NULL vs %NULL conflict.

diff --git a/src/cairo-scaled-font-subsets-private.h b/src/cairo-scaled-font-subsets-private.h
index 00344ae..6eb6ce0 100644
--- a/src/cairo-scaled-font-subsets-private.h
+++ b/src/cairo-scaled-font-subsets-private.h
@@ -640,8 +640,9 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
 /**
  * _cairo_truetype_read_font_name:
  * @scaled_font: the #cairo_scaled_font_t
- * @ps_name: returns the PostScript name of the font or NULL if the name could not be found.
- * @font_name: returns the font name or NULL if the name could not be found.
+ * @ps_name: returns the PostScript name of the font
+ *           or %NULL if the name could not be found.
+ * @font_name: returns the font name or %NULL if the name could not be found.
  *
  * If possible (depending on the format of the underlying
  * #cairo_scaled_font_t and the font backend in use) read the
@@ -659,8 +660,8 @@ _cairo_truetype_index_to_ucs4 (cairo_scaled_font_t *scaled_font,
  **/
 cairo_private cairo_int_status_t
 _cairo_truetype_read_font_name (cairo_scaled_font_t   *scaled_font,
-				char 	       	     **ps_name,
-				char 	       	     **font_name);
+				char		     **ps_name,
+				char		     **font_name);
 
 #endif /* CAIRO_HAS_FONT_SUBSET */
 
commit aaec63d48386ec825cd4d6e67b6adf7c5fd3b167
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Nov 21 15:04:47 2008 +0000

    [scaled-font] Global glyph cache
    
    Currently glyphs are cached independently in each font i.e. each font
    maintains a cache of up to 256 glyphs, and there can be as many scaled fonts
    in use as the application needs and references (we maintain a holdover
    cache of 512 scaled fonts as well).
    
    Alternatively, as in this patch, we can maintain a global pool of glyphs
    split between all open fonts. This allows a heavily used individual font
    to cache more glyphs than we could allow if we used per-font glyph caches,
    but at the same time maintains fairness across all fonts (by using random
    replacement) and provides a cap on the maximum number of global glyphs.
    
    The glyphs are allocated in pages, which are cached in the global pool.
    Using pages means we can exploit spatial locality within the font
    (nearby indices are typically used in clusters) to reduce frequency of small
    allocations and allow the scaled font to reserve a single MRU page of
    glyphs. This caching dramatically reduces the cairo overhead during the
    cairo-perf benchmarks, and drastically reduces the number of allocations
    made by the application (for example browsing multi-lingual site with
    firefox).

diff --git a/boilerplate/cairo-boilerplate.c b/boilerplate/cairo-boilerplate.c
index 91032cd..dd34110 100644
--- a/boilerplate/cairo-boilerplate.c
+++ b/boilerplate/cairo-boilerplate.c
@@ -866,10 +866,7 @@ void
 cairo_boilerplate_scaled_font_set_max_glyphs_cached (cairo_scaled_font_t *scaled_font,
 						     int max_glyphs)
 {
-    if (cairo_scaled_font_status (scaled_font))
-	return;
-
-    scaled_font->glyphs->max_size = max_glyphs;
+    /* XXX CAIRO_DEBUG */
 }
 
 #if HAS_DAEMON
diff --git a/src/cairo-cache-private.h b/src/cairo-cache-private.h
index 5ac8cc8..8ad0c77 100644
--- a/src/cairo-cache-private.h
+++ b/src/cairo-cache-private.h
@@ -113,11 +113,19 @@ cairo_private void *
 _cairo_cache_lookup (cairo_cache_t	  *cache,
 		     cairo_cache_entry_t  *key);
 
+cairo_private void *
+_cairo_cache_steal (cairo_cache_t	  *cache,
+		     cairo_cache_entry_t  *key);
+
 cairo_private cairo_status_t
 _cairo_cache_insert (cairo_cache_t	 *cache,
 		     cairo_cache_entry_t *entry);
 
 cairo_private void
+_cairo_cache_remove (cairo_cache_t	 *cache,
+		     cairo_cache_entry_t *entry);
+
+cairo_private void
 _cairo_cache_foreach (cairo_cache_t		 *cache,
 		      cairo_cache_callback_func_t cache_callback,
 		      void			 *closure);
diff --git a/src/cairo-cache.c b/src/cairo-cache.c
index 07c8b71..cab6e1e 100644
--- a/src/cairo-cache.c
+++ b/src/cairo-cache.c
@@ -39,10 +39,6 @@
 #include "cairoint.h"
 
 static void
-_cairo_cache_remove (cairo_cache_t	 *cache,
-		     cairo_cache_entry_t *entry);
-
-static void
 _cairo_cache_shrink_to_accommodate (cairo_cache_t *cache,
 				    unsigned long  additional);
 
@@ -225,6 +221,26 @@ _cairo_cache_lookup (cairo_cache_t	  *cache,
 				     (cairo_hash_entry_t *) key);
 }
 
+void *
+_cairo_cache_steal (cairo_cache_t	  *cache,
+		    cairo_cache_entry_t  *key)
+{
+    cairo_cache_entry_t *entry;
+
+    entry = _cairo_hash_table_steal (cache->hash_table,
+				     (cairo_hash_entry_t *) key);
+    if (entry != NULL)
+	cache->size -= entry->size;
+
+    return entry;
+}
+
+static cairo_bool_t
+_cairo_cache_entry_is_non_zero (void *entry)
+{
+    return ((cairo_cache_entry_t *)entry)->size;
+}
+
 /**
  * _cairo_cache_remove_random:
  * @cache: a cache
@@ -239,7 +255,8 @@ _cairo_cache_remove_random (cairo_cache_t *cache)
 {
     cairo_cache_entry_t *entry;
 
-    entry = _cairo_hash_table_random_entry (cache->hash_table, NULL);
+    entry = _cairo_hash_table_random_entry (cache->hash_table,
+					    _cairo_cache_entry_is_non_zero);
     if (unlikely (entry == NULL))
 	return FALSE;
 
@@ -307,13 +324,8 @@ _cairo_cache_insert (cairo_cache_t	 *cache,
  * @entry: an entry that exists in the cache
  *
  * Remove an existing entry from the cache.
- *
- * (Note: If any caller wanted access to a non-static version of this
- * function, an improved version would require only a key rather than
- * an entry. Fixing that would require fixing _cairo_hash_table_remove
- * to return (a copy of?) the entry being removed.)
  **/
-static void
+void
 _cairo_cache_remove (cairo_cache_t	 *cache,
 		     cairo_cache_entry_t *entry)
 {
@@ -336,7 +348,7 @@ _cairo_cache_remove (cairo_cache_t	 *cache,
  * non-specified order.
  **/
 void
-_cairo_cache_foreach (cairo_cache_t	 	      *cache,
+_cairo_cache_foreach (cairo_cache_t		      *cache,
 		      cairo_cache_callback_func_t      cache_callback,
 		      void			      *closure)
 {
diff --git a/src/cairo-hash-private.h b/src/cairo-hash-private.h
index a0be097..8ab0858 100644
--- a/src/cairo-hash-private.h
+++ b/src/cairo-hash-private.h
@@ -68,6 +68,10 @@ _cairo_hash_table_lookup (cairo_hash_table_t  *hash_table,
 			  cairo_hash_entry_t  *key);
 
 cairo_private void *
+_cairo_hash_table_steal (cairo_hash_table_t *hash_table,
+			 cairo_hash_entry_t *key);
+
+cairo_private void *
 _cairo_hash_table_random_entry (cairo_hash_table_t	   *hash_table,
 				cairo_hash_predicate_func_t predicate);
 
diff --git a/src/cairo-hash.c b/src/cairo-hash.c
index 973281d..c0c9f7d 100644
--- a/src/cairo-hash.c
+++ b/src/cairo-hash.c
@@ -312,17 +312,17 @@ void *
 _cairo_hash_table_lookup (cairo_hash_table_t *hash_table,
 			  cairo_hash_entry_t *key)
 {
-    cairo_hash_entry_t **entry;
+    cairo_hash_entry_t *entry;
     unsigned long table_size, i, idx, step;
 
     table_size = hash_table->arrangement->size;
     idx = key->hash % table_size;
-    entry = &hash_table->entries[idx];
 
-    if (ENTRY_IS_LIVE (*entry)) {
-	if (hash_table->keys_equal (key, *entry))
-	    return *entry;
-    } else if (ENTRY_IS_FREE (*entry))
+    entry = hash_table->entries[idx];
+    if (ENTRY_IS_LIVE (entry)) {
+	if (hash_table->keys_equal (key, entry))
+	    return entry;
+    } else if (ENTRY_IS_FREE (entry))
 	return NULL;
 
     i = 1;
@@ -334,11 +334,66 @@ _cairo_hash_table_lookup (cairo_hash_table_t *hash_table,
 	if (idx >= table_size)
 	    idx -= table_size;
 
-	entry = &hash_table->entries[idx];
-	if (ENTRY_IS_LIVE (*entry)) {
-	    if (hash_table->keys_equal (key, *entry))
-		return *entry;
-	} else if (ENTRY_IS_FREE (*entry))
+	entry = hash_table->entries[idx];
+	if (ENTRY_IS_LIVE (entry)) {
+	    if (hash_table->keys_equal (key, entry))
+		return entry;
+	} else if (ENTRY_IS_FREE (entry))
+	    return NULL;
+    } while (++i < table_size);
+
+    return NULL;
+}
+
+/**
+ * _cairo_hash_table_steal:
+ * @hash_table: a hash table
+ * @key: the key of interest
+ *
+ * Performs a lookup in @hash_table looking for an entry which has a
+ * key that matches @key, (as determined by the keys_equal() function
+ * passed to _cairo_hash_table_create) and removes that entry from the
+ * hash table.
+ *
+ * Return value: the matching entry, of %NULL if no match was found.
+ **/
+void *
+_cairo_hash_table_steal (cairo_hash_table_t *hash_table,
+			 cairo_hash_entry_t *key)
+{
+    cairo_hash_entry_t *entry;
+    unsigned long table_size, i, idx, step;
+
+    table_size = hash_table->arrangement->size;
+    idx = key->hash % table_size;
+
+    entry = hash_table->entries[idx];
+    if (ENTRY_IS_LIVE (entry)) {
+	if (hash_table->keys_equal (key, entry)) {
+	    hash_table->entries[idx] = DEAD_ENTRY;
+	    hash_table->live_entries--;
+	    return entry;
+	}
+    } else if (ENTRY_IS_FREE (entry))
+	return NULL;
+
+    i = 1;
+    step = key->hash % hash_table->arrangement->rehash;
+    if (step == 0)
+	step = 1;
+    do {
+	idx += step;
+	if (idx >= table_size)
+	    idx -= table_size;
+
+	entry = hash_table->entries[idx];
+	if (ENTRY_IS_LIVE (entry)) {
+	    if (hash_table->keys_equal (key, entry)) {
+		hash_table->entries[idx] = DEAD_ENTRY;
+		hash_table->live_entries--;
+		return entry;
+	    }
+	} else if (ENTRY_IS_FREE (entry))
 	    return NULL;
     } while (++i < table_size);
 
@@ -348,11 +403,10 @@ _cairo_hash_table_lookup (cairo_hash_table_t *hash_table,
 /**
  * _cairo_hash_table_random_entry:
  * @hash_table: a hash table
- * @predicate: a predicate function, or %NULL for any entry.
+ * @predicate: a predicate function.
  *
  * Find a random entry in the hash table satisfying the given
- * @predicate. A %NULL @predicate is taken as equivalent to a function
- * which always returns %TRUE, (eg. any entry in the table will do).
+ * @predicate.
  *
  * We use the same algorithm as the lookup algorithm to walk over the
  * entries in the hash table in a pseudo-random order. Walking
@@ -369,36 +423,33 @@ void *
 _cairo_hash_table_random_entry (cairo_hash_table_t	   *hash_table,
 				cairo_hash_predicate_func_t predicate)
 {
-    cairo_hash_entry_t **entry;
+    cairo_hash_entry_t *entry;
     unsigned long hash;
     unsigned long table_size, i, idx, step;
 
-    table_size = hash_table->arrangement->size;
+    assert (predicate != NULL);
 
+    table_size = hash_table->arrangement->size;
     hash = rand ();
     idx = hash % table_size;
-    step = 0;
 
-    for (i = 0; i < table_size; ++i)
-    {
-	entry = &hash_table->entries[idx];
-
-	if (ENTRY_IS_LIVE (*entry) &&
-	    (predicate == NULL || predicate (*entry)))
-	{
-	    return *entry;
-	}
-
-	if (step == 0) {
-	    step = hash % hash_table->arrangement->rehash;
-	    if (step == 0)
-		step = 1;
-	}
+    entry = hash_table->entries[idx];
+    if (ENTRY_IS_LIVE (entry) && predicate (entry))
+	return entry;
 
+    i = 1;
+    step = hash % hash_table->arrangement->rehash;
+    if (step == 0)
+	step = 1;
+    do {
 	idx += step;
 	if (idx >= table_size)
 	    idx -= table_size;
-    }
+
+	entry = hash_table->entries[idx];
+	if (ENTRY_IS_LIVE (entry) && predicate (entry))
+	    return entry;
+    } while (++i < table_size);
 
     return NULL;
 }
diff --git a/src/cairo-mutex-list-private.h b/src/cairo-mutex-list-private.h
index 7d43481..8f62cb9 100644
--- a/src/cairo-mutex-list-private.h
+++ b/src/cairo-mutex-list-private.h
@@ -42,6 +42,7 @@ CAIRO_MUTEX_DECLARE (_cairo_pattern_solid_surface_cache_lock)
 CAIRO_MUTEX_DECLARE (_cairo_toy_font_face_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_intern_string_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_map_mutex)
+CAIRO_MUTEX_DECLARE (_cairo_scaled_glyph_page_cache_mutex)
 CAIRO_MUTEX_DECLARE (_cairo_scaled_font_error_mutex)
 
 #if CAIRO_HAS_FT_FONT
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index 86a50bb..13d89eb 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -44,6 +44,8 @@
 #include "cairo-mutex-type-private.h"
 #include "cairo-reference-count-private.h"
 
+typedef struct _cairo_scaled_glyph_page cairo_scaled_glyph_page_t;
+
 struct _cairo_scaled_font {
     /* For most cairo objects, the rule for multiple threads is that
      * the user is responsible for any locking if the same object is
@@ -75,7 +77,6 @@ struct _cairo_scaled_font {
      *    scaled_font->mutex in the generic scaled_font code.
      */
 
-    /* must be first to be stored in a hash table */
     cairo_hash_entry_t hash_entry;
 
     /* useful bits for _cairo_scaled_font_nil */
@@ -103,7 +104,8 @@ struct _cairo_scaled_font {
     /* The mutex protects modification to all subsequent fields. */
     cairo_mutex_t mutex;
 
-    cairo_cache_t *glyphs;	  /* glyph index -> cairo_scaled_glyph_t */
+    int cache_frozen;
+    cairo_scaled_glyph_page_t *mru_page;
 
     /*
      * One surface backend may store data in each glyph.
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 1d5798f..727ab2e 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -35,11 +35,30 @@
  *      Graydon Hoare <graydon at redhat.com>
  *      Owen Taylor <otaylor at redhat.com>
  *      Behdad Esfahbod <behdad at behdad.org>
+ *      Chris Wilson <chris at chris-wilson.co.uk>
  */
 
 #include "cairoint.h"
 #include "cairo-scaled-font-private.h"
 
+#define CAIRO_SCALED_GLYPH_PAGE_SHIFT 7
+#define CAIRO_SCALED_GLYPH_PAGE_SIZE (1 << CAIRO_SCALED_GLYPH_PAGE_SHIFT)
+#define CAIRO_SCALED_GLYPH_PAGE_INDEX(I) \
+    ((I) & (CAIRO_SCALED_GLYPH_PAGE_SIZE - 1))
+#define CAIRO_SCALED_GLYPH_PAGE_BASE_INDEX(I) ((I) & -CAIRO_SCALED_GLYPH_PAGE_SIZE)
+#define CAIRO_SCALED_GLYPH_PAGE_HAS_INDEX(P, I) \
+    ((I) - (P)->base_index < CAIRO_SCALED_GLYPH_PAGE_SIZE)
+typedef struct _cairo_scaled_glyph_page_key {
+    cairo_cache_entry_t cache_entry;
+    cairo_scaled_font_t *scaled_font;
+} cairo_scaled_glyph_page_key_t;
+
+struct _cairo_scaled_glyph_page {
+    cairo_scaled_glyph_page_key_t key;
+    unsigned long base_index;
+    cairo_scaled_glyph_t glyphs[CAIRO_SCALED_GLYPH_PAGE_SIZE];
+};
+
 /*
  *  Notes:
  *
@@ -131,16 +150,16 @@
  *  Now compare the scaled-glyph space to device-space and surface-space
  *  and convince yourself that:
  *
- *  	(x_bearing,y_bearing) = (-x,-y) = - device_offset
+ *	(x_bearing,y_bearing) = (-x,-y) = - device_offset
  *
  *  That's right.  If you are not convinced yet, contrast the definition
  *  of the two:
  *
- *  	"(x_bearing,y_bearing) is the coordinates of top-left of the
- *  	 glyph relative to the glyph origin."
+ *	"(x_bearing,y_bearing) is the coordinates of top-left of the
+ *	 glyph relative to the glyph origin."
  *
- *  	"In other words: device_offset is the coordinates of the
- *  	 device-space origin relative to the top-left of the surface."
+ *	"In other words: device_offset is the coordinates of the
+ *	 device-space origin relative to the top-left of the surface."
  *
  *  and note that glyph origin = device-space origin.
  */
@@ -148,15 +167,9 @@
 static void
 _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font);
 
-static cairo_bool_t
-_cairo_scaled_glyph_keys_equal (const void *abstract_key_a, const void *abstract_key_b)
-{
-    const cairo_scaled_glyph_t *key_a = abstract_key_a;
-    const cairo_scaled_glyph_t *key_b = abstract_key_b;
-
-    return (_cairo_scaled_glyph_index (key_a) ==
-	    _cairo_scaled_glyph_index (key_b));
-}
+static void
+_cairo_scaled_glyph_page_cache_remove_scaled_font (cairo_scaled_font_t
+						   *scaled_font);
 
 static void
 _cairo_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph)
@@ -166,20 +179,17 @@ _cairo_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph)
 
     if (surface_backend != NULL && surface_backend->scaled_glyph_fini != NULL)
 	surface_backend->scaled_glyph_fini (scaled_glyph, scaled_font);
+
     if (scaled_glyph->surface != NULL)
 	cairo_surface_destroy (&scaled_glyph->surface->base);
+
     if (scaled_glyph->path != NULL)
 	_cairo_path_fixed_destroy (scaled_glyph->path);
+
     if (scaled_glyph->meta_surface != NULL)
 	cairo_surface_destroy (scaled_glyph->meta_surface);
-}
 
-static void
-_cairo_scaled_glyph_destroy (void *abstract_glyph)
-{
-    cairo_scaled_glyph_t *scaled_glyph = abstract_glyph;
-    _cairo_scaled_glyph_fini (scaled_glyph);
-    free (scaled_glyph);
+    scaled_glyph->scaled_font = NULL;
 }
 
 #define ZOMBIE 0
@@ -203,7 +213,8 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = {
     { 0., 0., 0., 0., 0. },	/* extents */
     { 0., 0., 0., 0., 0. },	/* fs_extents */
     CAIRO_MUTEX_NIL_INITIALIZER,/* mutex */
-    NULL,			/* glyphs */
+    FALSE,			/* cache_frozen */
+    NULL,			/* mru_page */
     NULL,			/* surface_backend */
     NULL,			/* surface_private */
     NULL			/* backend */
@@ -310,7 +321,7 @@ typedef struct _cairo_scaled_font_map {
     int num_holdovers;
 } cairo_scaled_font_map_t;
 
-static cairo_scaled_font_map_t *cairo_scaled_font_map = NULL;
+static cairo_scaled_font_map_t *cairo_scaled_font_map;
 
 static int
 _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_key_b);
@@ -400,6 +411,112 @@ _cairo_scaled_font_map_destroy (void)
     CAIRO_MUTEX_UNLOCK (_cairo_scaled_font_map_mutex);
 }
 
+/* Global Glyph Cache
+ *
+ * We maintain a global pool of glyphs split between all open fonts. This
+ * allows a heavily used individual font to cache more glyphs than we could
+ * manage if we used per-font glyph caches, but at the same time maintains
+ * fairness across all fonts and provides a cap on the maximum number of
+ * global glyphs.
+ *
+ * The glyphs are allocated in pages, which are cached in the global pool.
+ * Using pages means we can exploit spatial locality within the font (nearby
+ * indices are typically used in clusters) to reduce frequency of small
+ * allocations and allow the scaled font to reserve a single MRU page of
+ * glyphs.
+ */
+
+/* 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_bool_t
+_cairo_scaled_glyph_pages_equal (const void *key_a, const void *key_b)
+{
+    const cairo_scaled_glyph_page_key_t *a = key_a;
+    const cairo_scaled_glyph_page_key_t *b = key_b;
+
+    return
+	a->cache_entry.hash == b->cache_entry.hash &&
+	a->scaled_font == b->scaled_font;
+}
+
+static void
+_cairo_scaled_glyph_page_destroy (void *closure)
+{
+    cairo_scaled_glyph_page_t *page = closure;
+    int n;
+
+    for (n = 0; n < CAIRO_SCALED_GLYPH_PAGE_SIZE; n++) {
+	if (page->glyphs[n].scaled_font != NULL)
+	    _cairo_scaled_glyph_fini (&page->glyphs[n]);
+    }
+
+    free (page);
+}
+
+static cairo_scaled_glyph_page_t *
+_cairo_scaled_glyph_page_cache_lookup (cairo_scaled_font_t *scaled_font,
+				       unsigned long index)
+{
+    cairo_scaled_glyph_page_key_t key;
+    cairo_scaled_glyph_page_t *page;
+
+    key.cache_entry.hash =
+	(index >> CAIRO_SCALED_GLYPH_PAGE_SHIFT) ^
+	(unsigned long) scaled_font;
+    key.scaled_font = scaled_font;
+
+    if (scaled_font->cache_frozen) {
+	CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+	page = _cairo_cache_steal (cairo_scaled_glyph_page_cache,
+				   &key.cache_entry);
+	CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+    } else
+	page = NULL;
+
+    if (page == NULL) {
+	/* On miss, create glyph page and insert into cache */
+	page = malloc (sizeof (cairo_scaled_glyph_page_t));
+	if (unlikely (page == NULL))
+	    return NULL;
+
+	page->key.cache_entry.hash = key.cache_entry.hash;
+	/* We currently don't differentiate on glyph size at all */
+	page->key.cache_entry.size = 1;
+	page->key.scaled_font = scaled_font;
+	page->base_index = CAIRO_SCALED_GLYPH_PAGE_BASE_INDEX (index);
+
+	memset (page->glyphs, 0, sizeof (page->glyphs));
+    }
+
+    return page;
+}
+
+static void
+_cairo_scaled_glyph_page_cache_remove_scaled_font_cb (void *entry,
+						      void *closure)
+{
+    cairo_scaled_glyph_page_key_t *key = entry;
+
+    if (key->scaled_font == closure)
+	_cairo_cache_remove (cairo_scaled_glyph_page_cache, entry);
+}
+
+static void
+_cairo_scaled_glyph_page_cache_remove_scaled_font (cairo_scaled_font_t *scaled_font)
+{
+    CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+
+    if (cairo_scaled_glyph_page_cache != NULL) {
+	_cairo_cache_foreach (cairo_scaled_glyph_page_cache,
+			      _cairo_scaled_glyph_page_cache_remove_scaled_font_cb,
+			      scaled_font);
+    }
+
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+}
 
 /* If a scaled font wants to unlock the font map while still being
  * created (needed for user-fonts), we need to take extra care not
@@ -591,13 +708,6 @@ _cairo_scaled_font_keys_equal (const void *abstract_key_a, const void *abstract_
 	    cairo_font_options_equal (&key_a->options, &key_b->options));
 }
 
-/* XXX: This 256 number is arbitrary---we've never done any measurement
- * of this. In fact, having a per-font glyph caches each managed
- * separately is probably not what we want anyway. Would probably be
- * much better to have a single cache for glyphs with random
- * replacement across all glyphs of all fonts. */
-#define MAX_GLYPHS_CACHED_PER_FONT 256
-
 /*
  * Basic #cairo_scaled_font_t object management
  */
@@ -648,12 +758,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
     }
 
     scaled_font->finished = FALSE;
-
-    scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
-					       _cairo_scaled_glyph_destroy,
-					       MAX_GLYPHS_CACHED_PER_FONT);
-    if (unlikely (scaled_font->glyphs == NULL))
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    scaled_font->cache_frozen = FALSE;
 
     CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
 
@@ -663,6 +768,8 @@ _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
 
     CAIRO_MUTEX_INIT (scaled_font->mutex);
 
+    scaled_font->mru_page = NULL;
+
     scaled_font->surface_backend = NULL;
     scaled_font->surface_private = NULL;
 
@@ -678,13 +785,19 @@ _cairo_scaled_font_freeze_cache (cairo_scaled_font_t *scaled_font)
     assert (scaled_font->status == CAIRO_STATUS_SUCCESS);
 
     CAIRO_MUTEX_LOCK (scaled_font->mutex);
-    _cairo_cache_freeze (scaled_font->glyphs);
 }
 
 void
 _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
 {
-    _cairo_cache_thaw (scaled_font->glyphs);
+    if (scaled_font->cache_frozen) {
+	CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+	_cairo_cache_thaw (cairo_scaled_glyph_page_cache);
+	CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+
+	scaled_font->cache_frozen = FALSE;
+    }
+
     CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
 }
 
@@ -693,10 +806,12 @@ _cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
 {
     assert (CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex));
 
-    _cairo_cache_destroy (scaled_font->glyphs);
-    scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
-					       _cairo_scaled_glyph_destroy,
-					       MAX_GLYPHS_CACHED_PER_FONT);
+    if (scaled_font->mru_page != NULL) {
+	_cairo_scaled_glyph_page_destroy (scaled_font->mru_page);
+	scaled_font->mru_page = NULL;
+    }
+
+    _cairo_scaled_glyph_page_cache_remove_scaled_font (scaled_font);
 }
 
 cairo_status_t
@@ -733,12 +848,16 @@ _cairo_scaled_font_fini_internal (cairo_scaled_font_t *scaled_font)
 {
     scaled_font->finished = TRUE;
 
+    if (scaled_font->mru_page != NULL) {
+	_cairo_scaled_glyph_page_destroy (scaled_font->mru_page);
+	scaled_font->mru_page = NULL;
+    }
+
+    _cairo_scaled_glyph_page_cache_remove_scaled_font (scaled_font);
+
     if (scaled_font->font_face != NULL)
 	cairo_font_face_destroy (scaled_font->font_face);
 
-    if (scaled_font->glyphs != NULL)
-	_cairo_cache_destroy (scaled_font->glyphs);
-
     CAIRO_MUTEX_FINI (scaled_font->mutex);
 
     if (scaled_font->surface_backend != NULL &&
@@ -981,6 +1100,13 @@ _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;
+    }
+    CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
 }
 
 /**
@@ -1038,9 +1164,9 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
     assert (font_map != NULL);
 
     if (_cairo_reference_count_dec_and_test (&scaled_font->ref_count)) {
-
-
-	if (!scaled_font->placeholder && scaled_font->hash_entry.hash != ZOMBIE) {
+	if (! scaled_font->placeholder &&
+	    scaled_font->hash_entry.hash != ZOMBIE)
+	{
 	    /* Rather than immediately destroying this object, we put it into
 	     * the font_map->holdovers array in case it will get used again
 	     * soon (and is why we must hold the lock over the atomic op on
@@ -1066,7 +1192,6 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
 	    font_map->num_holdovers++;
 	} else
 	    lru = scaled_font;
-
     }
 
     _cairo_scaled_font_map_unlock ();
@@ -1160,13 +1285,6 @@ cairo_scaled_font_set_user_data (cairo_scaled_font_t	     *scaled_font,
 }
 slim_hidden_def (cairo_scaled_font_set_user_data);
 
-static cairo_bool_t
-_cairo_scaled_font_is_frozen (cairo_scaled_font_t *scaled_font)
-{
-    return CAIRO_MUTEX_IS_LOCKED (scaled_font->mutex) &&
-	   scaled_font->glyphs->freeze_count > 0;
-}
-
 /* Public font API follows. */
 
 /**
@@ -2378,58 +2496,72 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font,
 			    cairo_scaled_glyph_t **scaled_glyph_ret)
 {
     cairo_status_t		status = CAIRO_STATUS_SUCCESS;
-    cairo_cache_entry_t		key;
+    cairo_scaled_glyph_page_t	*page;
     cairo_scaled_glyph_t	*scaled_glyph;
     cairo_scaled_glyph_info_t	need_info;
 
-    if (scaled_font->status)
+    if (unlikely (scaled_font->status))
 	return scaled_font->status;
 
-    assert (_cairo_scaled_font_is_frozen (scaled_font));
+    page = scaled_font->mru_page;
+    if (page != NULL && ! CAIRO_SCALED_GLYPH_PAGE_HAS_INDEX (page, index)) {
+	CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
+	if (! scaled_font->cache_frozen) {
+	    if (cairo_scaled_glyph_page_cache == NULL) {
+		cairo_scaled_glyph_page_cache =
+		    _cairo_cache_create (_cairo_scaled_glyph_pages_equal,
+					 _cairo_scaled_glyph_page_destroy,
+					 MAX_GLYPH_PAGES_CACHED);
+		if (unlikely (cairo_scaled_glyph_page_cache == NULL)) {
+		    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		    goto BAIL;
+		}
+	    }
+
+	    _cairo_cache_freeze (cairo_scaled_glyph_page_cache);
+	    scaled_font->cache_frozen = TRUE;
+	}
+	status = _cairo_cache_insert (cairo_scaled_glyph_page_cache,
+				      &page->key.cache_entry);
+      BAIL:
+	CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+	if (unlikely (status))
+	    return _cairo_scaled_font_set_error (scaled_font, status);
+
+	page = scaled_font->mru_page = NULL;
+    }
+
+    if (page == NULL) {
+	page = _cairo_scaled_glyph_page_cache_lookup (scaled_font, index);
+	if (unlikely (page == NULL)) {
+	    return _cairo_scaled_font_set_error (scaled_font,
+						 _cairo_error (CAIRO_STATUS_NO_MEMORY));
+	}
+    }
+
+    scaled_font->mru_page = page;
 
-    key.hash = index;
     /*
      * Check cache for glyph
      */
     info |= CAIRO_SCALED_GLYPH_INFO_METRICS;
-    scaled_glyph = _cairo_cache_lookup (scaled_font->glyphs, &key);
-    if (scaled_glyph == NULL) {
-	/*
-	 * On miss, create glyph and insert into cache
-	 */
-	scaled_glyph = malloc (sizeof (cairo_scaled_glyph_t));
-	if (unlikely (scaled_glyph == NULL)) {
-	    status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    goto CLEANUP;
-	}
-
-	_cairo_scaled_glyph_set_index(scaled_glyph, index);
-	scaled_glyph->cache_entry.size = 1;	/* We currently don't differentiate on glyph size at all */
+    scaled_glyph = &page->glyphs[CAIRO_SCALED_GLYPH_PAGE_INDEX (index)];
+    if (scaled_glyph->scaled_font == NULL) {
+	scaled_glyph->index = index;
 	scaled_glyph->scaled_font = scaled_font;
-	scaled_glyph->surface = NULL;
-	scaled_glyph->path = NULL;
-	scaled_glyph->meta_surface = NULL;
-	scaled_glyph->surface_private = NULL;
 
 	/* ask backend to initialize metrics and shape fields */
 	status = (*scaled_font->backend->
 		  scaled_glyph_init) (scaled_font, scaled_glyph, info);
 	if (unlikely (status)) {
-	    _cairo_scaled_glyph_destroy (scaled_glyph);
-	    goto CLEANUP;
-	}
-
-	/* on success, the cache takes ownership of the scaled_glyph */
-	status = _cairo_cache_insert (scaled_font->glyphs,
-				      &scaled_glyph->cache_entry);
-	if (unlikely (status)) {
-	    _cairo_scaled_glyph_destroy (scaled_glyph);
+	    _cairo_scaled_glyph_fini (scaled_glyph);
 	    goto CLEANUP;
 	}
     }
+
     /*
      * Check and see if the glyph, as provided,
-     * already has the requested data and ammend it if not
+     * already has the requested data and amend it if not
      */
     need_info = 0;
     if ((info & CAIRO_SCALED_GLYPH_INFO_SURFACE) != 0 &&
diff --git a/src/cairoint.h b/src/cairoint.h
index 6b38fd4..e3869d3 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -351,21 +351,24 @@ typedef struct _cairo_unscaled_font {
 } cairo_unscaled_font_t;
 
 typedef struct _cairo_scaled_glyph {
-    cairo_cache_entry_t	    cache_entry;	/* hash is glyph index */
+    unsigned long index;
     cairo_scaled_font_t	    *scaled_font;	/* font the glyph lives in */
+
     cairo_text_extents_t    metrics;		/* user-space metrics */
     cairo_text_extents_t    fs_metrics;		/* font-space metrics */
     cairo_box_t		    bbox;		/* device-space bounds */
     int16_t                 x_advance;		/* device-space rounded X advance */
     int16_t                 y_advance;		/* device-space rounded Y advance */
+
     cairo_image_surface_t   *surface;		/* device-space image */
     cairo_path_fixed_t	    *path;		/* device-space outline */
     cairo_surface_t         *meta_surface;	/* device-space meta-surface */
+
     void		    *surface_private;	/* for the surface backend */
 } cairo_scaled_glyph_t;
 
-#define _cairo_scaled_glyph_index(g) ((g)->cache_entry.hash)
-#define _cairo_scaled_glyph_set_index(g,i)  ((g)->cache_entry.hash = (i))
+#define _cairo_scaled_glyph_index(g) ((g)->index)
+#define _cairo_scaled_glyph_set_index(g, i)  ((g)->index = (i))
 
 #include "cairo-scaled-font-private.h"
 
commit 54f6a49ebb18cf396823d0d70b95e4e264142171
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 19 15:36:29 2009 +0000

    [bounds] Skip spline evaluation based on bounding bbox of control points.
    
    The bounding polygon of the control points, defines the extents of the
    spline. Therefore if the control points are entirely contained within the
    current path extents, so is the spline and we do not need to evaluate its
    tight bounds.

diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 9f30eb5..1fadd84 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -121,8 +121,25 @@ _cairo_path_bounder_curve_to (void *closure,
 {
     cairo_path_bounder_t *bounder = closure;
 
-    return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder,
-				&bounder->current_point, b, c, d);
+    /* If the bbox of the control points is entirely inside, then we
+     * do not need to further evaluate the spline.
+     */
+    if (! bounder->has_point ||
+	b->x < bounder->extents.p1.x || b->x > bounder->extents.p2.x ||
+	b->y < bounder->extents.p1.y || b->y > bounder->extents.p2.y ||
+	c->x < bounder->extents.p1.x || c->x > bounder->extents.p2.x ||
+	c->y < bounder->extents.p1.y || c->y > bounder->extents.p2.y ||
+	d->x < bounder->extents.p1.x || d->x > bounder->extents.p2.x ||
+	d->y < bounder->extents.p1.y || d->y > bounder->extents.p2.y)
+    {
+	return _cairo_spline_bound (_cairo_path_bounder_line_to, bounder,
+				    &bounder->current_point, b, c, d);
+    }
+    else
+    {
+	/* All control points are within the current extents. */
+	return CAIRO_STATUS_SUCCESS;
+    }
 }
 
 static cairo_status_t
commit e217c4da7bc5c4817e0d829ff61dd2bd5b3145a6
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jan 19 15:28:05 2009 +0000

    [in-stroke] Check point against extents before computing path.
    
    We can avoid tessellating the path entirely by first checking whether the
    query point is inside the path extents.

diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c
index c79e799..9ee31b0 100644
--- a/src/cairo-gstate.c
+++ b/src/cairo-gstate.c
@@ -994,6 +994,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t	    *gstate,
 			 cairo_bool_t	    *inside_ret)
 {
     cairo_status_t status;
+    cairo_rectangle_int_t extents;
     cairo_box_t limit;
     cairo_traps_t traps;
 
@@ -1004,6 +1005,20 @@ _cairo_gstate_in_stroke (cairo_gstate_t	    *gstate,
 
     _cairo_gstate_user_to_backend (gstate, &x, &y);
 
+    /* Before we perform the expensive stroke analysis,
+     * check whether the point is within the extents of the path.
+     */
+    _cairo_path_fixed_approximate_stroke_extents (path,
+						  &gstate->stroke_style,
+						  &gstate->ctm,
+						  &extents);
+    if (x < extents.x || x > extents.x + extents.width ||
+	y < extents.y || y > extents.y + extents.height)
+    {
+	*inside_ret = FALSE;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
     limit.p1.x = _cairo_fixed_from_double (x) - 1;
     limit.p1.y = _cairo_fixed_from_double (y) - 1;
     limit.p2.x = limit.p1.x + 2;
commit 48f9a0e6da0dd24ea9c809876ef3c745dcfd0d52
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 15 00:26:03 2009 +0000

    [spline] Correct the definition of a cubic Bezier curve.
    
    Add the missing coefficients for p1 and p2 so the derivation of the
    derivative and the solution for its inflection points stands correct.

diff --git a/src/cairo-spline.c b/src/cairo-spline.c
index 414e053..948516e 100644
--- a/src/cairo-spline.c
+++ b/src/cairo-spline.c
@@ -234,7 +234,7 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
 
     /* The spline can be written as a polynomial of the four points:
      *
-     *   (1-t)³p0 + t(1-t)²p1 + t²(1-t)p2 + t³p3
+     *   (1-t)³p0 + 3t(1-t)²p1 + 3t²(1-t)p2 + t³p3
      *
      * for 0≤t≤1.  Now, the X and Y components of the spline follow the
      * same polynomial but with x and y replaced for p.  To find the
@@ -244,13 +244,13 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func,
      *
      * Here is the derivative of the curve, sorted on t:
      *
-     *   3t²(-p0+3p1-3p2+p3) + 6t(3p0-6p1+3p2) -3p0+3p1
+     *   3t²(-p0+3p1-3p2+p3) + 2t(3p0-6p1+3p2) -3p0+3p1
      *
      * Let:
      *
      *   a = -p0+3p1-3p2+p3
-     *   b =  3p0-6p1+3p2
-     *   c = -3p0+3p1
+     *   b =  p0-2p1+p2
+     *   c = -p0+p1
      *
      * Gives:
      *
commit ee7ac5681fa6a74b68beeae667d96d1421050fc9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jan 21 12:04:06 2009 +0000

    [path] A degenerate curve_to becomes a line_to.
    
    Be consistent.

diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 6e963ae..41096f1 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -901,7 +901,7 @@ _cpf_curve_to (void		*closure,
 			      cpf->closure,
 			      p0, p1, p2, p3))
     {
-	return CAIRO_STATUS_SUCCESS;
+	return _cpf_line_to (closure, p3);
     }
 
     cpf->current_point = *p3;
commit 778ced4879b09f7482bd41c398bf2d984754ed0b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jan 14 18:55:32 2009 +0000

    [path] Rename _cairo_path_fixed_approximate_extents()
    
    Rename approximate_extents() to approximate_clip_extents() so that it is
    consistent with the fill and stroke variants and clearer under what
    circumstances you may wish to use it.

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index b80ad62..1be592e 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -293,7 +293,7 @@ _cairo_analysis_surface_intersect_clip_path (void		*abstract_surface,
 	cairo_rectangle_int_t extents;
 	cairo_bool_t is_empty;
 
-	_cairo_path_fixed_approximate_extents (path, &extents);
+	_cairo_path_fixed_approximate_clip_extents (path, &extents);
 	is_empty = _cairo_rectangle_intersect (&surface->current_clip,
 					       &extents);
     }
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index f0f68a3..a64d524 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -145,7 +145,7 @@ _cairo_clip_path_intersect_to_rectangle (cairo_clip_path_t       *clip_path,
     while (clip_path) {
         cairo_rectangle_int_t extents;
 
-	_cairo_path_fixed_approximate_extents (&clip_path->path, &extents);
+	_cairo_path_fixed_approximate_clip_extents (&clip_path->path, &extents);
 
         if (! _cairo_rectangle_intersect (rectangle, &extents))
 	    return CAIRO_STATUS_SUCCESS;
@@ -577,7 +577,7 @@ _cairo_clip_intersect_mask_using_spans (cairo_clip_t       *clip,
     {
 	cairo_rectangle_int_t extents;
 
-	_cairo_path_fixed_approximate_extents (path, &extents);
+	_cairo_path_fixed_approximate_clip_extents (path, &extents);
 	if (! _cairo_rectangle_intersect (&surface_rect, &extents))
 	    goto SUCCESS;
 
diff --git a/src/cairo-path-bounds.c b/src/cairo-path-bounds.c
index 7047ca8..9f30eb5 100644
--- a/src/cairo-path-bounds.c
+++ b/src/cairo-path-bounds.c
@@ -156,8 +156,8 @@ _cairo_path_bounder_close_path (void *closure)
  * the control points of the curves, not the flattened path).
  */
 void
-_cairo_path_fixed_approximate_extents (cairo_path_fixed_t *path,
-				       cairo_rectangle_int_t *extents)
+_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t *path,
+					    cairo_rectangle_int_t *extents)
 {
     cairo_path_bounder_t bounder;
     cairo_status_t status;
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 6cc67d1..96ed2f9 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -957,7 +957,7 @@ _cairo_surface_fallback_fill (cairo_surface_t		*surface,
 	if (_cairo_operator_bounded_by_mask (op)) {
 	    cairo_rectangle_int_t path_extents;
 
-	    _cairo_path_fixed_approximate_fill_extents (path,
+	    _cairo_path_fixed_approximate_clip_extents (path,
 							&path_extents);
 	    if (! _cairo_rectangle_intersect (&extents, &path_extents))
 		return CAIRO_STATUS_SUCCESS;
diff --git a/src/cairoint.h b/src/cairoint.h
index a67dff5..6b38fd4 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1540,8 +1540,8 @@ _cairo_path_fixed_append (cairo_path_fixed_t		  *path,
 			  cairo_direction_t		   dir);
 
 cairo_private void
-_cairo_path_fixed_approximate_extents (cairo_path_fixed_t	*path,
-				       cairo_rectangle_int_t	*extents);
+_cairo_path_fixed_approximate_clip_extents (cairo_path_fixed_t	*path,
+					    cairo_rectangle_int_t *extents);
 
 cairo_private void
 _cairo_path_fixed_approximate_fill_extents (cairo_path_fixed_t *path,
commit 75f7c420b624049c1f6c51795679f8029cd2231d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Jan 21 13:09:41 2009 +0000

    [perf] Remove a redundant clear during source init.
    
    After a short wild goose chase to see why
    cairo_image_surface_fill_rectangles() was appearing in the profile,
    tweak init_and_set_source_surface() to remove the redundant clear and
    to propagate any errors in the auxiliary context.

diff --git a/perf/cairo-perf-cover.c b/perf/cairo-perf-cover.c
index 975c766..18a1588 100644
--- a/perf/cairo-perf-cover.c
+++ b/perf/cairo-perf-cover.c
@@ -35,9 +35,6 @@ init_and_set_source_surface (cairo_t		*cr,
 
     /* Fill it with something known */
     cr2 = cairo_create (source);
-    cairo_set_operator (cr2, CAIRO_OPERATOR_CLEAR);
-    cairo_paint (cr2);
-
     cairo_set_operator (cr2, CAIRO_OPERATOR_SOURCE);
     cairo_set_source_rgb (cr2, 0, 0, 1); /* blue */
     cairo_paint (cr2);
@@ -47,9 +44,9 @@ init_and_set_source_surface (cairo_t		*cr,
     cairo_rectangle (cr2, 0, 0, width/2.0, height/2.0);
     cairo_rectangle (cr2, width/2.0, height/2.0, width/2.0, height/2.0);
     cairo_fill (cr2);
-    cairo_destroy (cr2);
 
-    cairo_set_source_surface (cr, source, 0, 0);
+    cairo_set_source_surface (cr, cairo_get_target (cr2), 0, 0);
+    cairo_destroy (cr2);
 }
 
 static void
commit 706f6de68da65911f434d2065dcb143649fa793e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Sun Jan 18 16:47:59 2009 +0000

    [perf] Add another variation on the many-rectangles case
    
    This variation aims to show the difference between calling fill once
    per-rectangle, or once for all rectangles.

diff --git a/perf/rectangles.c b/perf/rectangles.c
index 991391c..374e364 100644
--- a/perf/rectangles.c
+++ b/perf/rectangles.c
@@ -60,6 +60,25 @@ do_rectangles (cairo_t *cr, int width, int height)
 }
 
 static cairo_perf_ticks_t
+do_rectangles_once (cairo_t *cr, int width, int height)
+{
+    int i;
+
+    cairo_perf_timer_start ();
+
+    for (i = 0; i < RECTANGLE_COUNT; i++)
+    {
+        cairo_rectangle (cr, rects[i].x, rects[i].y,
+                             rects[i].width, rects[i].height);
+    }
+    cairo_fill (cr);
+
+    cairo_perf_timer_stop ();
+
+    return cairo_perf_timer_elapsed ();
+}
+
+static cairo_perf_ticks_t
 do_rectangle (cairo_t *cr, int width, int height)
 {
     cairo_perf_timer_start ();
@@ -88,4 +107,5 @@ rectangles (cairo_perf_t *perf, cairo_t *cr, int width, int height)
 
     MODE (perf, "one-rectangle", do_rectangle);
     MODE (perf, "rectangles", do_rectangles);
+    MODE (perf, "rectangles-once", do_rectangles_once);
 }
commit ff5d37a8ad063e84e88f453a403715bc85f8a3ec
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jan 29 10:09:11 2009 +0000

    [mutex] Civilise the comment.
    
    Note bene that Behdad does not like people shouting.

diff --git a/src/cairo-mutex-impl-private.h b/src/cairo-mutex-impl-private.h
index ad65c11..9df14a4 100644
--- a/src/cairo-mutex-impl-private.h
+++ b/src/cairo-mutex-impl-private.h
@@ -55,7 +55,7 @@
 #define CAIRO_MUTEX_IMPL_NOOP	do {/*no-op*/} while (0)
 /* And one that evaluates it's argument once */
 #define CAIRO_MUTEX_IMPL_NOOP1(expr)        do { (void)(expr); } while (0)
-/* NOTE: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the
+/* Note: 'if (expr) {}' is an alternative to '(void)(expr);' that will 'use' the
  * result of __attribute__((warn_used_result)) functions. */
 
 /* Cairo mutex implementation:


More information about the cairo-commit mailing list