[cairo-commit] 8 commits - src/cairo-analysis-surface.c src/cairoint.h src/cairo-meta-surface.c src/cairo-pattern.c src/cairo-scaled-font.c src/cairo-scaled-font-subsets.c src/cairo-svg-surface.c src/cairo-xlib-private.h src/cairo-xlib-screen.c src/cairo-xlib-surface.c src/test-null-surface.c test/README test/show-glyphs-many.c

Chris Wilson ickle at kemper.freedesktop.org
Thu Jun 18 16:29:13 PDT 2009


 src/cairo-analysis-surface.c    |   57 +----------
 src/cairo-meta-surface.c        |   24 ++--
 src/cairo-pattern.c             |   17 +++
 src/cairo-scaled-font-subsets.c |    6 -
 src/cairo-scaled-font.c         |   23 +++-
 src/cairo-svg-surface.c         |    4 
 src/cairo-xlib-private.h        |   11 +-
 src/cairo-xlib-screen.c         |  200 ++++++++++++++++++++++++++++++----------
 src/cairo-xlib-surface.c        |   58 +++++++----
 src/cairoint.h                  |    3 
 src/test-null-surface.c         |  157 +++++++++++++++++++++++++++++++
 test/README                     |    9 +
 test/show-glyphs-many.c         |   19 ++-
 13 files changed, 430 insertions(+), 158 deletions(-)

New commits:
commit c12533b1358c9e5c1c3440efca4dc44ba2de9f6d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 17:46:14 2009 +0100

    [pattern] Add _cairo_pattern_fini_snapshot
    
    Symmetric operation to _cairo_pattern_init_snapshot() this exists simply
    to break the circular reference between the meta-surface and snapshot-cow.

diff --git a/src/cairo-meta-surface.c b/src/cairo-meta-surface.c
index d505adc..9f60007 100644
--- a/src/cairo-meta-surface.c
+++ b/src/cairo-meta-surface.c
@@ -134,31 +134,31 @@ _cairo_meta_surface_finish (void *abstract_surface)
 	/* 5 basic drawing operations */
 
 	case CAIRO_COMMAND_PAINT:
-	    _cairo_pattern_fini (&command->paint.source.base);
+	    _cairo_pattern_fini_snapshot (&command->paint.source.base);
 	    free (command);
 	    break;
 
 	case CAIRO_COMMAND_MASK:
-	    _cairo_pattern_fini (&command->mask.source.base);
-	    _cairo_pattern_fini (&command->mask.mask.base);
+	    _cairo_pattern_fini_snapshot (&command->mask.source.base);
+	    _cairo_pattern_fini_snapshot (&command->mask.mask.base);
 	    free (command);
 	    break;
 
 	case CAIRO_COMMAND_STROKE:
-	    _cairo_pattern_fini (&command->stroke.source.base);
+	    _cairo_pattern_fini_snapshot (&command->stroke.source.base);
 	    _cairo_path_fixed_fini (&command->stroke.path);
 	    _cairo_stroke_style_fini (&command->stroke.style);
 	    free (command);
 	    break;
 
 	case CAIRO_COMMAND_FILL:
-	    _cairo_pattern_fini (&command->fill.source.base);
+	    _cairo_pattern_fini_snapshot (&command->fill.source.base);
 	    _cairo_path_fixed_fini (&command->fill.path);
 	    free (command);
 	    break;
 
 	case CAIRO_COMMAND_SHOW_TEXT_GLYPHS:
-	    _cairo_pattern_fini (&command->show_text_glyphs.source.base);
+	    _cairo_pattern_fini_snapshot (&command->show_text_glyphs.source.base);
 	    free (command->show_text_glyphs.utf8);
 	    free (command->show_text_glyphs.glyphs);
 	    free (command->show_text_glyphs.clusters);
