[cairo-commit] 10 commits - configure.in src/cairo-arc.c src/cairo-pattern.c src/cairo-scaled-font.c src/cairo-scaled-font-private.h src/cairo-xlib-display.c src/cairo-xlib-private.h src/cairo-xlib-surface.c test/cairo-test.c test/degenerate-arc.c test/degenerate-arc-ps-ref.png test/degenerate-arc-ref.png test/.gitignore test/invalid-matrix.c test/Makefile.am

Chris Wilson ickle at kemper.freedesktop.org
Wed May 7 00:22:20 PDT 2008


 configure.in                    |   11 +
 src/cairo-arc.c                 |    2 
 src/cairo-pattern.c             |   62 ++++++++--
 src/cairo-scaled-font-private.h |    2 
 src/cairo-scaled-font.c         |    5 
 src/cairo-xlib-display.c        |   35 +++++
 src/cairo-xlib-private.h        |    7 +
 src/cairo-xlib-surface.c        |  244 +++++++++++++++++++++++-----------------
 test/.gitignore                 |    1 
 test/Makefile.am                |    3 
 test/cairo-test.c               |    7 +
 test/degenerate-arc-ps-ref.png  |binary
 test/degenerate-arc-ref.png     |binary
 test/degenerate-arc.c           |   82 +++++++++++++
 test/invalid-matrix.c           |    9 +
 15 files changed, 354 insertions(+), 116 deletions(-)

New commits:
commit b7272e9e8e716b04752058855aeb74c42af0b395
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 1 19:46:58 2008 +0100

    [cairo-xlib] Batch XRenderFreeGlyphs
    
    For every glyph evicted from the cache we would allocate a very small
    structure to push onto the xlib work queue. This quickly becomes
    noticably for an app that consumes a lot of scaled fonts and glyphs,
    e.g. trying to draw text at various angles. So we maintain a small array
    of glyphs pending finalisation and issue the XRenderFreeGlyphs() once the
    array is full.

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index c5e6371..58ac34c 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -2673,10 +2673,17 @@ enum {
   NUM_GLYPHSETS
 };
 
+typedef struct _cairo_xlib_font_glyphset_free_glyphs {
+    GlyphSet		glyphset;
+    int			glyph_count;
+    unsigned long	glyph_indices[128];
+} cairo_xlib_font_glyphset_free_glyphs_t;
+
 typedef struct _cairo_xlib_font_glyphset_info {
     GlyphSet		glyphset;
     cairo_format_t	format;
     XRenderPictFormat	*xrender_format;
+    cairo_xlib_font_glyphset_free_glyphs_t *pending_free_glyphs;
 } cairo_xlib_font_glyphset_info_t;
 
 typedef struct _cairo_xlib_surface_font_private {
@@ -2745,6 +2752,7 @@ _cairo_xlib_surface_font_init (Display		    *dpy,
 	}
 	glyphset_info->xrender_format = NULL;
 	glyphset_info->glyphset = None;
+	glyphset_info->pending_free_glyphs = NULL;
     }
 
     scaled_font->surface_private = font_private;
@@ -2770,6 +2778,10 @@ _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
 	    cairo_xlib_font_glyphset_info_t *glyphset_info;
 
 	    glyphset_info = &font_private->glyphset_info[i];
+
+	    if (glyphset_info->pending_free_glyphs != NULL)
+		free (glyphset_info->pending_free_glyphs);
+
 	    if (glyphset_info->glyphset) {
 		cairo_status_t status;
 
@@ -2785,15 +2797,14 @@ _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
     }
 }
 
-struct _cairo_xlib_render_free_glyphs {
-    GlyphSet glyphset;
-    unsigned long glyph_index;
-};
-static void _cairo_xlib_render_free_glyphs (Display *dpy, struct _cairo_xlib_render_free_glyphs *arg)
+static void
+_cairo_xlib_render_free_glyphs (Display *dpy,
+	                        cairo_xlib_font_glyphset_free_glyphs_t *to_free)
 {
     XRenderFreeGlyphs (dpy,
-	               arg->glyphset,
-	               &arg->glyph_index, 1);
+	               to_free->glyphset,
+	               to_free->glyph_indices,
+		       to_free->glyph_count);
 }
 
 static cairo_xlib_font_glyphset_info_t *