@@ -255,7 +255,7 @@ _cairo_meta_surface_paint (void			*abstract_surface,
     return CAIRO_STATUS_SUCCESS;
 
   CLEANUP_SOURCE:
-    _cairo_pattern_fini (&command->source.base);
+    _cairo_pattern_fini_snapshot (&command->source.base);
   CLEANUP_COMMAND:
     free (command);
     return status;
@@ -299,9 +299,9 @@ _cairo_meta_surface_mask (void			*abstract_surface,
     return CAIRO_STATUS_SUCCESS;
 
   CLEANUP_MASK:
-    _cairo_pattern_fini (&command->mask.base);
+    _cairo_pattern_fini_snapshot (&command->mask.base);
   CLEANUP_SOURCE:
-    _cairo_pattern_fini (&command->source.base);
+    _cairo_pattern_fini_snapshot (&command->source.base);
   CLEANUP_COMMAND:
     free (command);
     return status;
@@ -363,7 +363,7 @@ _cairo_meta_surface_stroke (void			*abstract_surface,
   CLEANUP_PATH:
     _cairo_path_fixed_fini (&command->path);
   CLEANUP_SOURCE:
-    _cairo_pattern_fini (&command->source.base);
+    _cairo_pattern_fini_snapshot (&command->source.base);
   CLEANUP_COMMAND:
     free (command);
     return status;
@@ -416,7 +416,7 @@ _cairo_meta_surface_fill (void			*abstract_surface,
   CLEANUP_PATH:
     _cairo_path_fixed_fini (&command->path);
   CLEANUP_SOURCE:
-    _cairo_pattern_fini (&command->source.base);
+    _cairo_pattern_fini_snapshot (&command->source.base);
   CLEANUP_COMMAND:
     free (command);
     return status;
@@ -511,7 +511,7 @@ _cairo_meta_surface_show_text_glyphs (void			    *abstract_surface,
     free (command->glyphs);
     free (command->clusters);
 
-    _cairo_pattern_fini (&command->source.base);
+    _cairo_pattern_fini_snapshot (&command->source.base);
   CLEANUP_COMMAND:
     free (command);
     return status;
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 38aa253..38db980 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -296,6 +296,23 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
 #endif
 }
 
+void
+_cairo_pattern_fini_snapshot (cairo_pattern_t *pattern)
+{
+    /* XXX this is quite ugly, but currently necessary to break the circular
+     * references with snapshot-cow and the meta-surface.
+     * This operation remains safe only whilst _cairo_surface_snapshot() is
+     * not public...
+     */
+    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) pattern;
+
+	cairo_surface_finish (spat->surface);
+    }
+
+    _cairo_pattern_fini (pattern);
+}
+
 cairo_status_t
 _cairo_pattern_create_copy (cairo_pattern_t	  **pattern_out,
 			    const cairo_pattern_t  *other)
diff --git a/src/cairoint.h b/src/cairoint.h
index f5968f3..6b12594 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2498,6 +2498,9 @@ _cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
 cairo_private void
 _cairo_pattern_fini (cairo_pattern_t *pattern);
 
+cairo_private void
+_cairo_pattern_fini_snapshot (cairo_pattern_t *pattern);
+
 cairo_private cairo_pattern_t *
 _cairo_pattern_create_solid (const cairo_color_t	*color,
 			     cairo_content_t		 content);
commit 2213c447373b600c1583b2edd24775c5c031231c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 17:14:53 2009 +0100

    [svg] Break circular snapshot reference by explicit finish
    
    The svg backend snapshots the meta surface which because of snapshot-cow
    creates a circular reference back to the creator. So in order to break the
    circular reference when we have finished with the snapshot, we need to
    call cairo_surface_finish() in addition to simply destroying the surface.

diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 7a35fab..d74e1f8 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1200,6 +1200,9 @@ _cairo_svg_surface_emit_meta_surface (cairo_svg_document_t *document,
     }
 
     meta = (cairo_meta_surface_t *) _cairo_surface_snapshot (&surface->base);
+    if (unlikely (meta->base.status))
+	return meta->base.status;
+
     paginated_surface = _cairo_svg_surface_create_for_document (document,
 								meta->content,
 								meta->width_pixels,
@@ -2655,6 +2658,7 @@ _cairo_svg_document_finish (cairo_svg_document_t *document)
 
     for (i = 0; i < document->meta_snapshots.num_elements; i++) {
 	snapshot = _cairo_array_index (&document->meta_snapshots, i);
+	cairo_surface_finish (&snapshot->meta->base);
 	status2 = cairo_surface_status (&snapshot->meta->base);
 	cairo_surface_destroy (&snapshot->meta->base);
 	if (status == CAIRO_STATUS_SUCCESS)
commit cd9eef1aad87558d3f2d47cfcfa2f5b9b254692c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 16:42:56 2009 +0100

    [scaled-font] Check the MRU font before resolving the implementor
    
    This should hide most of the overhead of resolving a frequently used toy font.

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index e342eb8..0510264 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -651,7 +651,7 @@ _cairo_scaled_font_matches (const cairo_scaled_font_t *scaled_font,
 			    const cairo_matrix_t *ctm,
 			    const cairo_font_options_t *options)
 {
-    return scaled_font->font_face == font_face &&
+    return scaled_font->original_font_face == font_face &&
 	    memcmp ((unsigned char *)(&scaled_font->font_matrix.xx),
 		    (unsigned char *)(&font_matrix->xx),
 		    sizeof(cairo_matrix_t)) == 0 &&
@@ -899,13 +899,6 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
     /* Note that degenerate ctm or font_matrix *are* allowed.
      * We want to support a font size of 0. */
 
-    if (font_face->backend->get_implementation != NULL) {
-	font_face = font_face->backend->get_implementation (font_face,
-							    font_matrix,
-							    ctm,
-							    options);
-    }
-
     font_map = _cairo_scaled_font_map_lock ();
     if (unlikely (font_map == NULL))
 	return _cairo_scaled_font_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
@@ -931,9 +924,23 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 	_cairo_hash_table_remove (font_map->hash_table,
 				  &scaled_font->hash_entry);
 	scaled_font->hash_entry.hash = ZOMBIE;
+
+	if (font_face->backend->get_implementation != NULL) {
+	    font_face = font_face->backend->get_implementation (font_face,
+								font_matrix,
+								ctm,
+								options);
+	}
     }
     else
     {
+	if (font_face->backend->get_implementation != NULL) {
+	    font_face = font_face->backend->get_implementation (font_face,
+								font_matrix,
+								ctm,
+								options);
+	}
+
 	_cairo_scaled_font_init_key (&key, font_face,
 				     font_matrix, ctm, options);
 
commit 5ad64dfda26f97f243f13e9b766567a30fda67a9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 16:39:02 2009 +0100

    [test] Add the sha1sum of the required fonts
    
    (Note, I think these are currently out-of-date... But it will serve as a
    useful reminder to verify and update them in future.)

diff --git a/test/README b/test/README
index c039ebb..7eca160 100644
--- a/test/README
+++ b/test/README
@@ -94,10 +94,11 @@ the software configuration used to generate the reference images.
 
 Here are some of the relevant details:
 
-  * Your system must have a copy of the Bitstream Vera font. These are
-      "Bitstream Vera Sans" (Vera.ttf);
-      "Bitstream Vera Sans Mono" (VeraMono.ttf);
-      "Bitstream Vera Serif" (VeraSe.ttf);
+  * Your system must have a copy of the Bitstream Vera font, the sha1sum of
+    the version used are listed in [...].  These are
+      "Bitstream Vera Sans" (Vera.ttf) [1d7f6fa0c34e3d50be52c16a0816bebac309fe41];
+      "Bitstream Vera Sans Mono" (VeraMono.ttf) [2c2e2ded3c903b5186f955871109f084edef4dbe];
+      "Bitstream Vera Serif" (VeraSe.ttf) [838645f09501d53647dd5edf5b961a8f8290613a];
     and also
       "Nimbus Sans L" (n019003l.pfb).
 
commit 3063f0f44c61869bc6bb07d46bf881f1558ac015
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 16:33:12 2009 +0100

    [scaled-font-subset] Check for matching font-face implementors
    
    Catch toy and user fonts which may have different font-faces for their
    implementation than the original ones they were created with.

diff --git a/src/cairo-scaled-font-subsets.c b/src/cairo-scaled-font-subsets.c
index 02b32aa..64c9d9a 100644
--- a/src/cairo-scaled-font-subsets.c
+++ b/src/cairo-scaled-font-subsets.c
@@ -230,11 +230,13 @@ _cairo_sub_fonts_equal (const void *key_a, const void *key_b)
 {
     const cairo_sub_font_t *sub_font_a = key_a;
     const cairo_sub_font_t *sub_font_b = key_b;
+    cairo_scaled_font_t *a = sub_font_a->scaled_font;
+    cairo_scaled_font_t *b = sub_font_b->scaled_font;
 
     if (sub_font_a->is_scaled)
-        return sub_font_a->scaled_font == sub_font_b->scaled_font;
+        return a == b;
     else
-        return sub_font_a->scaled_font->font_face == sub_font_b->scaled_font->font_face;
+	return a->font_face == b->font_face || a->original_font_face == b->original_font_face;
 }
 
 static void
commit 6e78c94615e407d72d4778d5558dc218e0402cbc
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 16:30:41 2009 +0100

    [test] Move calls to the getters from out of the innermost loops.
    
    Tidy the code slightly by removing excess and unsightly calls to
    cairo_test_get_context() and cairo_get_scaled_font().

diff --git a/test/show-glyphs-many.c b/test/show-glyphs-many.c
index dbfd6a1..f689fe0 100644
--- a/test/show-glyphs-many.c
+++ b/test/show-glyphs-many.c
@@ -80,7 +80,10 @@
 #define NUM_GLYPHS 65535
 
 static cairo_test_status_t
-get_glyph (cairo_t *cr, const char *utf8, cairo_glyph_t *glyph)
+get_glyph (const cairo_test_context_t *ctx,
+	   cairo_scaled_font_t *scaled_font,
+	   const char *utf8,
+	   cairo_glyph_t *glyph)
 {
     cairo_glyph_t *text_to_glyphs;
     cairo_status_t status;
@@ -88,15 +91,14 @@ get_glyph (cairo_t *cr, const char *utf8, cairo_glyph_t *glyph)
 
     text_to_glyphs = glyph;
     i = 1;
-    status = cairo_scaled_font_text_to_glyphs (cairo_get_scaled_font (cr),
+    status = cairo_scaled_font_text_to_glyphs (scaled_font,
 					       0, 0,
 					       utf8, -1,
 					       &text_to_glyphs, &i,
 					       NULL, NULL,
 					       0);
     if (status != CAIRO_STATUS_SUCCESS)
-	return cairo_test_status_from_status (cairo_test_get_context (cr),
-					      status);
+	return cairo_test_status_from_status (ctx, status);
 
     if (text_to_glyphs != glyph) {
 	*glyph = text_to_glyphs[0];
@@ -109,7 +111,9 @@ get_glyph (cairo_t *cr, const char *utf8, cairo_glyph_t *glyph)
 static cairo_test_status_t
 draw (cairo_t *cr, int width, int height)
 {
+    const cairo_test_context_t *ctx = cairo_test_get_context (cr);
     cairo_glyph_t *glyphs = xmalloc (NUM_GLYPHS * sizeof (cairo_glyph_t));
+    cairo_scaled_font_t *scaled_font;
     const char *characters[] = { /* try to exercise different widths of index */
 	"m", /* Latin letter m, index=0x50 */
 	"μ", /* Greek letter mu, index=0x349 */
@@ -126,9 +130,10 @@ draw (cairo_t *cr, int width, int height)
 			    CAIRO_FONT_SLANT_NORMAL,
 			    CAIRO_FONT_WEIGHT_NORMAL);
     cairo_set_font_size (cr, TEXT_SIZE);
+    scaled_font = cairo_get_scaled_font (cr);
 
     for (utf8 = characters; *utf8 != NULL; utf8++) {
-	status = get_glyph (cr, *utf8, &glyphs[0]);
+	status = get_glyph (ctx, scaled_font, *utf8, &glyphs[0]);
 	if (status)
 	    goto BAIL;
 
@@ -143,13 +148,13 @@ draw (cairo_t *cr, int width, int height)
     }
 
     /* we can pack ~21k 1-byte glyphs into a single XRenderCompositeGlyphs8 */
-    status = get_glyph (cr, "m", &glyphs[0]);
+    status = get_glyph (ctx, scaled_font, "m", &glyphs[0]);
     if (status)
 	goto BAIL;
     for (i=1; i < 21500; i++)
 	glyphs[i] = glyphs[0];
     /* so check expanding the current 1-byte request for 2-byte glyphs */
-    status = get_glyph (cr, "μ", &glyphs[i]);
+    status = get_glyph (ctx, scaled_font, "μ", &glyphs[i]);
     if (status)
 	goto BAIL;
     for (j=i+1; j < NUM_GLYPHS; j++)
commit 3da32e35afa57533379c3de35459f23aef7c3d04
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 15:31:27 2009 +0100

    [analysis] Restore nops for the analysis null surface
    
    Joonas reported that adding the extra routines to the null-surface as used
    by the analysis surface broke user-fonts. So create a separate null
    backend to be exported via the test-null surface.

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index b0cc54c..3a1b2f9 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -874,12 +874,7 @@ _return_success (void)
 typedef cairo_int_status_t
 (*_set_clip_region_func)	(void			*surface,
 				 cairo_region_t		*region);
-typedef cairo_int_status_t
-(*_intersect_clip_path_func)	(void			*dst,
-				 cairo_path_fixed_t	*path,
-				 cairo_fill_rule_t	fill_rule,
-				 double			tolerance,
-				 cairo_antialias_t	antialias);
+
 typedef cairo_int_status_t
 (*_paint_func)			(void			*surface,
 			         cairo_operator_t	 op,
@@ -925,50 +920,10 @@ typedef cairo_int_status_t
 				 int			*remaining_glyphs,
 				 cairo_rectangle_int_t  *extents);
 
-typedef cairo_int_status_t
-(*_show_text_glyphs_func)	(void			    *surface,
-				 cairo_operator_t	     op,
-				 const cairo_pattern_t	    *source,
-				 const char		    *utf8,
-				 int			     utf8_len,
-				 cairo_glyph_t		    *glyphs,
-				 int			     num_glyphs,
-				 const cairo_text_cluster_t *clusters,
-				 int			     num_clusters,
-				 cairo_text_cluster_flags_t  cluster_flags,
-				 cairo_scaled_font_t	    *scaled_font,
-				 cairo_rectangle_int_t	    *extents);
-
-static cairo_surface_t *
-_cairo_null_surface_create_similar (void *other,
-				    cairo_content_t content,
-				    int width, int height)
-{
-    return _cairo_null_surface_create (content);
-}
-
-static cairo_int_status_t
-_cairo_null_surface_get_extents (void *surface,
-				 cairo_rectangle_int_t *extents)
-{
-    extents->x = 0;
-    extents->y = 0;
-    extents->width = 0;
-    extents->height = 0;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_bool_t
-_cairo_null_surface_has_show_text_glyphs (void *surface)
-{
-    return TRUE;
-}
-
 static const cairo_surface_backend_t cairo_null_surface_backend = {
     CAIRO_INTERNAL_SURFACE_TYPE_NULL,
 
-    _cairo_null_surface_create_similar,
+    NULL, /* create_similar */
     NULL, /* finish */
     NULL, /* acquire_source_image */
     NULL, /* release_source_image */
@@ -983,8 +938,8 @@ static const cairo_surface_backend_t cairo_null_surface_backend = {
     NULL, /* copy_page */
     NULL, /* show_page */
     (_set_clip_region_func) _return_success, /* set_clip_region */
-    (_intersect_clip_path_func) _return_success, /* intersect_clip_path */
-    _cairo_null_surface_get_extents,
+    NULL, /* intersect_clip_path */
+    NULL, /* get_extents */
     NULL, /* old_show_glyphs */
     NULL, /* get_font_options */
     NULL, /* flush */
@@ -1002,8 +957,8 @@ static const cairo_surface_backend_t cairo_null_surface_backend = {
     NULL, /* fill_stroke */
     NULL, /* create_solid_pattern_surface */
     NULL, /* can_repaint_solid_pattern_surface */
-    _cairo_null_surface_has_show_text_glyphs,
-    (_show_text_glyphs_func) _return_success,    /* show_text_glyphs */
+    NULL, /* has_show_text_glyphs */
+    NULL  /* show_text_glyphs */
 };
 
 cairo_surface_t *
diff --git a/src/test-null-surface.c b/src/test-null-surface.c
index 3e470c5..2c71f85 100644
--- a/src/test-null-surface.c
+++ b/src/test-null-surface.c
@@ -39,10 +39,163 @@
 #include "cairoint.h"
 
 #include "test-null-surface.h"
-#include "cairo-analysis-surface-private.h"
+
+slim_hidden_proto (_cairo_test_null_surface_create);
+
+static cairo_int_status_t
+_return_success (void)
+{
+    return CAIRO_STATUS_SUCCESS;
+}
+
+/* These typedefs are just to silence the compiler... */
+typedef cairo_int_status_t
+(*_set_clip_region_func)	(void			*surface,
+				 cairo_region_t		*region);
+typedef cairo_int_status_t
+(*_intersect_clip_path_func)	(void			*dst,
+				 cairo_path_fixed_t	*path,
+				 cairo_fill_rule_t	fill_rule,
+				 double			tolerance,
+				 cairo_antialias_t	antialias);
+typedef cairo_int_status_t
+(*_paint_func)			(void			*surface,
+			         cairo_operator_t	 op,
+				 const cairo_pattern_t	*source,
+				 cairo_rectangle_int_t  *extents);
+
+typedef cairo_int_status_t
+(*_mask_func)			(void			*surface,
+			         cairo_operator_t	 op,
+				 const cairo_pattern_t	*source,
+				 const cairo_pattern_t	*mask,
+				 cairo_rectangle_int_t  *extents);
+
+typedef cairo_int_status_t
+(*_stroke_func)			(void			*surface,
+			         cairo_operator_t	 op,
+				 const cairo_pattern_t	*source,
+				 cairo_path_fixed_t	*path,
+				 cairo_stroke_style_t	*style,
+				 cairo_matrix_t		*ctm,
+				 cairo_matrix_t		*ctm_inverse,
+				 double			 tolerance,
+				 cairo_antialias_t	 antialias,
+				 cairo_rectangle_int_t  *extents);
+
+typedef cairo_int_status_t
+(*_fill_func)			(void			*surface,
+			         cairo_operator_t	 op,
+				 const cairo_pattern_t	*source,
+				 cairo_path_fixed_t	*path,
+				 cairo_fill_rule_t	 fill_rule,
+				 double			 tolerance,
+				 cairo_antialias_t	 antialias,
+				 cairo_rectangle_int_t  *extents);
+
+typedef cairo_int_status_t
+(*_show_glyphs_func)		(void			*surface,
+			         cairo_operator_t	 op,
+				 const cairo_pattern_t	*source,
+				 cairo_glyph_t		*glyphs,
+				 int			 num_glyphs,
+				 cairo_scaled_font_t	*scaled_font,
+				 int			*remaining_glyphs,
+				 cairo_rectangle_int_t  *extents);
+
+typedef cairo_int_status_t
+(*_show_text_glyphs_func)	(void			    *surface,
+				 cairo_operator_t	     op,
+				 const cairo_pattern_t	    *source,
+				 const char		    *utf8,
+				 int			     utf8_len,
+				 cairo_glyph_t		    *glyphs,
+				 int			     num_glyphs,
+				 const cairo_text_cluster_t *clusters,
+				 int			     num_clusters,
+				 cairo_text_cluster_flags_t  cluster_flags,
+				 cairo_scaled_font_t	    *scaled_font,
+				 cairo_rectangle_int_t	    *extents);
+
+static cairo_surface_t *
+_cairo_null_surface_create_similar (void *other,
+				    cairo_content_t content,
+				    int width, int height)
+{
+    return _cairo_test_null_surface_create (content);
+}
+
+static cairo_int_status_t
+_cairo_null_surface_get_extents (void *surface,
+				 cairo_rectangle_int_t *extents)
+{
+    extents->x = 0;
+    extents->y = 0;
+    extents->width = 0;
+    extents->height = 0;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_null_surface_has_show_text_glyphs (void *surface)
+{
+    return TRUE;
+}
+
+static const cairo_surface_backend_t null_surface_backend = {
+    CAIRO_INTERNAL_SURFACE_TYPE_NULL,
+
+    _cairo_null_surface_create_similar,
+    NULL, /* finish */
+    NULL, /* acquire_source_image */
+    NULL, /* release_source_image */
+    NULL, /* acquire_dest_image */
+    NULL, /* release_dest_image */
+    NULL, /* clone_similar */
+    NULL, /* composite */
+    NULL, /* fill_rectangles */
+    NULL, /* composite_trapezoids */
+    NULL, /* create_span_renderer */
+    NULL, /* check_span_renderer */
+    NULL, /* copy_page */
+    NULL, /* show_page */
+    (_set_clip_region_func) _return_success, /* set_clip_region */
+    (_intersect_clip_path_func) _return_success, /* intersect_clip_path */
+    _cairo_null_surface_get_extents,
+    NULL, /* old_show_glyphs */
+    NULL, /* get_font_options */
+    NULL, /* flush */
+    NULL, /* mark_dirty_rectangle */
+    NULL, /* scaled_font_fini */
+    NULL, /* scaled_glyph_fini */
+    (_paint_func) _return_success,	    /* paint */
+    (_mask_func) _return_success,	    /* mask */
+    (_stroke_func) _return_success,	    /* stroke */
+    (_fill_func) _return_success,	    /* fill */
+    (_show_glyphs_func) _return_success,    /* show_glyphs */
+    NULL, /* snapshot */
+    NULL, /* is_similar */
+    NULL, /* reset */
+    NULL, /* fill_stroke */
+    NULL, /* create_solid_pattern_surface */
+    NULL, /* can_repaint_solid_pattern_surface */
+    _cairo_null_surface_has_show_text_glyphs,
+    (_show_text_glyphs_func) _return_success,    /* show_text_glyphs */
+};
 
 cairo_surface_t *
 _cairo_test_null_surface_create (cairo_content_t content)
 {
-    return _cairo_null_surface_create (content);
+    cairo_surface_t *surface;
+
+    surface = malloc (sizeof (cairo_surface_t));
+    if (unlikely (surface == NULL)) {
+	return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
+    }
+
+    _cairo_surface_init (surface, &null_surface_backend, content);
+
+    return surface;
 }
+slim_hidden_def (_cairo_test_null_surface_create);
commit c0e01d9cd71bd958e1b31a03cea4c08a1bdf4926
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu Jun 18 14:32:53 2009 +0100

    [xlib] Improve GC caching efficacy
    
    Shrink the overall size of the per-screen GC cache, but allow multiple GCs
    per depth, as it quite common to need up to two temporary GCs along some
    drawing paths. Decrease the number of GCs we obtain in total by returning
    clean (i.e. a GC without a clip set) back to the screen pool after use.
    Compensate for the increased number of put/get by performing the query
    using atomic operations where available. So overall we see a dramatic
    reduction on the numbers of XCreateGC and XFreeGC, of even greater benefit
    for RENDER-less servers.

diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 4995bc6..c79617b 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -103,8 +103,8 @@ struct _cairo_xlib_screen_info {
     cairo_bool_t has_font_options;
     cairo_font_options_t font_options;
 
-    GC gc[9];
-    unsigned int gc_needs_clip_reset;
+    GC gc[4];
+    int gc_depths; /* 4 x uint8_t, high bit == needs reset */
 
     cairo_array_t visuals;
 };
@@ -154,12 +154,13 @@ _cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info);
 
 cairo_private GC
 _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info,
-			   int depth,
+			   unsigned int depth,
+			   Drawable drawable,
 			   unsigned int *need_reset);
 
-cairo_private cairo_status_t
+cairo_private void
 _cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info,
-			   int depth,
+			   unsigned int depth,
 			   GC gc,
 			   cairo_bool_t reset_clip);
 
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index 8f1e949..e491a33 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -268,19 +268,29 @@ void
 _cairo_xlib_screen_info_close_display (cairo_xlib_screen_info_t *info)
 {
     cairo_xlib_visual_info_t **visuals;
-    int i;
+    Display *dpy;
+    int i, old;
 
     CAIRO_MUTEX_LOCK (info->mutex);
+
+    dpy = info->display->display;
+
+#if HAS_ATOMIC_OPS
+    do {
+	old = info->gc_depths;
+    } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, 0) != old);
+#else
+    old = info->gc_depths;
+#endif
+
     for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
-	if (info->gc[i] != NULL) {
-	    XFreeGC (info->display->display, info->gc[i]);
-	    info->gc[i] = NULL;
-	}
+	if (old >> (8*i) & 0x7f)
+	    XFreeGC (dpy, info->gc[i]);
     }
 
     visuals = _cairo_array_index (&info->visuals, 0);
     for (i = 0; i < _cairo_array_num_elements (&info->visuals); i++)
-	_cairo_xlib_visual_info_destroy (info->display->display, visuals[i]);
+	_cairo_xlib_visual_info_destroy (dpy, visuals[i]);
     _cairo_array_truncate (&info->visuals, 0);
 
     CAIRO_MUTEX_UNLOCK (info->mutex);
@@ -358,8 +368,8 @@ _cairo_xlib_screen_info_get (cairo_xlib_display_t *display,
 	info->screen = screen;
 	info->has_render = FALSE;
 	info->has_font_options = FALSE;
+	info->gc_depths = 0;
 	memset (info->gc, 0, sizeof (info->gc));
-	info->gc_needs_clip_reset = 0;
 
 	_cairo_array_init (&info->visuals,
 			   sizeof (cairo_xlib_visual_info_t*));
@@ -386,71 +396,165 @@ _cairo_xlib_screen_info_get (cairo_xlib_display_t *display,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static int
-depth_to_index (int depth)
+#if HAS_ATOMIC_OPS
+GC
+_cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info,
+			   unsigned int depth,
+			   Drawable drawable,
+			   unsigned int *dirty)
 {
-    switch(depth){
-	case 1:  return 1;
-	case 8:  return 2;
-	case 12: return 3;
-	case 15: return 4;
-	case 16: return 5;
-	case 24: return 6;
-	case 30: return 7;
-	case 32: return 8;
+    XGCValues gcv;
+    int i, new, old;
+    GC gc;
+
+    do {
+	gc = NULL;
+	old = info->gc_depths;
+	if (old == 0)
+	    break;
+
+	if (((old >> 0) & 0x7f) == depth)
+	    i = 0;
+	else if (((old >> 8) & 0x7f) == depth)
+	    i = 1;
+	else if (((old >> 16) & 0x7f) == depth)
+	    i = 2;
+	else if (((old >> 24) & 0x7f) == depth)
+	    i = 3;
+	else
+	    break;
+
+	gc = info->gc[i];
+	new = old & ~(0xff << (8*i));
+    } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old);
+
+    if (likely (gc != NULL)) {
+	(void) _cairo_atomic_ptr_cmpxchg (&info->gc[i], gc, NULL);
+
+	if (old & 0x80 << (8 * i))
+	    *dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC;
+
+	return gc;
     }
-    return 0;
+
+    gcv.graphics_exposures = False;
+    return XCreateGC (info->display->display, drawable,
+		      GCGraphicsExposures, &gcv);
 }
 
+void
+_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info,
+			   unsigned int depth,
+			   GC gc,
+			   cairo_bool_t reset_clip)
+{
+    int i, old, new;
+
+    depth |= reset_clip ? 0x80 : 0;
+    do {
+	do {
+	    i = -1;
+	    old = info->gc_depths;
+
+	    if (((old >> 0) & 0x7f) == 0)
+		i = 0;
+	    else if (((old >> 8) & 0x7f) == 0)
+		i = 1;
+	    else if (((old >> 16) & 0x7f) == 0)
+		i = 2;
+	    else if (((old >> 24) & 0x7f) == 0)
+		i = 3;
+	    else
+		goto out;
+
+	    new = old | (depth << (8*i));
+	} while (_cairo_atomic_ptr_cmpxchg (&info->gc[i], NULL, gc) != NULL);
+    } while (_cairo_atomic_int_cmpxchg (&info->gc_depths, old, new) != old);
+
+    return;
+
+out:
+    if (unlikely (_cairo_xlib_display_queue_work (info->display,
+				(cairo_xlib_notify_func) XFreeGC,
+				gc,
+				NULL)))
+    {
+	/* leak the server side resource... */
+	XFree ((char *) gc);
+    }
+}
+#else
 GC
 _cairo_xlib_screen_get_gc (cairo_xlib_screen_info_t *info,
-			   int depth,
+			   unsigned int depth,
+			   Drawable drawable,
 			   unsigned int *dirty)
 {
-    GC gc;
-    cairo_bool_t needs_reset;
-
-    depth = depth_to_index (depth);
+    GC gc = NULL;
+    int i;
 
     CAIRO_MUTEX_LOCK (info->mutex);
-    gc = info->gc[depth];
-    info->gc[depth] = NULL;
-    needs_reset = info->gc_needs_clip_reset & (1 << depth);
-    info->gc_needs_clip_reset &= ~(1 << depth);
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+	if (((info->gc_depths >> (8*i)) & 0x7f) == depth) {
+	    if (info->gc_depths & 0x80 << (8*i))
+		*dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC;
+
+	    info->gc_depths &= ~(0xff << (8*i));
+	    gc = info->gc[i];
+	    break;
+	}
+    }
     CAIRO_MUTEX_UNLOCK (info->mutex);
 
-    if (needs_reset)
-	*dirty |= CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC;
+    if (gc == NULL) {
+	XGCValues gcv;
+
+	gcv.graphics_exposures = False;
+	gc = XCreateGC (info->display->display, drawable,
+			GCGraphicsExposures, &gcv);
+    }
 
     return gc;
 }
 
-cairo_status_t
-_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info, int depth, GC gc, cairo_bool_t reset_clip)
+void
+_cairo_xlib_screen_put_gc (cairo_xlib_screen_info_t *info,
+			   unsigned int depth,
+			   GC gc,
+			   cairo_bool_t reset_clip)
 {
-    cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    GC oldgc;
+    int i;
 
-    depth = depth_to_index (depth);
+    depth |= reset_clip ? 0x80 : 0;
 
     CAIRO_MUTEX_LOCK (info->mutex);
-    oldgc = info->gc[depth];
-    info->gc[depth] = gc;
-    if (reset_clip)
-	info->gc_needs_clip_reset |= 1 << depth;
-    else
-	info->gc_needs_clip_reset &= ~(1 << depth);
-    CAIRO_MUTEX_UNLOCK (info->mutex);
+    for (i = 0; i < ARRAY_LENGTH (info->gc); i++) {
+	if (((info->gc_depths >> (8*i)) & 0x7f) == 0)
+	    break;
+    }
 
-    if (oldgc != NULL) {
-	status = _cairo_xlib_display_queue_work (info->display,
-		                               (cairo_xlib_notify_func) XFreeGC,
-					       oldgc,
-					       NULL);
+    if (i == ARRAY_LENGTH (info->gc)) {
+	cairo_status_t status;
+
+	/* perform random substitution to ensure fair caching over depths */
+	i = rand () % ARRAY_LENGTH (info->gc);
+	status =
+	    _cairo_xlib_display_queue_work (info->display,
+					    (cairo_xlib_notify_func) XFreeGC,
+					    info->gc[i],
+					    NULL);
+	if (unlikely (status)) {
+	    /* leak the server side resource... */
+	    XFree ((char *) info->gc[i]);
+	}
     }
 
-    return status;
+    info->gc[i] = gc;
+    info->gc_depths &= ~(0xff << (8*i));
+    info->gc_depths |= depth << (8*i);
+    CAIRO_MUTEX_UNLOCK (info->mutex);
 }
+#endif
 
 cairo_status_t
 _cairo_xlib_screen_get_visual_info (cairo_xlib_screen_info_t *info,
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 4fa5d2a..b68e6ed 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -68,6 +68,9 @@ static cairo_status_t
 _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface);
 
 static void
+_cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface);
+
+static void
 _cairo_xlib_surface_ensure_src_picture (cairo_xlib_surface_t *surface);
 
 static void
@@ -305,14 +308,11 @@ _cairo_xlib_surface_finish (void *abstract_surface)
     }
 
     if (surface->gc != NULL) {
-	cairo_status_t status2;
-	status2 = _cairo_xlib_screen_put_gc (surface->screen_info,
-		                             surface->depth,
-				             surface->gc,
-				             surface->gc_has_clip_rects);
+	_cairo_xlib_screen_put_gc (surface->screen_info,
+				   surface->depth,
+				   surface->gc,
+				   surface->gc_has_clip_rects);
 	surface->gc = NULL;
-	if (status == CAIRO_STATUS_SUCCESS)
-	    status = status2;
     }
 
     if (surface->clip_rects != surface->embedded_clip_rects)
@@ -662,8 +662,7 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 	ximage = NULL;
     }
 
-    if (!ximage)
-    {
+    if (!ximage) {
 
 	/* XGetImage from a window is dangerous because it can
 	 * produce errors if the window is unmapped or partially
@@ -695,9 +694,12 @@ _get_image_surface (cairo_xlib_surface_t    *surface,
 
 	    XFreePixmap (surface->dpy, pixmap);
 	}
+
+	if (!ximage)
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+	_cairo_xlib_surface_maybe_put_gc (surface);
     }
-    if (!ximage)
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
     _swap_ximage_to_native (ximage);
 
@@ -902,19 +904,14 @@ _cairo_xlib_surface_ensure_dst_picture (cairo_xlib_surface_t    *surface)
 static cairo_status_t
 _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface)
 {
-    XGCValues gcv;
 
     if (surface->gc == NULL) {
 	surface->gc = _cairo_xlib_screen_get_gc (surface->screen_info,
 						 surface->depth,
+						 surface->drawable,
 						 &surface->clip_dirty);
-	if (surface->gc == NULL) {
-	    gcv.graphics_exposures = False;
-	    surface->gc = XCreateGC (surface->dpy, surface->drawable,
-				     GCGraphicsExposures, &gcv);
-	    if (unlikely (surface->gc == NULL))
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	}
+	if (unlikely (surface->gc == NULL))
+	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
     if (surface->clip_dirty & CAIRO_XLIB_SURFACE_CLIP_DIRTY_GC)
@@ -923,6 +920,21 @@ _cairo_xlib_surface_ensure_gc (cairo_xlib_surface_t *surface)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+_cairo_xlib_surface_maybe_put_gc (cairo_xlib_surface_t *surface)
+{
+    /* return the GC back to the common pool if clean */
+
+    if (surface->gc_has_clip_rects)
+	return;
+
+    _cairo_xlib_screen_put_gc (surface->screen_info,
+			       surface->depth,
+			       surface->gc,
+			       FALSE);
+    surface->gc = NULL;
+}
+
 static cairo_status_t
 _draw_image_surface (cairo_xlib_surface_t   *surface,
 		     cairo_image_surface_t  *image,
@@ -1081,6 +1093,8 @@ _draw_image_surface (cairo_xlib_surface_t   *surface,
 	      &ximage, src_x, src_y, dst_x, dst_y,
 	      width, height);
 
+    _cairo_xlib_surface_maybe_put_gc (surface);
+
   BAIL:
     if (own_data)
 	free (ximage.data);
@@ -1870,6 +1884,8 @@ _cairo_xlib_surface_composite (cairo_operator_t		op,
 		   src_y + src_attr.y_offset + ity,
 		   width, height,
 		   dst_x, dst_y);
+
+	_cairo_xlib_surface_maybe_put_gc (dst);
 	break;
 
     case DO_XTILE:
@@ -1896,6 +1912,8 @@ _cairo_xlib_surface_composite (cairo_operator_t		op,
 
 	XFillRectangle (dst->dpy, dst->drawable, dst->gc,
 			dst_x, dst_y, width, height);
+
+	_cairo_xlib_surface_maybe_put_gc (dst);
 	break;
 
     case DO_UNSUPPORTED:
@@ -1969,6 +1987,8 @@ _cairo_xlib_surface_solid_fill_rectangles (cairo_xlib_surface_t    *surface,
 			rects[i].width, rects[i].height);
     }
 
+    _cairo_xlib_surface_maybe_put_gc (surface);
+
   BAIL:
     _cairo_pattern_release_surface (&solid.base, solid_surface, &attrs);
 


More information about the cairo-commit mailing list