@@ -2822,26 +2833,38 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
     font_private = scaled_font->surface_private;
     glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
     if (font_private != NULL && glyphset_info != NULL) {
-	cairo_xlib_display_t *display;
-	struct _cairo_xlib_render_free_glyphs *arg;
-
-	display = font_private->display;
-	arg = malloc (sizeof (*arg));
-	if (arg != NULL) {
-	    cairo_status_t status;
-
-	    arg->glyphset = glyphset_info->glyphset;
-	    arg->glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+	cairo_xlib_font_glyphset_free_glyphs_t *to_free;
+	cairo_status_t status;
 
-	    status = _cairo_xlib_display_queue_work (display,
+	to_free = glyphset_info->pending_free_glyphs;
+	if (to_free != NULL &&
+	    to_free->glyph_count == ARRAY_LENGTH (to_free->glyph_indices))
+	{
+	    status = _cairo_xlib_display_queue_work (font_private->display,
 		    (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
-		    arg,
+		    to_free,
 		    free);
-	    if (status) {
-		/* XXX cannot propagate failure */
-		free (arg);
+	    /* XXX cannot propagate failure */
+	    if (status)
+		free (to_free);
+
+	    to_free = glyphset_info->pending_free_glyphs = NULL;
+	}
+
+	if (to_free == NULL) {
+	    to_free = malloc (sizeof (cairo_xlib_font_glyphset_free_glyphs_t));
+	    if (to_free == NULL) {
+		_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+		return; /* XXX cannot propagate failure */
 	    }
+
+	    to_free->glyphset = glyphset_info->glyphset;
+	    to_free->glyph_count = 0;
+	    glyphset_info->pending_free_glyphs = to_free;
 	}
+
+	to_free->glyph_indices[to_free->glyph_count++] =
+	    _cairo_scaled_glyph_index (scaled_glyph);
     }
 }
 
@@ -2899,9 +2922,11 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
     cairo_bool_t already_had_glyph_surface;
     cairo_xlib_font_glyphset_info_t *glyphset_info;
 
+    glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
+
     if (!glyph_surface) {
 	status = _cairo_scaled_glyph_lookup (scaled_font,
-					     _cairo_scaled_glyph_index (scaled_glyph),
+					     glyph_index,
 					     CAIRO_SCALED_GLYPH_INFO_METRICS |
 					     CAIRO_SCALED_GLYPH_INFO_SURFACE,
 					     pscaled_glyph);
@@ -2939,6 +2964,23 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
     glyphset_info = _cairo_xlib_scaled_font_get_glyphset_info_for_format (scaled_font,
 									  glyph_surface->format);
 
+    /* check to see if we have a pending XRenderFreeGlyph for this glyph */
+    if (glyphset_info->pending_free_glyphs != NULL) {
+	cairo_xlib_font_glyphset_free_glyphs_t *to_free;
+	int i;
+
+	to_free = glyphset_info->pending_free_glyphs;
+	for (i = 0; i < to_free->glyph_count; i++) {
+	    if (to_free->glyph_indices[i] == glyph_index) {
+		to_free->glyph_count--;
+		memmove (&to_free->glyph_indices[i],
+			 &to_free->glyph_indices[i+1],
+			 (to_free->glyph_count - i) * sizeof (to_free->glyph_indices));
+		goto DONE;
+	    }
+	}
+    }
+
     /* If the glyph surface has zero height or width, we create
      * a clear 1x1 surface, to avoid various X server bugs.
      */
@@ -3067,18 +3109,17 @@ _cairo_xlib_surface_add_glyph (Display *dpy,
     }
     /* XXX assume X server wants pixman padding. Xft assumes this as well */
 
-    glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
-
     XRenderAddGlyphs (dpy, glyphset_info->glyphset,
 		      &glyph_index, &glyph_info, 1,
 		      (char *) data,
 		      glyph_surface->stride * glyph_surface->height);
 
-    _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
-
     if (data != glyph_surface->data)
 	free (data);
 
+ DONE:
+    _cairo_xlib_scaled_glyph_set_glyphset_info (scaled_glyph, glyphset_info);
+
  BAIL:
     if (glyph_surface != scaled_glyph->surface)
 	cairo_surface_destroy (&glyph_surface->base);
commit 3428acf25db6c67ca48d619e98406a6bd4868880
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 14:52:02 2008 +0100

    [cairo-scaled-font] Mark the scaled font as finished during destruction.
    
    During the destruction of every font used with an xlib surface, we send
    an XRenderFreeGlyphs() for every single glyph in the cache. These
    requests are redundant as the server-side glyphs will be released along
    with the XRenderFreeGlyphSet(), so we skip the individual glyph
    destruction if the font is marked as finished.

diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index 87a4616..4cf6a37 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -89,6 +89,8 @@ struct _cairo_scaled_font {
     cairo_matrix_t ctm;	          /* user space => device space */
     cairo_font_options_t options;
 
+    cairo_bool_t finished;
+
     /* "live" scaled_font members */
     cairo_matrix_t scale;	  /* font space => device space */
     cairo_matrix_t scale_inverse; /* device space => font space */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 9bab7d3..e12446c 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -190,6 +190,7 @@ static const cairo_scaled_font_t _cairo_scaled_font_nil = {
       CAIRO_SUBPIXEL_ORDER_DEFAULT,
       CAIRO_HINT_STYLE_DEFAULT,
       CAIRO_HINT_METRICS_DEFAULT} ,
+    TRUE,			/* finished */
     { 1., 0., 0., 1., 0, 0},	/* scale */
     { 1., 0., 0., 1., 0, 0},	/* scale_inverse */
     { 0., 0., 0., 0., 0. },	/* extents */
@@ -499,6 +500,8 @@ _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
 	    return status;
     }
 
+    scaled_font->finished = FALSE;
+
     scaled_font->glyphs = _cairo_cache_create (_cairo_scaled_glyph_keys_equal,
 					       _cairo_scaled_glyph_destroy,
 					       MAX_GLYPHS_CACHED_PER_FONT);
@@ -572,6 +575,8 @@ _cairo_scaled_font_set_metrics (cairo_scaled_font_t	    *scaled_font,
 void
 _cairo_scaled_font_fini (cairo_scaled_font_t *scaled_font)
 {
+    scaled_font->finished = TRUE;
+
     if (scaled_font->font_face != NULL)
 	cairo_font_face_destroy (scaled_font->font_face);
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 1c71468..c5e6371 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -41,6 +41,7 @@
 #include "cairo-xlib-private.h"
 #include "cairo-xlib-surface-private.h"
 #include "cairo-clip-private.h"
+#include "cairo-scaled-font-private.h"
 
 #include <X11/Xutil.h> /* for XDestroyImage */
 
@@ -2815,6 +2816,9 @@ _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
     cairo_xlib_surface_font_private_t	*font_private;
     cairo_xlib_font_glyphset_info_t *glyphset_info;
 
+    if (scaled_font->finished)
+	return;
+
     font_private = scaled_font->surface_private;
     glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
     if (font_private != NULL && glyphset_info != NULL) {
commit 24284c5101363f8d418083e821c1bad88830dbcb
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 1 18:05:08 2008 +0100

    [cairo-xlib] Enlarge the on-stack arrays.
    
    Grow the on-stack arrays for the XRectangles and XTrapezoids to the
    standard size in order to reduce the frequency of allocations.

diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 1b094f9..1c71468 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -1621,7 +1621,7 @@ _cairo_xlib_surface_fill_rectangles (void		     *abstract_surface,
 {
     cairo_xlib_surface_t *surface = abstract_surface;
     XRenderColor render_color;
-    XRectangle static_xrects[16];
+    XRectangle static_xrects[CAIRO_STACK_ARRAY_LENGTH (XRectangle)];
     XRectangle *xrects = static_xrects;
     int i;
 
@@ -1635,8 +1635,8 @@ _cairo_xlib_surface_fill_rectangles (void		     *abstract_surface,
     render_color.blue  = color->blue_short;
     render_color.alpha = color->alpha_short;
 
-    if (num_rects > ARRAY_LENGTH(static_xrects)) {
-        xrects = _cairo_malloc_ab (num_rects, sizeof(XRectangle));
+    if (num_rects > ARRAY_LENGTH (static_xrects)) {
+        xrects = _cairo_malloc_ab (num_rects, sizeof (XRectangle));
 	if (xrects == NULL)
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
@@ -1655,7 +1655,7 @@ _cairo_xlib_surface_fill_rectangles (void		     *abstract_surface,
 			   &render_color, xrects, num_rects);
 
     if (xrects != static_xrects)
-        free(xrects);
+        free (xrects);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1869,12 +1869,12 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t	op,
 								 dst_x, dst_y, width, height);
 
     } else {
-        XTrapezoid xtraps_stack[16];
+        XTrapezoid xtraps_stack[CAIRO_STACK_ARRAY_LENGTH (XTrapezoid)];
         XTrapezoid *xtraps = xtraps_stack;
         int i;
 
-        if (num_traps > ARRAY_LENGTH(xtraps_stack)) {
-            xtraps = _cairo_malloc_ab (num_traps, sizeof(XTrapezoid));
+        if (num_traps > ARRAY_LENGTH (xtraps_stack)) {
+            xtraps = _cairo_malloc_ab (num_traps, sizeof (XTrapezoid));
             if (xtraps == NULL) {
                 status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
                 goto BAIL;
@@ -1940,7 +1940,7 @@ _cairo_xlib_surface_set_clip_region (void           *abstract_surface,
             return status;
 
 	if (n_boxes > ARRAY_LENGTH (surface->embedded_clip_rects)) {
-	    rects = _cairo_malloc_ab (n_boxes, sizeof(XRectangle));
+	    rects = _cairo_malloc_ab (n_boxes, sizeof (XRectangle));
 	    if (rects == NULL) {
                 _cairo_region_boxes_fini (region, boxes);
 		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
commit fda95868020effcbc56ff687a763af650a758ba2
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Thu May 1 15:44:22 2008 +0100

    [cairo-xlib] Cache standard xrender formats.
    
    XRender performs a round-trip in order to query the available formats on
    the xserver, before searching for a matching format. In order to save
    that round-trip and to avoid the short-lived allocation of the array of
    available formats, we cache the result on the display.

diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index b1fc67e..cdae8c9 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -287,6 +287,9 @@ _cairo_xlib_display_get (Display *dpy)
     display->close_display_hooks = NULL;
     display->closed = FALSE;
 
+    memset (display->cached_xrender_formats, 0,
+	    sizeof (display->cached_xrender_formats));
+
     display->buggy_repeat = FALSE;
     if (strstr (ServerVendor (dpy), "X.Org") != NULL) {
 	/* When modularized, the X.Org server VendorRelease was
@@ -485,3 +488,35 @@ _cairo_xlib_display_notify (cairo_xlib_display_t *display)
     }
     CAIRO_MUTEX_UNLOCK (display->mutex);
 }
+
+XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
+	                                cairo_format_t		 format)
+{
+    XRenderPictFormat *xrender_format;
+
+    CAIRO_MUTEX_LOCK (display->mutex);
+    xrender_format = display->cached_xrender_formats[format];
+    if (xrender_format == NULL) {
+	int pict_format;
+
+	switch (format) {
+	case CAIRO_FORMAT_A1:
+	    pict_format = PictStandardA1; break;
+	case CAIRO_FORMAT_A8:
+	    pict_format = PictStandardA8; break;
+	case CAIRO_FORMAT_RGB24:
+	    pict_format = PictStandardRGB24; break;
+	default:
+	    ASSERT_NOT_REACHED;
+	case CAIRO_FORMAT_ARGB32:
+	    pict_format = PictStandardARGB32; break;
+	}
+	xrender_format = XRenderFindStandardFormat (display->display,
+		                                    pict_format);
+	display->cached_xrender_formats[format] = xrender_format;
+    }
+    CAIRO_MUTEX_UNLOCK (display->mutex);
+
+    return xrender_format;
+}
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 2d9737d..a88e6b5 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -34,6 +34,7 @@
 #define CAIRO_XLIB_PRIVATE_H
 
 #include "cairo-xlib.h"
+#include "cairo-xlib-xrender-private.h"
 
 #include "cairo-compiler-private.h"
 #include "cairo-freelist-private.h"
@@ -61,6 +62,8 @@ struct _cairo_xlib_display {
     Display *display;
     cairo_xlib_screen_info_t *screens;
 
+    XRenderPictFormat *cached_xrender_formats[CAIRO_FORMAT_A1 + 1];
+
     cairo_xlib_job_t *workqueue;
     cairo_freelist_t wq_freelist;
 
@@ -118,6 +121,10 @@ _cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
 cairo_private void
 _cairo_xlib_display_notify (cairo_xlib_display_t *display);
 
+cairo_private XRenderPictFormat *
+_cairo_xlib_display_get_xrender_format (cairo_xlib_display_t	*display,
+	                                cairo_format_t		 format);
+
 cairo_private cairo_xlib_screen_info_t *
 _cairo_xlib_screen_info_get (Display *display, Screen *screen);
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index acb51c5..1b094f9 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -117,24 +117,6 @@ static const XTransform identity = { {
 #define CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM(surface)	CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
 #define CAIRO_SURFACE_RENDER_HAS_FILTERS(surface)	CAIRO_SURFACE_RENDER_AT_LEAST((surface), 0, 6)
 
-static XRenderPictFormat *
-_CAIRO_FORMAT_TO_XRENDER_FORMAT(Display *dpy, cairo_format_t format)
-{
-    int	pict_format;
-    switch (format) {
-    case CAIRO_FORMAT_A1:
-	pict_format = PictStandardA1; break;
-    case CAIRO_FORMAT_A8:
-	pict_format = PictStandardA8; break;
-    case CAIRO_FORMAT_RGB24:
-	pict_format = PictStandardRGB24; break;
-    case CAIRO_FORMAT_ARGB32:
-    default:
-	pict_format = PictStandardARGB32; break;
-    }
-    return XRenderFindStandardFormat (dpy, pict_format);
-}
-
 static cairo_surface_t *
 _cairo_xlib_surface_create_similar_with_format (void	       *abstract_src,
 						cairo_format_t	format,
@@ -145,15 +127,20 @@ _cairo_xlib_surface_create_similar_with_format (void	       *abstract_src,
     Display *dpy = src->dpy;
     Pixmap pix;
     cairo_xlib_surface_t *surface;
-    XRenderPictFormat *xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (dpy,
-									 format);
+    XRenderPictFormat *xrender_format;
 
     /* As a good first approximation, if the display doesn't have even
      * the most elementary RENDER operation, then we're better off
      * using image surfaces for all temporary operations, so return NULL
      * and let the fallback code happen.
      */
-    if (xrender_format == NULL || ! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src))
+    if (! CAIRO_SURFACE_RENDER_HAS_COMPOSITE (src))
+	return NULL;
+
+    xrender_format = _cairo_xlib_display_get_xrender_format (
+	                                              src->screen_info->display,
+						      format);
+    if (xrender_format == NULL)
 	return NULL;
 
     pix = XCreatePixmap (dpy, src->drawable,
@@ -2031,8 +2018,9 @@ _cairo_xlib_surface_is_similar (void		*surface_a,
     if (xrender_format == NULL ||
 	_xrender_format_to_content (xrender_format) != content)
     {
-	xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT (b->dpy,
-		_cairo_format_from_content (content));
+	xrender_format = _cairo_xlib_display_get_xrender_format (
+					  b->screen_info->display,
+					  _cairo_format_from_content (content));
     }
 
 
@@ -2691,8 +2679,8 @@ typedef struct _cairo_xlib_font_glyphset_info {
 } cairo_xlib_font_glyphset_info_t;
 
 typedef struct _cairo_xlib_surface_font_private {
-    Display		*dpy;
-    cairo_xlib_font_glyphset_info_t glyphset_info[NUM_GLYPHSETS];
+    cairo_xlib_display_t	    *display;
+    cairo_xlib_font_glyphset_info_t  glyphset_info[NUM_GLYPHSETS];
 } cairo_xlib_surface_font_private_t;
 
 static void
@@ -2711,12 +2699,16 @@ _cairo_xlib_surface_remove_scaled_font (Display *dpy,
 
     if (font_private != NULL) {
 	int i;
+
 	for (i = 0; i < NUM_GLYPHSETS; i++) {
-	    cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
-	    if (glyphset_info->glyphset) {
-		XRenderFreeGlyphSet (font_private->dpy, glyphset_info->glyphset);
-	    }
+	    cairo_xlib_font_glyphset_info_t *glyphset_info;
+
+	    glyphset_info = &font_private->glyphset_info[i];
+	    if (glyphset_info->glyphset)
+		XRenderFreeGlyphSet (dpy, glyphset_info->glyphset);
 	}
+
+	_cairo_xlib_display_destroy (font_private->display);
 	free (font_private);
     }
 }
@@ -2729,25 +2721,26 @@ _cairo_xlib_surface_font_init (Display		    *dpy,
     int i;
 
     font_private = malloc (sizeof (cairo_xlib_surface_font_private_t));
-    if (!font_private)
+    if (font_private == NULL)
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-    if (!_cairo_xlib_add_close_display_hook (dpy,
-		_cairo_xlib_surface_remove_scaled_font,
-		scaled_font, scaled_font)) {
+    if (! _cairo_xlib_add_close_display_hook (dpy,
+		                         _cairo_xlib_surface_remove_scaled_font,
+					 scaled_font, scaled_font))
+    {
 	free (font_private);
 	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
 
-    font_private->dpy = dpy;
+    font_private->display = _cairo_xlib_display_get (dpy);
     for (i = 0; i < NUM_GLYPHSETS; i++) {
 	cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
 	switch (i) {
-	    case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break;
-	    case GLYPHSET_INDEX_A8:     glyphset_info->format = CAIRO_FORMAT_A8;     break;
-	    case GLYPHSET_INDEX_A1:     glyphset_info->format = CAIRO_FORMAT_A1;     break;
-	    default:                    ASSERT_NOT_REACHED;                          break;
+	case GLYPHSET_INDEX_ARGB32: glyphset_info->format = CAIRO_FORMAT_ARGB32; break;
+	case GLYPHSET_INDEX_A8:     glyphset_info->format = CAIRO_FORMAT_A8;     break;
+	case GLYPHSET_INDEX_A1:     glyphset_info->format = CAIRO_FORMAT_A1;     break;
+	default:                    ASSERT_NOT_REACHED;                          break;
 	}
 	glyphset_info->xrender_format = NULL;
 	glyphset_info->glyphset = None;
@@ -2762,31 +2755,31 @@ _cairo_xlib_surface_font_init (Display		    *dpy,
 static void
 _cairo_xlib_surface_scaled_font_fini (cairo_scaled_font_t *scaled_font)
 {
-    cairo_xlib_surface_font_private_t	*font_private = scaled_font->surface_private;
+    cairo_xlib_surface_font_private_t *font_private;
 
-    if (font_private) {
+    font_private = scaled_font->surface_private;
+    if (font_private != NULL) {
 	cairo_xlib_display_t *display;
+	int i;
 
-	_cairo_xlib_remove_close_display_hooks (font_private->dpy, scaled_font);
+	display = font_private->display;
+	_cairo_xlib_remove_close_display_hooks (display->display, scaled_font);
 
-	display = _cairo_xlib_display_get (font_private->dpy);
-	if (display != NULL) {
-	    int i;
+	for (i = 0; i < NUM_GLYPHSETS; i++) {
+	    cairo_xlib_font_glyphset_info_t *glyphset_info;
 
-	    for (i = 0; i < NUM_GLYPHSETS; i++) {
-		cairo_xlib_font_glyphset_info_t *glyphset_info = &font_private->glyphset_info[i];
-		if (glyphset_info->glyphset) {
-		    cairo_status_t status;
-		    status = _cairo_xlib_display_queue_resource (display,
-								 XRenderFreeGlyphSet,
-								 glyphset_info->glyphset);
-		    (void) status; /* XXX cannot propagate failure */
-		}
-	    }
+	    glyphset_info = &font_private->glyphset_info[i];
+	    if (glyphset_info->glyphset) {
+		cairo_status_t status;
 
-	    _cairo_xlib_display_destroy (display);
+		status = _cairo_xlib_display_queue_resource (display,
+							     XRenderFreeGlyphSet,
+							     glyphset_info->glyphset);
+		(void) status; /* XXX cannot propagate failure */
+	    }
 	}
 
+	_cairo_xlib_display_destroy (display);
 	free (font_private);
     }
 }
@@ -2819,28 +2812,31 @@ static void
 _cairo_xlib_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
 				       cairo_scaled_font_t  *scaled_font)
 {
-    cairo_xlib_surface_font_private_t	*font_private = scaled_font->surface_private;
+    cairo_xlib_surface_font_private_t	*font_private;
+    cairo_xlib_font_glyphset_info_t *glyphset_info;
 
-    if (font_private != NULL && scaled_glyph->surface_private != NULL) {
-	cairo_xlib_display_t *display = _cairo_xlib_display_get (font_private->dpy);
-	if (display != NULL) {
-	    struct _cairo_xlib_render_free_glyphs *arg = malloc (sizeof (*arg));
-	    if (arg != NULL) {
-		cairo_status_t status;
-		arg->glyphset = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph)->glyphset;
-		arg->glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
-		status = _cairo_xlib_display_queue_work (display,
-			(cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
-			arg,
-			free);
-		if (status) {
-		    /* XXX cannot propagate failure */
-		    free (arg);
-		}
-	    }
+    font_private = scaled_font->surface_private;
+    glyphset_info = _cairo_xlib_scaled_glyph_get_glyphset_info (scaled_glyph);
+    if (font_private != NULL && glyphset_info != NULL) {
+	cairo_xlib_display_t *display;
+	struct _cairo_xlib_render_free_glyphs *arg;
+
+	display = font_private->display;
+	arg = malloc (sizeof (*arg));
+	if (arg != NULL) {
+	    cairo_status_t status;
 
-	    _cairo_xlib_display_destroy (display);
+	    arg->glyphset = glyphset_info->glyphset;
+	    arg->glyph_index = _cairo_scaled_glyph_index (scaled_glyph);
 
+	    status = _cairo_xlib_display_queue_work (display,
+		    (cairo_xlib_notify_func) _cairo_xlib_render_free_glyphs,
+		    arg,
+		    free);
+	    if (status) {
+		/* XXX cannot propagate failure */
+		free (arg);
+	    }
 	}
     }
 }
@@ -2857,7 +2853,7 @@ static cairo_xlib_font_glyphset_info_t *
 _cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scaled_font,
 						      cairo_format_t       format)
 {
-    cairo_xlib_surface_font_private_t *font_private = scaled_font->surface_private;
+    cairo_xlib_surface_font_private_t *font_private;
     cairo_xlib_font_glyphset_info_t *glyphset_info;
     int glyphset_index;
 
@@ -2869,11 +2865,16 @@ _cairo_xlib_scaled_font_get_glyphset_info_for_format (cairo_scaled_font_t *scale
     case CAIRO_FORMAT_A1:     glyphset_index = GLYPHSET_INDEX_A1;     break;
     }
 
+    font_private = scaled_font->surface_private;
     glyphset_info = &font_private->glyphset_info[glyphset_index];
-
     if (glyphset_info->glyphset == None) {
-	glyphset_info->xrender_format = _CAIRO_FORMAT_TO_XRENDER_FORMAT(font_private->dpy, glyphset_info->format);
-	glyphset_info->glyphset = XRenderCreateGlyphSet (font_private->dpy, glyphset_info->xrender_format);
+	cairo_xlib_display_t *display = font_private->display;
+
+	glyphset_info->xrender_format =
+	    _cairo_xlib_display_get_xrender_format (display,
+		                                    glyphset_info->format);
+	glyphset_info->glyphset = XRenderCreateGlyphSet (display->display,
+		                                 glyphset_info->xrender_format);
     }
 
     return glyphset_info;
@@ -3442,7 +3443,7 @@ _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
     font_private = scaled_font->surface_private;
     if ((scaled_font->surface_backend != NULL &&
 	 scaled_font->surface_backend != &cairo_xlib_surface_backend) ||
-	(font_private != NULL && font_private->dpy != dst->dpy))
+	(font_private != NULL && font_private->display != dst->screen_info->display))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     /* After passing all those tests, we're now committed to rendering
commit 8457374c9cf350841a7c16f1ef1657aeb354e5c9
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Wed Apr 30 22:03:21 2008 +0100

    [cairo-pattern] Repaint existing cached similar solid surfaces.
    
    The current solid surface cache returns an existing similar surface only
    if it is an exact match for the desired colour.  This gives a high hit
    rate for the original goal of drawing text, but fails when using a lot
    of colours - for example drawing a pie-chart, the mosaic perf case, or
    browsing the web.  So instead of creating a new surface from scratch we
    can repaint an existing one, providing that we have sufficient surfaces
    already cached, i.e. if we are going to evict a surface, we may as well
    just repaint it.

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 682a690..0f2eb3e 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1402,27 +1402,64 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t	     *pattern,
 
     cairo_surface_t *surface;
     cairo_status_t   status;
+    char is_similar[MAX_SURFACE_CACHE_SIZE];
+    int count;
 
     CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
 
     /* Check cache first */
     if (i < solid_surface_cache.size &&
-	    _cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
-		                                  pattern,
-						  dst))
+	_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
+		                              pattern,
+					      dst))
     {
-	if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
-	    goto DONE;
+	status = _cairo_surface_reset (solid_surface_cache.cache[i].surface);
+	if (status)
+	    goto UNLOCK;
+
+	goto DONE;
     }
 
+    count = 0;
     for (i = 0 ; i < solid_surface_cache.size; i++) {
-	if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
-		                                  pattern,
-						  dst))
-	{
-	    if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
-		goto DONE;
-	}
+	const struct _cairo_pattern_solid_surface_cache *cache;
+
+	cache = &solid_surface_cache.cache[i];
+
+	if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1)
+	    continue;
+
+	if (! _cairo_surface_is_similar (cache->surface, dst, pattern->content))
+	    continue;
+
+	is_similar[count++] = i;
+
+	if (! _cairo_color_equal (&cache->color, &pattern->color))
+	    continue;
+
+	status = _cairo_surface_reset (cache->surface);
+	if (status)
+	    goto UNLOCK;
+
+	goto DONE;
+    }
+
+    /* Choose a surface to re-use (but only if we have an ample supply) */
+    if (i == MAX_SURFACE_CACHE_SIZE && count > MAX_SURFACE_CACHE_SIZE / 4) {
+	i = is_similar[rand () % count];
+	surface = solid_surface_cache.cache[i].surface;
+
+	status = _cairo_surface_reset (surface);
+	if (status)
+	    goto UNLOCK;
+
+	status = _cairo_surface_paint (surface,
+		                       CAIRO_OPERATOR_SOURCE,
+				       &pattern->base);
+	if (status)
+	    goto UNLOCK;
+
+	goto SAVE;
     }
 
     /* Not cached, need to create new */
@@ -1453,6 +1490,7 @@ _cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t	     *pattern,
 	cairo_surface_destroy (solid_surface_cache.cache[i].surface);
     }
 
+SAVE:
     solid_surface_cache.cache[i].color   = pattern->color;
     solid_surface_cache.cache[i].surface = surface;
 
commit 4b3f9c7c7d6ba01cf02815994af7da663e85bb9b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 14:18:18 2008 +0100

    [cairo-arc] Protect against division-by-zero.
    
    Avoid a division-by-zero which can only happen if the start angle is
    exactly equal to the end angle.
    
    Fixes test/degenerate-arc.

diff --git a/src/cairo-arc.c b/src/cairo-arc.c
index 5232970..eed75e7 100644
--- a/src/cairo-arc.c
+++ b/src/cairo-arc.c
@@ -208,7 +208,7 @@ _cairo_arc_in_direction (cairo_t	  *cr,
 				     angle_min, angle_mid,
 				     dir);
 	}
-    } else {
+    } else if (angle_max != angle_min) {
 	cairo_matrix_t ctm;
 	int i, segments;
 	double angle, angle_step;
commit ff596c64fb08b9845e2176146ee129b60fe5775a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 10:58:57 2008 +0100

    [test] Add degenerate-arc
    
    This test case is to exercise the divide-by-zero error reported by
    Luiz Americo Pereira Camara <luizmed at oi.com.br>,
    http://lists.cairographics.org/archives/cairo/2008-May/014054.html.

diff --git a/test/.gitignore b/test/.gitignore
index f34494e..cf61740 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -43,6 +43,7 @@ dash-offset-negative
 dash-scale
 dash-state
 dash-zero-length
+degenerate-arc
 degenerate-path
 degenerate-pen
 device-offset
diff --git a/test/Makefile.am b/test/Makefile.am
index ec98a49..99a7ece 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -36,6 +36,7 @@ dash-offset-negative$(EXEEXT)				\
 dash-scale$(EXEEXT)					\
 dash-state$(EXEEXT)					\
 dash-zero-length$(EXEEXT)				\
+degenerate-arc$(EXEEXT)					\
 degenerate-path$(EXEEXT)				\
 degenerate-pen$(EXEEXT)					\
 device-offset$(EXEEXT)					\
@@ -349,6 +350,8 @@ REFERENCE_IMAGES = \
 	dash-zero-length-ps-rgb24-ref.png	\
 	dash-zero-length-ref.png	\
 	dash-zero-length-rgb24-ref.png	\
+	degenerate-arc-ref.png		\
+	degenerate-arc-ps-ref.png	\
 	degenerate-pen-ref.png		\
 	degenerate-pen-ps-ref.png	\
 	degenerate-pen-quartz-ref.png	\
diff --git a/test/degenerate-arc-ps-ref.png b/test/degenerate-arc-ps-ref.png
new file mode 100644
index 0000000..3cbf4da
Binary files /dev/null and b/test/degenerate-arc-ps-ref.png differ
diff --git a/test/degenerate-arc-ref.png b/test/degenerate-arc-ref.png
new file mode 100644
index 0000000..5112d7f
Binary files /dev/null and b/test/degenerate-arc-ref.png differ
diff --git a/test/degenerate-arc.c b/test/degenerate-arc.c
new file mode 100644
index 0000000..f01b666
--- /dev/null
+++ b/test/degenerate-arc.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2008 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * Chris Wilson not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Chris Wilson makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * CHRIS WILSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL CHRIS WILSON BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+/* This test case exercises a "Potential division by zero in cairo_arc"
+ * reported by Luiz Americo Pereira Camara <luizmed at oi.com.br>,
+ * http://lists.cairographics.org/archives/cairo/2008-May/014054.html.
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+    "degenerate-arc",
+    "Tests the behaviour of degenerate arcs",
+    40, 40,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int n;
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+
+    cairo_set_line_width (cr, 5);
+    cairo_set_source_rgb (cr, 0, 1, 0);
+    for (n = 0; n < 8; n++) {
+	double theta = n * 2 * M_PI / 8;
+	cairo_new_sub_path (cr);
+	cairo_arc (cr, 20, 20, 15, theta, theta);
+	cairo_close_path (cr);
+    }
+    cairo_stroke (cr);
+
+    cairo_set_line_width (cr, 2);
+    cairo_set_source_rgb (cr, 0, 0, 1);
+    for (n = 0; n < 8; n++) {
+	double theta = n * 2 * M_PI / 8;
+	cairo_move_to (cr, 20, 20);
+	cairo_arc (cr, 20, 20, 15, theta, theta);
+    }
+    cairo_stroke (cr);
+
+    cairo_set_source_rgb (cr, 1, 0, 0);
+    cairo_arc (cr, 20, 20, 2, 0, 2*M_PI);
+    cairo_fill (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}
commit fed9d9060eb950a6692334a7ba3879009b597532
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 11:31:14 2008 +0100

    [test/invalid-matrix] Disable floating point exception.
    
    test/invalid-matrix purposely feeds invalid numbers into cairo, in
    order to check its detection of garbage values. In doing so, cairo
    raises an Invalid exception, but as this is a direct result of an abuse
    of the API we can treat it as expected behaviour and ignore the
    exception.

diff --git a/configure.in b/configure.in
index 268a709..2ec8f56 100644
--- a/configure.in
+++ b/configure.in
@@ -126,7 +126,7 @@ esac
 AC_MSG_RESULT([$solaris_posix_pthread])
 
 # check for GNU-extensions to fenv
-AC_CHECK_HEADER(fenv.h, [AC_CHECK_FUNCS(feenableexcept)])
+AC_CHECK_HEADER(fenv.h, [AC_CHECK_FUNCS(feenableexcept fedisableexcept)])
 
 dnl ===========================================================================
 dnl
diff --git a/test/invalid-matrix.c b/test/invalid-matrix.c
index 42240bc..c25197f 100644
--- a/test/invalid-matrix.c
+++ b/test/invalid-matrix.c
@@ -30,6 +30,10 @@
 #define HAVE_INFINITY 1
 #endif
 
+#if HAVE_FEDISABLEEXCEPT
+#include <fenv.h>
+#endif
+
 static cairo_test_draw_function_t draw;
 
 cairo_test_t test = {
@@ -69,6 +73,11 @@ if ((status) == CAIRO_STATUS_SUCCESS) {							\
     return CAIRO_TEST_FAILURE;								\
 }
 
+    /* clear floating point exceptions (added by cairo_test_init()) */
+#if HAVE_FEDISABLEEXCEPT
+    fedisableexcept (FE_INVALID);
+#endif
+
     /* create a bogus matrix and check results of attempted inversion */
     bogus.x0 = bogus.xy = bogus.xx = strtod ("NaN", NULL);
     bogus.y0 = bogus.yx = bogus.yy = bogus.xx;
commit 1755a2d27def853163f9c496c07ae79070c5002e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 10:29:20 2008 +0100

    [test] Enable floating point exceptions.
    
    Some platforms and applications enable floating point exceptions, so it
    seems sensible to run the test-suite with the most serious exceptions
    enabled - divide by zero, invalid result and overflow.

diff --git a/configure.in b/configure.in
index c93b8cd..268a709 100644
--- a/configure.in
+++ b/configure.in
@@ -108,6 +108,9 @@ AC_DEFUN([_CHECK_FUNCS_WITH_FLAGS],
 
 dnl ===========================================================================
 
+AC_CHECK_LIBM
+LIBS="$LIBS $LIBM"
+
 AC_CHECK_FUNCS(vasnprintf ctime_r drand48)
 
 AC_MSG_CHECKING([for Sun Solaris (non-POSIX ctime_r)])
@@ -122,6 +125,9 @@ case "$host" in
 esac
 AC_MSG_RESULT([$solaris_posix_pthread])
 
+# check for GNU-extensions to fenv
+AC_CHECK_HEADER(fenv.h, [AC_CHECK_FUNCS(feenableexcept)])
+
 dnl ===========================================================================
 dnl
 dnl Test for native atomic operations.
@@ -159,9 +165,6 @@ AC_MSG_RESULT([$cairo_atomic_op_needs_memory_barrier])
 
 dnl ===========================================================================
 
-AC_CHECK_LIBM
-LIBS="$LIBS $LIBM"
-
 AC_CHECK_LIB(z, compress,
 	 [AC_CHECK_HEADER(zlib.h, [have_libz=yes],
 	 [have_libz="no (requires zlib http://www.gzip.org/zlib/)"])],
diff --git a/test/cairo-test.c b/test/cairo-test.c
index 8ed5889..b1f0191 100644
--- a/test/cairo-test.c
+++ b/test/cairo-test.c
@@ -35,6 +35,9 @@
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 #endif
+#if HAVE_FEENABLEEXCEPT
+#include <fenv.h>
+#endif
 #include <assert.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
@@ -110,6 +113,10 @@ cairo_test_init (const char *test_name)
 {
     char *log_name;
 
+#if HAVE_FEENABLEEXCEPT
+    feenableexcept (FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW);
+#endif
+
     xasprintf (&log_name, "%s%s", test_name, CAIRO_TEST_LOG_SUFFIX);
     xunlink (log_name);
 
commit bf49015202523c6e9f4130c3160e8b5c50f16db8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue May 6 10:19:59 2008 +0100

    [configure.in] Add lcov-1.6 to list of valid versions.
    
    Accept version 1.6 of lcov.

diff --git a/configure.in b/configure.in
index c7e457f..c93b8cd 100644
--- a/configure.in
+++ b/configure.in
@@ -900,7 +900,7 @@ if test "x$use_gcov" = "xyes"; then
     AC_MSG_ERROR([ccache must be disabled when --enable-gcov option is used. You can disable ccache by setting environment variable CCACHE_DISABLE=1.])
   fi
 
-  ltp_version_list="1.4 1.5"
+  ltp_version_list="1.6 1.5 1.4"
   AC_CHECK_PROG(LTP, lcov, lcov)
   AC_CHECK_PROG(LTP_GENHTML, genhtml, genhtml)
 


More information about the cairo-commit mailing list