[cairo-commit] 24 commits - src/cairo-analysis-surface.c src/cairo-gl-glyphs.c src/cairo-gl-private.h src/cairo-gl-surface.c src/cairoint.h src/cairo-rtree.c src/cairo-rtree-private.h src/cairo-scaled-font.c src/cairo-surface-fallback.c src/cairo-tor-scan-converter.c src/cairo-xcb-surface.c src/cairo-xlib-surface.c src/Makefile.sources test/clip-fill.c test/clip-fill.ps.xfail.png test/clip-fill.ref.png test/clip-fill.xlib-fallback.ref.png test/clip-fill.xlib.ref.png test/clip-operator.gl.argb32.ref.png test/clip-text.c test/clip-text.ps.xfail.png test/clip-text.ref.png test/clip-text.svg.ref.png test/clip-text.xlib.ref.png test/Makefile.am test/Makefile.sources

Chris Wilson ickle at kemper.freedesktop.org
Fri Jul 31 11:40:11 PDT 2009


 src/Makefile.sources                 |    2 
 src/cairo-analysis-surface.c         |    6 
 src/cairo-gl-glyphs.c                |  747 +++++++++++++++++++++++++++++++++++
 src/cairo-gl-private.h               |  124 +++++
 src/cairo-gl-surface.c               |  590 ++++++++++++---------------
 src/cairo-rtree-private.h            |    3 
 src/cairo-rtree.c                    |   13 
 src/cairo-scaled-font.c              |   23 -
 src/cairo-surface-fallback.c         |    3 
 src/cairo-tor-scan-converter.c       |   15 
 src/cairo-xcb-surface.c              |    3 
 src/cairo-xlib-surface.c             |    3 
 src/cairoint.h                       |    3 
 test/Makefile.am                     |    9 
 test/Makefile.sources                |    2 
 test/clip-fill.c                     |   78 +++
 test/clip-fill.ps.xfail.png          |binary
 test/clip-fill.ref.png               |binary
 test/clip-fill.xlib-fallback.ref.png |binary
 test/clip-fill.xlib.ref.png          |binary
 test/clip-operator.gl.argb32.ref.png |binary
 test/clip-text.c                     |   88 ++++
 test/clip-text.ps.xfail.png          |binary
 test/clip-text.ref.png               |binary
 test/clip-text.svg.ref.png           |binary
 test/clip-text.xlib.ref.png          |binary
 26 files changed, 1384 insertions(+), 328 deletions(-)

New commits:
commit 1d2abe7d1061e580e189604b25465c8025cedfb7
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 19:38:23 2009 +0100

    [gl] Refresh clip-operator ref.
    
    A minor renderering difference, acceptable.

diff --git a/test/Makefile.am b/test/Makefile.am
index bf9671d..6e03910 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -189,6 +189,7 @@ REFERENCE_IMAGES = \
 	clip-nesting.rgb24.ref.png \
 	clip-nesting.test-paginated.rgb24.ref.png \
 	clip-nesting.xlib.rgb24.ref.png \
+	clip-operator.gl.argb32.ref.png \
 	clip-operator.pdf.argb32.ref.png \
 	clip-operator.pdf.rgb24.ref.png \
 	clip-operator.ps2.rgb24.ref.png \
diff --git a/test/clip-operator.gl.argb32.ref.png b/test/clip-operator.gl.argb32.ref.png
new file mode 100644
index 0000000..92d8b75
Binary files /dev/null and b/test/clip-operator.gl.argb32.ref.png differ
commit 640a0be2e2ef0124424aeca32915a2f644b03d27
Merge: 067de9c... 5c2239b...
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 18:03:35 2009 +0100

    Merge branch 'gl-glyph-cache'
    
    Comparing the glyph-cache on an i915:
    
       gnome-terminal-20090728:  1.51x speedup
              firefox-20090601:  No change

commit 5c2239be4b11437b1ab1731ef2dfac6cfb8095b1
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 16:29:49 2009 +0100

    [gl] Use scissors rather than allocating a temporary depth buffer.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 579fb9b..ff599d8 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -241,15 +241,6 @@ typedef struct _cairo_gl_glyphs_setup
     cairo_gl_surface_t *dst;
 } cairo_gl_glyphs_setup_t;
 
-static int
-_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
-{
-    if (surface->fb)
-	return y;
-    else
-	return (surface->height - 1) - y;
-}
-
 static void
 _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 			cairo_gl_glyphs_setup_t *setup)
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index f064d18..110df34 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -203,4 +203,13 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 cairo_private void
 _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
 
+static inline int
+_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
+{
+    if (surface->fb)
+	return y;
+    else
+	return (surface->height - 1) - y;
+}
+
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index c7b97a7..1668fbc 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1610,6 +1610,7 @@ typedef struct _cairo_gl_surface_span_renderer {
     cairo_antialias_t antialias;
 
     cairo_gl_surface_t *dst;
+    cairo_region_t *clip;
 
     cairo_composite_rectangles_t composite_rectangles;
     GLuint vbo;
@@ -1622,12 +1623,32 @@ typedef struct _cairo_gl_surface_span_renderer {
 static void
 _cairo_gl_span_renderer_flush (cairo_gl_surface_span_renderer_t *renderer)
 {
+    int count;
+
     if (renderer->vbo_offset == 0)
 	return;
 
     glUnmapBufferARB (GL_ARRAY_BUFFER_ARB);
-    glDrawArrays (GL_LINES, 0, renderer->vbo_offset / renderer->vertex_size);
+
+    count = renderer->vbo_offset / renderer->vertex_size;
     renderer->vbo_offset = 0;
+
+    if (renderer->clip) {
+	int i, num_rectangles = cairo_region_num_rectangles (renderer->clip);
+
+	glEnable (GL_SCISSOR_TEST);
+	for (i = 0; i < num_rectangles; i++) {
+	    cairo_rectangle_int_t rect;
+
+	    cairo_region_get_rectangle (renderer->clip, i, &rect);
+
+	    glScissor (rect.x, rect.y, rect.width, rect.height);
+	    glDrawArrays (GL_LINES, 0, count);
+	}
+	glDisable (GL_SCISSOR_TEST);
+    } else {
+	glDrawArrays (GL_LINES, 0, count);
+    }
 }
 
 static void *
@@ -1813,7 +1834,6 @@ _cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
     glDisable (GL_TEXTURE_2D);
 
     glDisable (GL_BLEND);
-    glDisable (GL_DEPTH_TEST);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1839,89 +1859,6 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
     (void) rects;
 }
 
-static void
-_cairo_gl_surface_ensure_depth_buffer (cairo_gl_surface_t *surface)
-{
-    if (surface->depth_stencil_tex)
-	return;
-
-    glGenTextures (1, &surface->depth_stencil_tex);
-    glBindTexture (GL_TEXTURE_2D, surface->depth_stencil_tex);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexImage2D (GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8_EXT,
-		  surface->width, surface->height, 0,
-		  GL_DEPTH_STENCIL_EXT, GL_UNSIGNED_INT_24_8_EXT, NULL);
-    glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
-			       GL_DEPTH_ATTACHMENT_EXT,
-			       GL_TEXTURE_2D,
-			       surface->depth_stencil_tex, 0);
-    glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
-			       GL_STENCIL_ATTACHMENT_EXT,
-			       GL_TEXTURE_2D,
-			       surface->depth_stencil_tex, 0);
-}
-
-
-static cairo_status_t
-_cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
-				   cairo_region_t *clip_region)
-{
-    GLfloat vertices_stack[CAIRO_STACK_ARRAY_LENGTH(GLfloat)], *vertices;
-    int num_rectangles, i, n;
-
-    if (clip_region == NULL)
-	return CAIRO_STATUS_SUCCESS;
-
-    num_rectangles = cairo_region_num_rectangles (clip_region);
-
-    vertices = vertices_stack;
-    if (num_rectangles * 8 > ARRAY_LENGTH (vertices_stack)) {
-	vertices = _cairo_malloc_ab (num_rectangles,
-				     4*3*sizeof (vertices[0]));
-	if (unlikely (vertices == NULL))
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-    }
-
-    for (i = n = 0; i < num_rectangles; i++) {
-	cairo_rectangle_int_t rect;
-
-	cairo_region_get_rectangle (clip_region, i, &rect);
-	vertices[n++] = rect.x;
-	vertices[n++] = rect.y;
-	vertices[n++] = rect.x + rect.width;
-	vertices[n++] = rect.y;
-	vertices[n++] = rect.x + rect.width;
-	vertices[n++] = rect.y + rect.height;
-	vertices[n++] = rect.x;
-	vertices[n++] = rect.y +rect. height;
-    }
-
-    _cairo_gl_surface_ensure_depth_buffer (surface);
-
-    glDisable (GL_BLEND);
-    glDepthFunc (GL_ALWAYS);
-    glEnable (GL_DEPTH_TEST);
-    glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-
-    glDepthMask (GL_TRUE);
-    glClear (GL_DEPTH_BUFFER_BIT);
-
-    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
-    glEnableClientState (GL_VERTEX_ARRAY);
-    glDrawArrays (GL_QUADS, 0, 4 * num_rectangles);
-    glDisableClientState (GL_VERTEX_ARRAY);
-
-    glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-    glDepthFunc (GL_EQUAL);
-    glDepthMask (GL_FALSE);
-
-    if (vertices != vertices_stack)
-	free (vertices);
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
 static cairo_span_renderer_t *
 _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
 					const cairo_pattern_t	*src,
@@ -1949,6 +1886,7 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     renderer->op = op;
     renderer->antialias = antialias;
     renderer->dst = dst;
+    renderer->clip = clip_region;
 
     renderer->composite_rectangles = *rects;
 
@@ -1965,12 +1903,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     _cairo_gl_context_acquire (dst->ctx);
     _cairo_gl_set_destination (dst);
 
-    status = _cairo_gl_surface_set_clip_region (dst, clip_region);
-    if (unlikely (status)) {
-	_cairo_gl_surface_span_renderer_destroy (renderer);
-	return _cairo_span_renderer_create_in_error (status);
-    }
-
     src_attributes = &renderer->setup.src.operand.texture.attributes;
 
     _cairo_gl_set_operator (dst, op);
commit 067de9cf62223ee7a36aab10e3d8a4e0dcda4741
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 17:49:39 2009 +0100

    [test] Add clip-fill.
    
    Test filling through a complex clip.

diff --git a/test/Makefile.am b/test/Makefile.am
index 783511c..bf9671d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -163,6 +163,10 @@ REFERENCE_IMAGES = \
 	clear.svg12.rgb24.xfail.png \
 	clip-all.ref.png \
 	clip-empty.ref.png \
+	clip-fill.ref.png \
+	clip-fill.ps.xfail.png \
+	clip-fill.xlib.ref.png \
+	clip-fill.xlib-fallback.ref.png \
 	clip-fill-rule-pixel-aligned.ref.png \
 	clip-fill-rule-pixel-aligned.rgb24.ref.png \
 	clip-fill-rule.pdf.argb32.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index 45bf5fd..50b4d3e 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -15,6 +15,7 @@ test_sources = \
 	clear.c						\
 	clip-all.c					\
 	clip-empty.c					\
+	clip-fill.c					\
 	clip-fill-rule.c				\
 	clip-fill-rule-pixel-aligned.c			\
 	clip-nesting.c					\
diff --git a/test/clip-fill.c b/test/clip-fill.c
new file mode 100644
index 0000000..331af79
--- /dev/null
+++ b/test/clip-fill.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * 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>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0, 0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 8, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0.7);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0.7, 0);
+    cairo_arc (cr, 10, 10, 7.5, 0, 2 * M_PI);
+    cairo_fill (cr);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_fill,
+	    "Tests filling through complex clips.",
+	    "clip, fill", /* keywords */
+	    NULL, /* requirements */
+	    2 * WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/clip-fill.ps.xfail.png b/test/clip-fill.ps.xfail.png
new file mode 100644
index 0000000..d0aeaf1
Binary files /dev/null and b/test/clip-fill.ps.xfail.png differ
diff --git a/test/clip-fill.ref.png b/test/clip-fill.ref.png
new file mode 100644
index 0000000..41871bc
Binary files /dev/null and b/test/clip-fill.ref.png differ
diff --git a/test/clip-fill.xlib-fallback.ref.png b/test/clip-fill.xlib-fallback.ref.png
new file mode 100644
index 0000000..72dc229
Binary files /dev/null and b/test/clip-fill.xlib-fallback.ref.png differ
diff --git a/test/clip-fill.xlib.ref.png b/test/clip-fill.xlib.ref.png
new file mode 100644
index 0000000..d6e84a3
Binary files /dev/null and b/test/clip-fill.xlib.ref.png differ
commit 7db1fb6fa97e0c44e66c0ae533b94fb3f26b273d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 16:57:14 2009 +0100

    [test] Add clip-text
    
    Test applying a complex clip to text.

diff --git a/test/Makefile.am b/test/Makefile.am
index 8ef31ca..783511c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -213,6 +213,10 @@ REFERENCE_IMAGES = \
 	clip-push-group.quartz.ref.png \
 	clip-push-group.ref.png \
 	clip-push-group.xlib.ref.png \
+	clip-text.ref.png \
+	clip-text.ps.xfail.png \
+	clip-text.svg.ref.png \
+	clip-text.xlib.ref.png \
 	clip-twice.pdf.argb32.ref.png \
 	clip-twice.ps2.argb32.ref.png \
 	clip-twice.ps2.rgb24.ref.png \
diff --git a/test/Makefile.sources b/test/Makefile.sources
index dd71856..45bf5fd 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -20,6 +20,7 @@ test_sources = \
 	clip-nesting.c					\
 	clip-operator.c					\
 	clip-push-group.c				\
+	clip-text.c					\
 	clip-twice.c					\
 	clip-unbounded.c				\
 	clip-zero.c					\
diff --git a/test/clip-text.c b/test/clip-text.c
new file mode 100644
index 0000000..ed8e107
--- /dev/null
+++ b/test/clip-text.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2009 Chris Wilson
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * 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>
+ */
+
+#include "cairo-test.h"
+
+#define WIDTH 20
+#define HEIGHT 20
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    const char *cairo = "Cairo";
+    cairo_text_extents_t extents;
+    double x0, y0;
+
+    cairo_text_extents (cr, cairo, &extents);
+    x0 = WIDTH/2. - (extents.width/2. + extents.x_bearing);
+    y0 = HEIGHT/2. - (extents.height/2. + extents.y_bearing);
+
+    cairo_set_source_rgb (cr, 1, 1, 1);
+    cairo_paint (cr);
+
+    /* aligned-clip */
+    cairo_save (cr);
+    cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_rectangle (cr, 0, 0, 20, 20);
+    cairo_rectangle (cr, 3, 3, 10, 10);
+    cairo_rectangle (cr, 7, 7, 10, 10);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0.7, 0, 0);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_move_to (cr, x0, y0);
+    cairo_show_text (cr, cairo);
+    cairo_restore (cr);
+
+    cairo_translate (cr, WIDTH, 0);
+
+    /* force a clip-mask */
+    cairo_save (cr);
+    cairo_arc (cr, 10, 10, 10, 0, 2 * M_PI);
+    cairo_new_sub_path (cr);
+    cairo_arc_negative (cr, 10, 10, 5, 2 * M_PI, 0);
+    cairo_new_sub_path (cr);
+    cairo_arc (cr, 10, 10, 2, 0, 2 * M_PI);
+    cairo_clip (cr);
+
+    cairo_set_source_rgb (cr, 0, 0, 0.7);
+    cairo_paint (cr);
+    cairo_set_source_rgb (cr, 0, 0, 0);
+
+    cairo_move_to (cr, x0, y0);
+    cairo_show_text (cr, cairo);
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_text,
+	    "Tests drawing text through complex clips.",
+	    "clip, text", /* keywords */
+	    NULL, /* requirements */
+	    2 * WIDTH, HEIGHT,
+	    NULL, draw)
diff --git a/test/clip-text.ps.xfail.png b/test/clip-text.ps.xfail.png
new file mode 100644
index 0000000..b50217d
Binary files /dev/null and b/test/clip-text.ps.xfail.png differ
diff --git a/test/clip-text.ref.png b/test/clip-text.ref.png
new file mode 100644
index 0000000..5cb0030
Binary files /dev/null and b/test/clip-text.ref.png differ
diff --git a/test/clip-text.svg.ref.png b/test/clip-text.svg.ref.png
new file mode 100644
index 0000000..8a2dc29
Binary files /dev/null and b/test/clip-text.svg.ref.png differ
diff --git a/test/clip-text.xlib.ref.png b/test/clip-text.xlib.ref.png
new file mode 100644
index 0000000..be27b96
Binary files /dev/null and b/test/clip-text.xlib.ref.png differ
commit 728d3f6383bba218de03f2693f289ddad6a80d0c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 16:24:37 2009 +0100

    Revert "[gl] Fixup unbounded regions after spans"
    
    This reverts commit f7121ca88844fa70ecb1a35f3f6c9b653324718d.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index b586a67..c7b97a7 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1812,37 +1812,6 @@ _cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
     glActiveTexture (GL_TEXTURE1);
     glDisable (GL_TEXTURE_2D);
 
-    if (! _cairo_operator_bounded_by_mask (renderer->op)) {
-	struct {
-	    GLfloat x,y;
-	} vertices[4];
-
-	glBlendFunc (GL_ZERO, GL_ZERO);
-
-	vertices[0].x = renderer->composite_rectangles.dst.x;
-	vertices[0].y = renderer->composite_rectangles.dst.y;
-
-	vertices[1].x = renderer->composite_rectangles.dst.x +
-	                renderer->composite_rectangles.width;
-	vertices[1].y = renderer->composite_rectangles.dst.y;
-
-	vertices[2].x = renderer->composite_rectangles.dst.x +
-	                renderer->composite_rectangles.width;
-	vertices[2].y = renderer->composite_rectangles.dst.y +
-	                renderer->composite_rectangles.height;
-
-	vertices[3].x = renderer->composite_rectangles.dst.x;
-	vertices[3].y = renderer->composite_rectangles.dst.y +
-	                renderer->composite_rectangles.height;
-
-	glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
-	glEnableClientState (GL_VERTEX_ARRAY);
-
-	glDrawArrays (GL_QUADS, 0, 4);
-
-	glDisableClientState (GL_VERTEX_ARRAY);
-    }
-
     glDisable (GL_BLEND);
     glDisable (GL_DEPTH_TEST);
 
@@ -1873,8 +1842,6 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
 static void
 _cairo_gl_surface_ensure_depth_buffer (cairo_gl_surface_t *surface)
 {
-    GLenum err;
-
     if (surface->depth_stencil_tex)
 	return;
 
@@ -1889,44 +1856,27 @@ _cairo_gl_surface_ensure_depth_buffer (cairo_gl_surface_t *surface)
 			       GL_DEPTH_ATTACHMENT_EXT,
 			       GL_TEXTURE_2D,
 			       surface->depth_stencil_tex, 0);
-#if 0
     glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
 			       GL_STENCIL_ATTACHMENT_EXT,
 			       GL_TEXTURE_2D,
 			       surface->depth_stencil_tex, 0);
-#endif
-
-    while ((err = glGetError ()))
-	fprintf (stderr, "ensure depth buffer: GL error 0x%08x\n", (int) err);
-
 }
 
 
 static cairo_status_t
 _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
-	                           cairo_operator_t op,
 				   cairo_region_t *clip_region)
 {
     GLfloat vertices_stack[CAIRO_STACK_ARRAY_LENGTH(GLfloat)], *vertices;
     int num_rectangles, i, n;
 
-    if (clip_region == NULL) {
-	if (! _cairo_operator_bounded_by_mask (op)) {
-	    _cairo_gl_surface_ensure_depth_buffer (surface);
-	    glDepthMask (GL_TRUE);
-	    glEnable (GL_DEPTH_TEST);
-	    glClearDepth (1.);
-	    glClear (GL_DEPTH_BUFFER_BIT);
-	    glDepthFunc (GL_LESS);
-	}
-
+    if (clip_region == NULL)
 	return CAIRO_STATUS_SUCCESS;
-    }
 
     num_rectangles = cairo_region_num_rectangles (clip_region);
 
     vertices = vertices_stack;
-    if (num_rectangles * 12 > ARRAY_LENGTH (vertices_stack)) {
+    if (num_rectangles * 8 > ARRAY_LENGTH (vertices_stack)) {
 	vertices = _cairo_malloc_ab (num_rectangles,
 				     4*3*sizeof (vertices[0]));
 	if (unlikely (vertices == NULL))
@@ -1939,16 +1889,12 @@ _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
 	cairo_region_get_rectangle (clip_region, i, &rect);
 	vertices[n++] = rect.x;
 	vertices[n++] = rect.y;
-	vertices[n++] = 1.;
 	vertices[n++] = rect.x + rect.width;
 	vertices[n++] = rect.y;
-	vertices[n++] = 1.;
 	vertices[n++] = rect.x + rect.width;
 	vertices[n++] = rect.y + rect.height;
-	vertices[n++] = 1.;
 	vertices[n++] = rect.x;
-	vertices[n++] = rect.y + rect. height;
-	vertices[n++] = 1.;
+	vertices[n++] = rect.y +rect. height;
     }
 
     _cairo_gl_surface_ensure_depth_buffer (surface);
@@ -1959,16 +1905,16 @@ _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
     glDepthMask (GL_TRUE);
-    glClearDepth (-1.);
     glClear (GL_DEPTH_BUFFER_BIT);
 
-    glVertexPointer (3, GL_FLOAT, sizeof (GLfloat) * 3, vertices);
+    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
     glEnableClientState (GL_VERTEX_ARRAY);
     glDrawArrays (GL_QUADS, 0, 4 * num_rectangles);
     glDisableClientState (GL_VERTEX_ARRAY);
 
     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-    glDepthFunc (GL_LESS);
+    glDepthFunc (GL_EQUAL);
+    glDepthMask (GL_FALSE);
 
     if (vertices != vertices_stack)
 	free (vertices);
@@ -2019,7 +1965,7 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     _cairo_gl_context_acquire (dst->ctx);
     _cairo_gl_set_destination (dst);
 
-    status = _cairo_gl_surface_set_clip_region (dst, op, clip_region);
+    status = _cairo_gl_surface_set_clip_region (dst, clip_region);
     if (unlikely (status)) {
 	_cairo_gl_surface_span_renderer_destroy (renderer);
 	return _cairo_span_renderer_create_in_error (status);
commit 74bdea1249bc1fcd223e9bab83d9b9e22fa98b99
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 16:20:54 2009 +0100

    [spans] Emit empty rows.
    
    A problem that does not present itself whilst using spans to intermediate
    masks is that the tor-span-convertor did not emit the empty rows. When
    compositing directly using spans with an unbounded operator this was
    causing rendering artefacts, for overlapping-glyphs and the gl backend.

diff --git a/src/cairo-tor-scan-converter.c b/src/cairo-tor-scan-converter.c
index 7d37f47..f6b5b43 100644
--- a/src/cairo-tor-scan-converter.c
+++ b/src/cairo-tor-scan-converter.c
@@ -131,6 +131,9 @@ blit_with_span_renderer(
     int				 xmin,
     int				 xmax);
 
+static glitter_status_t
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y);
+
 #define GLITTER_BLIT_COVERAGES_ARGS \
 	cairo_span_renderer_t *span_renderer, \
 	struct pool *span_pool
@@ -144,6 +147,12 @@ blit_with_span_renderer(
 	return status;							\
 } while (0)
 
+#define GLITTER_BLIT_COVERAGES_EMPTY(y, xmin, xmax) do {		\
+    cairo_status_t status = blit_empty_with_span_renderer (span_renderer, y); \
+    if (unlikely (status))						\
+	return status;							\
+} while (0)
+
 /*-------------------------------------------------------------------------
  * glitter-paths.h
  */
@@ -1889,6 +1898,12 @@ blit_with_span_renderer(
     return renderer->render_row (renderer, y, spans, num_spans);
 }
 
+static glitter_status_t
+blit_empty_with_span_renderer (cairo_span_renderer_t *renderer, int y)
+{
+    return renderer->render_row (renderer, y, NULL, 0);
+}
+
 struct _cairo_tor_scan_converter {
     cairo_scan_converter_t base;
     glitter_scan_converter_t converter[1];
commit 1e4b9ab04bfe18f50b9b7c2cdb75d5828e33ef69
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Fri Jul 31 15:27:20 2009 +0100

    [gl] Return status from _render_glyphs()

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 3a21547..579fb9b 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -395,8 +395,10 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     _cairo_gl_set_src_operand (ctx, &composite_setup);
 
     _cairo_scaled_font_freeze_cache (scaled_font);
-    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
+    if (! _cairo_gl_surface_owns_font (dst, scaled_font)) {
+	status = CAIRO_INT_STATUS_UNSUPPORTED;
 	goto CLEANUP_FONT;
+    }
 
     if (scaled_font->surface_private == NULL) {
 	/* XXX couple into list to remove on context destruction */
@@ -535,7 +537,7 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     _cairo_gl_operand_destroy (&composite_setup.src);
 
     *remaining_glyphs = num_glyphs - i;
-    return CAIRO_STATUS_SUCCESS;
+    return status;
 }
 
 static cairo_int_status_t
commit f7121ca88844fa70ecb1a35f3f6c9b653324718d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 20:48:26 2009 +0100

    [gl] Fixup unbounded regions after spans

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index c7b97a7..b586a67 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1812,6 +1812,37 @@ _cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
     glActiveTexture (GL_TEXTURE1);
     glDisable (GL_TEXTURE_2D);
 
+    if (! _cairo_operator_bounded_by_mask (renderer->op)) {
+	struct {
+	    GLfloat x,y;
+	} vertices[4];
+
+	glBlendFunc (GL_ZERO, GL_ZERO);
+
+	vertices[0].x = renderer->composite_rectangles.dst.x;
+	vertices[0].y = renderer->composite_rectangles.dst.y;
+
+	vertices[1].x = renderer->composite_rectangles.dst.x +
+	                renderer->composite_rectangles.width;
+	vertices[1].y = renderer->composite_rectangles.dst.y;
+
+	vertices[2].x = renderer->composite_rectangles.dst.x +
+	                renderer->composite_rectangles.width;
+	vertices[2].y = renderer->composite_rectangles.dst.y +
+	                renderer->composite_rectangles.height;
+
+	vertices[3].x = renderer->composite_rectangles.dst.x;
+	vertices[3].y = renderer->composite_rectangles.dst.y +
+	                renderer->composite_rectangles.height;
+
+	glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
+	glEnableClientState (GL_VERTEX_ARRAY);
+
+	glDrawArrays (GL_QUADS, 0, 4);
+
+	glDisableClientState (GL_VERTEX_ARRAY);
+    }
+
     glDisable (GL_BLEND);
     glDisable (GL_DEPTH_TEST);
 
@@ -1842,6 +1873,8 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
 static void
 _cairo_gl_surface_ensure_depth_buffer (cairo_gl_surface_t *surface)
 {
+    GLenum err;
+
     if (surface->depth_stencil_tex)
 	return;
 
@@ -1856,27 +1889,44 @@ _cairo_gl_surface_ensure_depth_buffer (cairo_gl_surface_t *surface)
 			       GL_DEPTH_ATTACHMENT_EXT,
 			       GL_TEXTURE_2D,
 			       surface->depth_stencil_tex, 0);
+#if 0
     glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT,
 			       GL_STENCIL_ATTACHMENT_EXT,
 			       GL_TEXTURE_2D,
 			       surface->depth_stencil_tex, 0);
+#endif
+
+    while ((err = glGetError ()))
+	fprintf (stderr, "ensure depth buffer: GL error 0x%08x\n", (int) err);
+
 }
 
 
 static cairo_status_t
 _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
+	                           cairo_operator_t op,
 				   cairo_region_t *clip_region)
 {
     GLfloat vertices_stack[CAIRO_STACK_ARRAY_LENGTH(GLfloat)], *vertices;
     int num_rectangles, i, n;
 
-    if (clip_region == NULL)
+    if (clip_region == NULL) {
+	if (! _cairo_operator_bounded_by_mask (op)) {
+	    _cairo_gl_surface_ensure_depth_buffer (surface);
+	    glDepthMask (GL_TRUE);
+	    glEnable (GL_DEPTH_TEST);
+	    glClearDepth (1.);
+	    glClear (GL_DEPTH_BUFFER_BIT);
+	    glDepthFunc (GL_LESS);
+	}
+
 	return CAIRO_STATUS_SUCCESS;
+    }
 
     num_rectangles = cairo_region_num_rectangles (clip_region);
 
     vertices = vertices_stack;
-    if (num_rectangles * 8 > ARRAY_LENGTH (vertices_stack)) {
+    if (num_rectangles * 12 > ARRAY_LENGTH (vertices_stack)) {
 	vertices = _cairo_malloc_ab (num_rectangles,
 				     4*3*sizeof (vertices[0]));
 	if (unlikely (vertices == NULL))
@@ -1889,12 +1939,16 @@ _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
 	cairo_region_get_rectangle (clip_region, i, &rect);
 	vertices[n++] = rect.x;
 	vertices[n++] = rect.y;
+	vertices[n++] = 1.;
 	vertices[n++] = rect.x + rect.width;
 	vertices[n++] = rect.y;
+	vertices[n++] = 1.;
 	vertices[n++] = rect.x + rect.width;
 	vertices[n++] = rect.y + rect.height;
+	vertices[n++] = 1.;
 	vertices[n++] = rect.x;
-	vertices[n++] = rect.y +rect. height;
+	vertices[n++] = rect.y + rect. height;
+	vertices[n++] = 1.;
     }
 
     _cairo_gl_surface_ensure_depth_buffer (surface);
@@ -1905,16 +1959,16 @@ _cairo_gl_surface_set_clip_region (cairo_gl_surface_t *surface,
     glColorMask (GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
 
     glDepthMask (GL_TRUE);
+    glClearDepth (-1.);
     glClear (GL_DEPTH_BUFFER_BIT);
 
-    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
+    glVertexPointer (3, GL_FLOAT, sizeof (GLfloat) * 3, vertices);
     glEnableClientState (GL_VERTEX_ARRAY);
     glDrawArrays (GL_QUADS, 0, 4 * num_rectangles);
     glDisableClientState (GL_VERTEX_ARRAY);
 
     glColorMask (GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
-    glDepthFunc (GL_EQUAL);
-    glDepthMask (GL_FALSE);
+    glDepthFunc (GL_LESS);
 
     if (vertices != vertices_stack)
 	free (vertices);
@@ -1965,7 +2019,7 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     _cairo_gl_context_acquire (dst->ctx);
     _cairo_gl_set_destination (dst);
 
-    status = _cairo_gl_surface_set_clip_region (dst, clip_region);
+    status = _cairo_gl_surface_set_clip_region (dst, op, clip_region);
     if (unlikely (status)) {
 	_cairo_gl_surface_span_renderer_destroy (renderer);
 	return _cairo_span_renderer_create_in_error (status);
commit 5631f01075a030255c5eb47a8ee600b062705d0f
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 18:56:12 2009 +0100

    [gl] Cache texture snapshots.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index d99ffc2..3a21547 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -53,18 +53,34 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
     cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
     cairo_gl_glyph_private_t *glyph_private;
     cairo_rtree_node_t *node = NULL;
+    cairo_image_surface_t *clone = NULL;
     cairo_status_t status;
     int width, height;
     GLenum internal_format, format, type;
     cairo_bool_t has_alpha;
 
-    status = _cairo_gl_get_image_format_and_type (glyph_surface->pixman_format,
-						  &internal_format,
-						  &format,
-						  &type,
-						  &has_alpha);
-    if (status != CAIRO_STATUS_SUCCESS)
-	return status;
+    if (! _cairo_gl_get_image_format_and_type (glyph_surface->pixman_format,
+					       &internal_format,
+					       &format,
+					       &type,
+					       &has_alpha))
+    {
+	cairo_bool_t is_supported;
+
+	clone = _cairo_image_surface_coerce (glyph_surface,
+		                             _cairo_format_from_content (glyph_surface->base.content));
+	if (unlikely (clone->base.status))
+	    return clone->base.status;
+
+	is_supported =
+	    _cairo_gl_get_image_format_and_type (clone->pixman_format,
+					         &internal_format,
+						 &format,
+						 &type,
+						 &has_alpha);
+	assert (is_supported);
+	glyph_surface = clone;
+    }
 
     width = glyph_surface->width;
     if (width < GLYPH_CACHE_MIN_SIZE)
@@ -112,6 +128,8 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
     glyph_private->p2.y =
 	(node->y + glyph_surface->height) / (double) cache->height;
 
+    cairo_surface_destroy (&clone->base);
+
     return CAIRO_STATUS_SUCCESS;
 }
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 378272b..f064d18 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -178,7 +178,7 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 cairo_private void
 _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand);
 
-cairo_private cairo_status_t
+cairo_private cairo_bool_t
 _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
 				     GLenum *internal_format, GLenum *format,
 				     GLenum *type, cairo_bool_t *has_alpha);
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 0bbe401..c7b97a7 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -175,7 +175,7 @@ _cairo_gl_context_acquire (cairo_gl_context_t *ctx)
     return ctx;
 }
 
-cairo_status_t
+cairo_bool_t
 _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
 				     GLenum *internal_format, GLenum *format,
 				     GLenum *type, cairo_bool_t *has_alpha)
@@ -187,82 +187,82 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
 	*internal_format = GL_RGBA;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_INT_8_8_8_8_REV;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_x8r8g8b8:
 	*internal_format = GL_RGB;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_INT_8_8_8_8_REV;
 	*has_alpha = FALSE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_a8b8g8r8:
 	*internal_format = GL_RGBA;
 	*format = GL_RGBA;
 	*type = GL_UNSIGNED_INT_8_8_8_8_REV;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_x8b8g8r8:
 	*internal_format = GL_RGB;
 	*format = GL_RGBA;
 	*type = GL_UNSIGNED_INT_8_8_8_8_REV;
 	*has_alpha = FALSE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_b8g8r8a8:
 	*internal_format = GL_BGRA;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_INT_8_8_8_8;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_b8g8r8x8:
 	*internal_format = GL_RGB;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_INT_8_8_8_8;
 	*has_alpha = FALSE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_r8g8b8:
 	*internal_format = GL_RGB;
 	*format = GL_RGB;
 	*type = GL_UNSIGNED_BYTE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_b8g8r8:
 	*internal_format = GL_RGB;
 	*format = GL_BGR;
 	*type = GL_UNSIGNED_BYTE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_r5g6b5:
 	*internal_format = GL_RGB;
 	*format = GL_RGB;
 	*type = GL_UNSIGNED_SHORT_5_6_5;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_b5g6r5:
 	*internal_format = GL_RGB;
 	*format = GL_RGB;
 	*type = GL_UNSIGNED_SHORT_5_6_5_REV;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_a1r5g5b5:
 	*internal_format = GL_RGBA;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_x1r5g5b5:
 	*internal_format = GL_RGB;
 	*format = GL_BGRA;
 	*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
 	*has_alpha = FALSE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_a1b5g5r5:
 	*internal_format = GL_RGBA;
 	*format = GL_RGBA;
 	*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_x1b5g5r5:
 	*internal_format = GL_RGB;
 	*format = GL_RGBA;
 	*type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
 	*has_alpha = FALSE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
     case PIXMAN_a8:
 	*internal_format = GL_ALPHA;
 	*format = GL_ALPHA;
 	*type = GL_UNSIGNED_BYTE;
-	return CAIRO_STATUS_SUCCESS;
+	return TRUE;
 
     case PIXMAN_a2b10g10r10:
     case PIXMAN_x2b10g10r10:
@@ -294,7 +294,7 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
     case PIXMAN_a2r10g10b10:
 #endif
     default:
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+	return FALSE;
     }
 }
 
@@ -617,26 +617,28 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
     GLenum internal_format, format, type;
     cairo_bool_t has_alpha;
     cairo_image_surface_t *clone = NULL;
-    cairo_status_t status;
     int cpp;
 
-    status = _cairo_gl_get_image_format_and_type (src->pixman_format,
-						  &internal_format,
-						  &format,
-						  &type,
-						  &has_alpha);
-    if (status != CAIRO_STATUS_SUCCESS) {
+    if (! _cairo_gl_get_image_format_and_type (src->pixman_format,
+					      &internal_format,
+					      &format,
+					      &type,
+					      &has_alpha))
+    {
+	cairo_bool_t is_supported;
+
 	clone = _cairo_image_surface_coerce (src,
 		_cairo_format_from_content (src->base.content));
 	if (unlikely (clone->base.status))
 	    return clone->base.status;
 
-	status = _cairo_gl_get_image_format_and_type (clone->pixman_format,
-		                                      &internal_format,
-						      &format,
-						      &type,
-						      &has_alpha);
-	assert (status == CAIRO_STATUS_SUCCESS);
+	is_supported =
+	    _cairo_gl_get_image_format_and_type (clone->pixman_format,
+		                                 &internal_format,
+						 &format,
+						 &type,
+						 &has_alpha);
+	assert (is_supported);
 	src = clone;
     }
 
@@ -925,105 +927,117 @@ _cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
 }
 
 static cairo_status_t
-_cairo_gl_pattern_image_texture_setup (cairo_gl_composite_operand_t *operand,
-				       const cairo_pattern_t *src,
-				       cairo_gl_surface_t *dst,
-				       int src_x, int src_y,
-				       int dst_x, int dst_y,
-				       int width, int height)
+_cairo_gl_pattern_surface_texture_setup (cairo_gl_composite_operand_t *operand,
+				         const cairo_surface_pattern_t *src,
+					 cairo_gl_surface_t *dst,
+					 int src_x, int src_y,
+					 int dst_x, int dst_y)
 {
-    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *)src;
-    cairo_image_surface_t *image_surface;
+    cairo_surface_t *source = src->surface;
+    cairo_gl_surface_t *gl_surface;
+    cairo_status_t status;
     cairo_matrix_t m;
     cairo_surface_attributes_t *attributes;
-    GLuint tex;
-    GLenum format, internal_format, type;
-    cairo_status_t status;
-
-    if (src->type != CAIRO_PATTERN_TYPE_SURFACE)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
 
-    if (!_cairo_surface_is_image (surface_pattern->surface))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    if (source->backend != &_cairo_gl_surface_backend) {
+	gl_surface = (cairo_gl_surface_t *)
+	    _cairo_surface_has_snapshot (source,
+		                         &_cairo_gl_surface_backend,
+		                         source->content);
+	if (gl_surface == NULL) {
+	    cairo_image_surface_t *image;
+	    void *image_extra;
+
+	    status = _cairo_surface_acquire_source_image (source,
+		                                          &image, &image_extra);
+	    if (unlikely (status))
+		return status;
+
+	    gl_surface = (cairo_gl_surface_t *)
+		_cairo_gl_surface_create_similar (&dst->base,
+			                          image->base.content,
+						  image->width, image->height);
+	    if (gl_surface == NULL) {
+		_cairo_surface_release_source_image (source,
+			                             image, image_extra);
+		return CAIRO_INT_STATUS_UNSUPPORTED;
+	    }
+	    if (unlikely (gl_surface->base.status)) {
+		_cairo_surface_release_source_image (source,
+			                             image, image_extra);
+		return gl_surface->base.status;
+	    }
 
-    image_surface = (cairo_image_surface_t *)surface_pattern->surface;
-    if (image_surface->width > dst->ctx->max_texture_size ||
-	image_surface->height > dst->ctx->max_texture_size)
-    {
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
+	    status = _cairo_gl_surface_draw_image (gl_surface, image,
+		                                   0, 0,
+						   image->width, image->height,
+						   0, 0);
+	    _cairo_surface_release_source_image (source, image, image_extra);
+	    if (unlikely (status)) {
+		cairo_surface_destroy (&gl_surface->base);
+		return status;
+	    }
 
-    /* The textures we create almost always has appropriate alpha channel
-     * contents.  But sometimes GL sucks at image specification and we end up
-     * with junk in the alpha.
-     */
-    operand->operand.texture.has_alpha = TRUE;
+	    status = _cairo_surface_attach_snapshot (source,
+		                                     &gl_surface->base,
+						     cairo_surface_destroy);
+	    if (unlikely (status)) {
+		cairo_surface_destroy (&gl_surface->base);
+		return status;
+	    }
 
-    status = _cairo_gl_get_image_format_and_type (image_surface->pixman_format,
-						  &internal_format, &format,
-						  &type,
-						  &operand->operand.texture.has_alpha);
-    if (status)
-	return status;
+	    /* XXX consider placing a bound on snapshot caches */
+	}
+    } else {
+	gl_surface = (cairo_gl_surface_t *) source;
+    }
 
-    glGenTextures (1, &tex);
-    glBindTexture (GL_TEXTURE_2D, tex);
-    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-    assert (((image_surface->stride * image_surface->depth) %
-	     image_surface->depth) == 0);
-    glPixelStorei (GL_UNPACK_ROW_LENGTH,
-		   image_surface->stride /
-		   (PIXMAN_FORMAT_BPP (image_surface->pixman_format) / 8));
-    /* The filter will be correctly set up later, but for now we want to
-     * hint to glTexImage that we're not mipmapping.
-     */
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
-		  image_surface->width, image_surface->height, 0,
-		  format, type, image_surface->data);
-    glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+    operand->operand.texture.surface = NULL;
+    operand->operand.texture.tex = gl_surface->tex;
+    switch (gl_surface->base.content) {
+	case CAIRO_CONTENT_ALPHA:
+	case CAIRO_CONTENT_COLOR_ALPHA:
+	    operand->operand.texture.has_alpha = TRUE;
+	    break;
+	case CAIRO_CONTENT_COLOR:
+	    operand->operand.texture.has_alpha = FALSE;
+	    break;
+    }
 
     attributes = &operand->operand.texture.attributes;
 
     operand->type = OPERAND_TEXTURE;
-    operand->operand.texture.tex = tex;
+    operand->operand.texture.tex = gl_surface->tex;
     operand->operand.texture.surface = NULL;
-    attributes->matrix = src->matrix;
-    attributes->extend = src->extend;
-    attributes->filter = src->filter;
+    attributes->matrix = src->base.matrix;
+    attributes->extend = src->base.extend;
+    attributes->filter = src->base.filter;
     /* Demote the filter if we're doing a 1:1 mapping of pixels. */
-    if ((src->filter == CAIRO_FILTER_GOOD ||
-	 src->filter == CAIRO_FILTER_BEST ||
-	 src->filter == CAIRO_FILTER_BILINEAR) &&
-	_cairo_matrix_is_pixel_exact (&src->matrix)) {
+    if ((attributes->filter == CAIRO_FILTER_GOOD ||
+	 attributes->filter == CAIRO_FILTER_BEST ||
+	 attributes->filter == CAIRO_FILTER_BILINEAR) &&
+	_cairo_matrix_is_pixel_exact (&attributes->matrix))
+    {
 	attributes->filter = CAIRO_FILTER_NEAREST;
     }
 
     attributes->x_offset = 0;
     attributes->y_offset = 0;
 
-
     /* Set up translation matrix for
      * (unnormalized dst -> unnormalized src)
      */
-    cairo_matrix_init_translate (&m,
-				 src_x - dst_x,
-				 src_y - dst_y);
-    cairo_matrix_multiply (&attributes->matrix,
-			   &m,
-			   &attributes->matrix);
+    cairo_matrix_init_translate (&m, src_x - dst_x, src_y - dst_y);
+    cairo_matrix_multiply (&attributes->matrix, &m, &attributes->matrix);
 
     /* Translate the matrix from
      * (unnormalized src -> unnormalized src) to
      * (unnormalized dst -> normalized src)
      */
     cairo_matrix_init_scale (&m,
-			     1.0 / image_surface->width,
-			     1.0 / image_surface->height);
-    cairo_matrix_multiply (&attributes->matrix,
-			   &attributes->matrix,
-			   &m);
+			     1.0 / gl_surface->width,
+			     1.0 / gl_surface->height);
+    cairo_matrix_multiply (&attributes->matrix, &attributes->matrix, &m);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -1047,13 +1061,17 @@ _cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
 
     attributes = &operand->operand.texture.attributes;
 
-    /* First, try to just upload it to a texture if it's an image surface. */
-    status = _cairo_gl_pattern_image_texture_setup (operand, src, dst,
-						    src_x, src_y,
-						    dst_x, dst_y,
-						    width, height);
-    if (status == CAIRO_STATUS_SUCCESS)
-	return CAIRO_STATUS_SUCCESS;
+    /* First, try to just upload it to a texture if it's a surface. */
+    if (src->type == CAIRO_PATTERN_TYPE_SURFACE) {
+	status =
+	    _cairo_gl_pattern_surface_texture_setup (operand,
+		                                     (cairo_surface_pattern_t *) src,
+						     dst,
+						     src_x, src_y,
+						     dst_x, dst_y);
+	if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+	    return status;
+    }
 
     status = _cairo_pattern_acquire_surface (src, &dst->base,
 					     CAIRO_CONTENT_COLOR_ALPHA,
@@ -1106,6 +1124,18 @@ _cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand,
+	                      const cairo_color_t *color)
+{
+    operand->type = OPERAND_CONSTANT;
+    operand->operand.constant.color[0] = color->red   * color->alpha;
+    operand->operand.constant.color[1] = color->green * color->alpha;
+    operand->operand.constant.color[2] = color->blue  * color->alpha;
+    operand->operand.constant.color[3] = color->alpha;
+    return CAIRO_STATUS_SUCCESS;
+}
+
 cairo_int_status_t
 _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		       const cairo_pattern_t *pattern,
@@ -1114,22 +1144,53 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		       int dst_x, int dst_y,
 		       int width, int height)
 {
-    cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *)pattern;
-
     operand->pattern = pattern;
 
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
-	operand->type = OPERAND_CONSTANT;
-	operand->operand.constant.color[0] = solid->color.red * solid->color.alpha;
-	operand->operand.constant.color[1] = solid->color.green * solid->color.alpha;
-	operand->operand.constant.color[2] = solid->color.blue * solid->color.alpha;
-	operand->operand.constant.color[3] = solid->color.alpha;
-	return CAIRO_STATUS_SUCCESS;
-    default:
-    case CAIRO_PATTERN_TYPE_SURFACE:
+	return _cairo_gl_solid_operand_init (operand,
+		                             &((cairo_solid_pattern_t *) pattern)->color);
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
+	{
+	    cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
+
+	    /* Fast path for gradients with less than 2 color stops.
+	     * Required to prevent _cairo_pattern_acquire_surface() returning
+	     * a solid color which is cached beyond the life of the context.
+	     */
+	    if (src->n_stops < 2) {
+		if (src->n_stops) {
+		    return _cairo_gl_solid_operand_init (operand,
+			                                 &src->stops->color);
+		} else {
+		    return _cairo_gl_solid_operand_init (operand,
+			                                 CAIRO_COLOR_TRANSPARENT);
+		}
+	    } else {
+		unsigned int i;
+
+		/* Is the gradient a uniform colour?
+		 * Happens more often than you would believe.
+		 */
+		for (i = 1; i < src->n_stops; i++) {
+		    if (! _cairo_color_equal (&src->stops[0].color,
+				              &src->stops[i].color))
+		    {
+			break;
+		    }
+		}
+		if (i == src->n_stops) {
+		    return _cairo_gl_solid_operand_init (operand,
+			                                 &src->stops->color);
+		}
+	    }
+	}
+
+	/* fall through */
+
+    default:
+    case CAIRO_PATTERN_TYPE_SURFACE:
 	operand->type = OPERAND_TEXTURE;
 	return _cairo_gl_pattern_texture_setup (operand,
 						pattern, dst,
@@ -1152,8 +1213,6 @@ _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
 	    _cairo_pattern_release_surface (operand->pattern,
 					    &surface->base,
 					    &operand->operand.texture.attributes);
-	} else {
-	    glDeleteTextures (1, &operand->operand.texture.tex);
 	}
 	break;
     }
commit 3b0bb2cfd2ad8579e88f1c0ab46f5ccf762e0974
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 18:04:59 2009 +0100

    [gl] Replace DrawPixels with TexSubImage

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 72f79e5..0bbe401 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -614,42 +614,46 @@ _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 			      int width, int height,
 			      int dst_x, int dst_y)
 {
-    char *temp_data;
-    int y;
-    unsigned int cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
     GLenum internal_format, format, type;
-    char *src_data_start;
     cairo_bool_t has_alpha;
+    cairo_image_surface_t *clone = NULL;
     cairo_status_t status;
+    int cpp;
 
     status = _cairo_gl_get_image_format_and_type (src->pixman_format,
 						  &internal_format,
 						  &format,
 						  &type,
 						  &has_alpha);
-    if (status != CAIRO_STATUS_SUCCESS)
-	return status;
-
-    /* Write the data to a temporary as GL wants bottom-to-top data
-     * screen-wise, and we want top-to-bottom.
-     */
-    temp_data = malloc (width * height * cpp);
-    if (temp_data == NULL)
-	return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+    if (status != CAIRO_STATUS_SUCCESS) {
+	clone = _cairo_image_surface_coerce (src,
+		_cairo_format_from_content (src->base.content));
+	if (unlikely (clone->base.status))
+	    return clone->base.status;
 
-    src_data_start = (char *)src->data + (src_y * src->stride) + (src_x * cpp);
-    for (y = 0; y < height; y++) {
-	memcpy (temp_data + y * width * cpp, src_data_start +
-		y * src->stride,
-		width * cpp);
+	status = _cairo_gl_get_image_format_and_type (clone->pixman_format,
+		                                      &internal_format,
+						      &format,
+						      &type,
+						      &has_alpha);
+	assert (status == CAIRO_STATUS_SUCCESS);
+	src = clone;
     }
 
-    _cairo_gl_set_destination (dst);
-    glRasterPos2i (dst_x, dst_y);
+    cpp = PIXMAN_FORMAT_BPP (src->pixman_format) / 8;
+
+    glBindTexture (GL_TEXTURE_2D, dst->tex);
+    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
-    glDrawPixels (width, height, format, type, temp_data);
+    glPixelStorei (GL_UNPACK_ROW_LENGTH, src->stride / cpp);
+    glTexSubImage2D (GL_TEXTURE_2D, 0,
+	             dst_x, dst_y, width, height,
+		     format, GL_UNSIGNED_BYTE,
+		     src->data + src_y * src->stride + src_x * cpp);
+    glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
 
-    free (temp_data);
+    cairo_surface_destroy (&clone->base);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -836,19 +840,9 @@ _cairo_gl_surface_clone_similar (void		     *abstract_surface,
 
 	return CAIRO_STATUS_SUCCESS;
     } else if (_cairo_surface_is_image (src)) {
-	cairo_gl_surface_t *clone;
 	cairo_image_surface_t *image_src = (cairo_image_surface_t *)src;
+	cairo_gl_surface_t *clone;
 	cairo_status_t status;
-	GLenum internal_format, format, type;
-	cairo_bool_t has_alpha;
-
-	status = _cairo_gl_get_image_format_and_type (image_src->pixman_format,
-						      &internal_format,
-						      &format,
-						      &type,
-						      &has_alpha);
-	if (status != CAIRO_STATUS_SUCCESS)
-	    return status;
 
 	clone = (cairo_gl_surface_t *)
 	    _cairo_gl_surface_create_similar (&surface->base,
commit 4b7a1a2da7a7f55db788cbfb3164f9f948bbb26b
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 17:32:30 2009 +0100

    [gl] Disable the EXTEND_REFLECT convertor
    
    As we can handle EXTEND_REFLECT via the samplers, we do not need to
    convert such patterns to EXTEND_REPEAT during acquisition.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 72d3876..72f79e5 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1065,7 +1065,7 @@ _cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
 					     CAIRO_CONTENT_COLOR_ALPHA,
 					     src_x, src_y,
 					     width, height,
-					     CAIRO_PATTERN_ACQUIRE_NO_REFLECT,
+					     CAIRO_PATTERN_ACQUIRE_NONE,
 					     (cairo_surface_t **)
 					     &surface,
 					     attributes);
commit 780a95b024c5880353ffc256a634eb3bcc641601
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 16:49:04 2009 +0100

    [gl] Trim glyphs to surface/clip extents.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 53129f5..d99ffc2 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -520,7 +520,6 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     return CAIRO_STATUS_SUCCESS;
 }
 
-
 static cairo_int_status_t
 _cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t	*dst,
 			                cairo_operator_t	 op,
@@ -586,6 +585,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 			       int			*remaining_glyphs)
 {
     cairo_gl_surface_t *dst = abstract_dst;
+    cairo_rectangle_int_t surface_extents;
     cairo_rectangle_int_t extents;
     cairo_region_t *clip_region = NULL;
     cairo_solid_pattern_t solid_pattern;
@@ -598,8 +598,6 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     if (! _cairo_gl_operator_is_supported (op))
 	return UNSUPPORTED ("unsupported operator");
 
-    /* Just let unbounded operators go through the fallback code
-     * instead of trying to do the fixups here */
     if (! _cairo_operator_bounded_by_mask (op))
 	use_mask |= TRUE;
 
@@ -678,8 +676,18 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	    return status;
 
 	use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED;
+
+	if (! _cairo_rectangle_intersect (&extents,
+		                          _cairo_clip_get_extents (clip)))
+	    goto EMPTY;
     }
 
+    surface_extents.x = surface_extents.y = 0;
+    surface_extents.width = dst->width;
+    surface_extents.height = dst->height;
+    if (! _cairo_rectangle_intersect (&extents, &surface_extents))
+	goto EMPTY;
+
     if (use_mask) {
 	return _cairo_gl_surface_show_glyphs_via_mask (dst, op,
 			                               source,
@@ -694,6 +702,13 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	                   op, source,
 			   glyphs, num_glyphs, &extents,
 			   scaled_font, clip_region, remaining_glyphs);
+
+EMPTY:
+    *remaining_glyphs = 0;
+    if (! _cairo_operator_bounded_by_mask (op))
+	return _cairo_surface_paint (&dst->base, op, source, clip);
+    else
+	return CAIRO_STATUS_SUCCESS;
 }
 
 void
commit b1227dea29d3e357cc69c49eed0ae10d5578f24a
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 16:39:52 2009 +0100

    [gl] Render glyphs to an intermediate mask as opposed to falling back.
    
    There are many common scenarios, mostly involving overlapping glyphs,
    for which to guarantee correct rendering we have to composite the glyphs
    via an explicit mask. That is instead of just blending the glyphs on to
    the destination, we have to add the glyphs to a mask, and then composite
    that mask+source with the destination.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 5be984f..53129f5 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -323,124 +323,32 @@ _cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
     setup->num_prims++;
 }
 
-cairo_int_status_t
-_cairo_gl_surface_show_glyphs (void			*abstract_dst,
-			       cairo_operator_t		 op,
-			       const cairo_pattern_t	*source,
-			       cairo_glyph_t		*glyphs,
-			       int			 num_glyphs,
-			       cairo_scaled_font_t	*scaled_font,
-			       cairo_clip_t		*clip,
-			       int			*remaining_glyphs)
+static cairo_status_t
+_render_glyphs (cairo_gl_surface_t	*dst,
+	        int dst_x, int dst_y,
+	        cairo_operator_t	 op,
+		const cairo_pattern_t	*source,
+		cairo_glyph_t		*glyphs,
+		int			 num_glyphs,
+		const cairo_rectangle_int_t *glyph_extents,
+		cairo_scaled_font_t	*scaled_font,
+		cairo_region_t		*clip_region,
+		int			*remaining_glyphs)
 {
-    cairo_gl_surface_t *dst = abstract_dst;
-    cairo_gl_context_t *ctx;
-    cairo_rectangle_int_t extents;
     cairo_format_t last_format = (cairo_format_t) -1;
     cairo_gl_glyph_cache_t *cache = NULL;
-    cairo_status_t status;
-    int i = 0;
-    cairo_region_t *clip_region = NULL;
-    cairo_solid_pattern_t solid_pattern;
+    cairo_gl_context_t *ctx;
     cairo_gl_glyphs_setup_t setup;
     cairo_gl_composite_setup_t composite_setup;
-    cairo_bool_t overlap;
+    cairo_status_t status;
+    int i = 0;
     GLuint vbo = 0;
 
-    if (! GLEW_ARB_vertex_buffer_object)
-	return UNSUPPORTED ("requires ARB_vertex_buffer_object");
-
-    if (! _cairo_gl_operator_is_supported (op))
-	return UNSUPPORTED ("unsupported operator");
-
-    /* Just let unbounded operators go through the fallback code
-     * instead of trying to do the fixups here */
-    if (! _cairo_operator_bounded_by_mask (op))
-	return UNSUPPORTED ("unhandled unbound operator");
-
-    /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
-     * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
-     * is:
-     *     mask IN clip ? src OP dest : dest
-     * or more simply:
-     *     mask IN CLIP ? 0 : dest
-     *
-     * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
-     *
-     * The model we use in _cairo_gl_set_operator() is Render's:
-     *     src IN mask IN clip OP dest
-     * which would boil down to:
-     *     0 (bounded by the extents of the drawing).
-     *
-     * However, we can do a Render operation using an opaque source
-     * and DEST_OUT to produce:
-     *    1 IN mask IN clip DEST_OUT dest
-     * which is
-     *    mask IN clip ? 0 : dest
-     */
-    if (op == CAIRO_OPERATOR_CLEAR) {
-	_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
-				   CAIRO_CONTENT_COLOR);
-	source = &solid_pattern.base;
-	op = CAIRO_OPERATOR_DEST_OUT;
-    }
-
-    /* For SOURCE, cairo's rendering equation is:
-     *     (mask IN clip) ? src OP dest : dest
-     * or more simply:
-     *     (mask IN clip) ? src : dest.
-     *
-     * If we just used the Render equation, we would get:
-     *     (src IN mask IN clip) OP dest
-     * or:
-     *     (src IN mask IN clip) bounded by extents of the drawing.
-     *
-     * The trick is that for GL blending, we only get our 4 source values
-     * into the blender, and since we need all 4 components of source, we
-     * can't also get the mask IN clip into the blender.  But if we did
-     * two passes we could make it work:
-     *     dest = (mask IN clip) DEST_OUT dest
-     *     dest = src IN mask IN clip ADD dest
-     *
-     * But for now, fall back :)
-     */
-    if (op == CAIRO_OPERATOR_SOURCE)
-	return UNSUPPORTED ("not handling SOURCE operator");
-
-    /* XXX we don't need ownership of the font as we use a global
-     * glyph cache -- but we do need scaled_glyph eviction notification. :-(
-     */
-    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
-	return UNSUPPORTED ("do not control font");
-
-    if (clip != NULL) {
-	status = _cairo_clip_get_region (clip, &clip_region);
-	/* the empty clip should never be propagated this far */
-	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
-	if (unlikely (_cairo_status_is_error (status)))
-	    return status;
-	if (status == CAIRO_INT_STATUS_UNSUPPORTED)
-	    return UNSUPPORTED ("unhandled clipmask");
-    }
-
-    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
-						      glyphs, num_glyphs,
-						      &extents,
-						      &overlap);
-    if (unlikely (status))
-	return status;
-
-    /* If the glyphs overlap, we need to build an intermediate mask rather
-     * then compositing directly.
-     * For the time being, just fallback.
-     */
-    if (overlap)
-	return UNSUPPORTED ("overlapping glyphs");
-
     status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
-				     extents.x, extents.y,
-				     extents.x, extents.y,
-				     extents.width, extents.height);
+				     glyph_extents->x, glyph_extents->y,
+				     dst_x, dst_y,
+				     glyph_extents->width,
+				     glyph_extents->height);
     if (unlikely (status))
 	return status;
 
@@ -465,9 +373,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
 
     _cairo_gl_set_destination (dst);
-
     _cairo_gl_set_operator (dst, op);
-
     _cairo_gl_set_src_operand (ctx, &composite_setup);
 
     _cairo_scaled_font_freeze_cache (scaled_font);
@@ -611,9 +517,185 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     _cairo_gl_operand_destroy (&composite_setup.src);
 
     *remaining_glyphs = num_glyphs - i;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+
+static cairo_int_status_t
+_cairo_gl_surface_show_glyphs_via_mask (cairo_gl_surface_t	*dst,
+			                cairo_operator_t	 op,
+					const cairo_pattern_t	*source,
+					cairo_glyph_t		*glyphs,
+					int			 num_glyphs,
+					const cairo_rectangle_int_t *glyph_extents,
+					cairo_scaled_font_t	*scaled_font,
+					cairo_clip_t		*clip,
+					int			*remaining_glyphs)
+{
+    cairo_surface_t *mask;
+    cairo_status_t status;
+    cairo_solid_pattern_t solid;
+    int i;
+
+    mask = cairo_gl_surface_create (dst->ctx,
+	                            CAIRO_CONTENT_ALPHA,
+				    glyph_extents->width,
+				    glyph_extents->height);
+    if (unlikely (mask->status))
+	return mask->status;
+
+    for (i = 0; i < num_glyphs; i++) {
+	glyphs[i].x -= glyph_extents->x;
+	glyphs[i].y -= glyph_extents->y;
+    }
+
+    _cairo_pattern_init_solid (&solid, CAIRO_COLOR_WHITE, CAIRO_CONTENT_ALPHA);
+    status = _render_glyphs ((cairo_gl_surface_t *) mask, 0, 0,
+	                     CAIRO_OPERATOR_ADD, &solid.base,
+	                     glyphs, num_glyphs, glyph_extents,
+			     scaled_font, NULL, remaining_glyphs);
+    if (likely (status == CAIRO_STATUS_SUCCESS)) {
+	cairo_surface_pattern_t mask_pattern;
+
+	_cairo_pattern_init_for_surface (&mask_pattern, mask);
+	cairo_matrix_init_translate (&mask_pattern.base.matrix,
+		                     -glyph_extents->x, -glyph_extents->y);
+	status = _cairo_surface_mask (&dst->base, op,
+		                      source, &mask_pattern.base, clip);
+	_cairo_pattern_fini (&mask_pattern.base);
+    } else {
+	for (i = 0; i < num_glyphs; i++) {
+	    glyphs[i].x += glyph_extents->x;
+	    glyphs[i].y += glyph_extents->y;
+	}
+	*remaining_glyphs = num_glyphs;
+    }
+
+    cairo_surface_destroy (mask);
     return status;
 }
 
+cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void			*abstract_dst,
+			       cairo_operator_t		 op,
+			       const cairo_pattern_t	*source,
+			       cairo_glyph_t		*glyphs,
+			       int			 num_glyphs,
+			       cairo_scaled_font_t	*scaled_font,
+			       cairo_clip_t		*clip,
+			       int			*remaining_glyphs)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_rectangle_int_t extents;
+    cairo_region_t *clip_region = NULL;
+    cairo_solid_pattern_t solid_pattern;
+    cairo_bool_t overlap, use_mask = FALSE;
+    cairo_status_t status;
+
+    if (! GLEW_ARB_vertex_buffer_object)
+	return UNSUPPORTED ("requires ARB_vertex_buffer_object");
+
+    if (! _cairo_gl_operator_is_supported (op))
+	return UNSUPPORTED ("unsupported operator");
+
+    /* Just let unbounded operators go through the fallback code
+     * instead of trying to do the fixups here */
+    if (! _cairo_operator_bounded_by_mask (op))
+	use_mask |= TRUE;
+
+    /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
+     * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
+     * is:
+     *     mask IN clip ? src OP dest : dest
+     * or more simply:
+     *     mask IN CLIP ? 0 : dest
+     *
+     * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
+     *
+     * The model we use in _cairo_gl_set_operator() is Render's:
+     *     src IN mask IN clip OP dest
+     * which would boil down to:
+     *     0 (bounded by the extents of the drawing).
+     *
+     * However, we can do a Render operation using an opaque source
+     * and DEST_OUT to produce:
+     *    1 IN mask IN clip DEST_OUT dest
+     * which is
+     *    mask IN clip ? 0 : dest
+     */
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
+				   CAIRO_CONTENT_COLOR);
+	source = &solid_pattern.base;
+	op = CAIRO_OPERATOR_DEST_OUT;
+    }
+
+    /* For SOURCE, cairo's rendering equation is:
+     *     (mask IN clip) ? src OP dest : dest
+     * or more simply:
+     *     (mask IN clip) ? src : dest.
+     *
+     * If we just used the Render equation, we would get:
+     *     (src IN mask IN clip) OP dest
+     * or:
+     *     (src IN mask IN clip) bounded by extents of the drawing.
+     *
+     * The trick is that for GL blending, we only get our 4 source values
+     * into the blender, and since we need all 4 components of source, we
+     * can't also get the mask IN clip into the blender.  But if we did
+     * two passes we could make it work:
+     *     dest = (mask IN clip) DEST_OUT dest
+     *     dest = src IN mask IN clip ADD dest
+     *
+     * But for now, composite via an intermediate mask.
+     */
+    if (op == CAIRO_OPERATOR_SOURCE)
+	use_mask |= TRUE;
+
+    /* XXX we don't need ownership of the font as we use a global
+     * glyph cache -- but we do need scaled_glyph eviction notification. :-(
+     */
+    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
+	return UNSUPPORTED ("do not control font");
+
+    /* If the glyphs overlap, we need to build an intermediate mask rather
+     * then perform the compositing directly.
+     */
+    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+						      glyphs, num_glyphs,
+						      &extents,
+						      &overlap);
+    if (unlikely (status))
+	return status;
+
+    use_mask |= overlap;
+
+    if (clip != NULL) {
+	status = _cairo_clip_get_region (clip, &clip_region);
+	/* the empty clip should never be propagated this far */
+	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+	if (unlikely (_cairo_status_is_error (status)))
+	    return status;
+
+	use_mask |= status == CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    if (use_mask) {
+	return _cairo_gl_surface_show_glyphs_via_mask (dst, op,
+			                               source,
+			                               glyphs, num_glyphs,
+						       &extents,
+						       scaled_font,
+						       clip,
+						       remaining_glyphs);
+    }
+
+    return _render_glyphs (dst, extents.x, extents.y,
+	                   op, source,
+			   glyphs, num_glyphs, &extents,
+			   scaled_font, clip_region, remaining_glyphs);
+}
+
 void
 _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache)
 {
commit cd259489e2f41caaa7441233d87b46fbd43991ae
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 15:58:54 2009 +0100

    [gl] Fallback for overlapping glyphs.

diff --git a/src/cairo-analysis-surface.c b/src/cairo-analysis-surface.c
index eb1e87d..5faa1b9 100644
--- a/src/cairo-analysis-surface.c
+++ b/src/cairo-analysis-surface.c
@@ -568,7 +568,8 @@ _cairo_analysis_surface_show_glyphs (void		  *abstract_surface,
 	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
 							  glyphs,
 							  num_glyphs,
-							  &glyph_extents);
+							  &glyph_extents,
+							  NULL);
 	if (unlikely (status))
 	    return status;
 
@@ -651,7 +652,8 @@ _cairo_analysis_surface_show_text_glyphs (void			    *abstract_surface,
 	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
 							  glyphs,
 							  num_glyphs,
-							  &glyph_extents);
+							  &glyph_extents,
+							  NULL);
 	if (unlikely (status))
 	    return status;
 
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 2e8cf77..5be984f 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -344,18 +344,19 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     cairo_solid_pattern_t solid_pattern;
     cairo_gl_glyphs_setup_t setup;
     cairo_gl_composite_setup_t composite_setup;
+    cairo_bool_t overlap;
     GLuint vbo = 0;
 
     if (! GLEW_ARB_vertex_buffer_object)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+	return UNSUPPORTED ("requires ARB_vertex_buffer_object");
 
     if (! _cairo_gl_operator_is_supported (op))
-        return CAIRO_INT_STATUS_UNSUPPORTED;
+	return UNSUPPORTED ("unsupported operator");
 
     /* Just let unbounded operators go through the fallback code
      * instead of trying to do the fixups here */
     if (! _cairo_operator_bounded_by_mask (op))
-        return CAIRO_INT_STATUS_UNSUPPORTED;
+	return UNSUPPORTED ("unhandled unbound operator");
 
     /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
      * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
@@ -404,34 +405,38 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
      * But for now, fall back :)
      */
     if (op == CAIRO_OPERATOR_SOURCE)
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+	return UNSUPPORTED ("not handling SOURCE operator");
 
     /* XXX we don't need ownership of the font as we use a global
      * glyph cache -- but we do need scaled_glyph eviction notification. :-(
      */
     if (! _cairo_gl_surface_owns_font (dst, scaled_font))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+	return UNSUPPORTED ("do not control font");
 
     if (clip != NULL) {
 	status = _cairo_clip_get_region (clip, &clip_region);
-	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
-	    return CAIRO_STATUS_SUCCESS;
-	else if (status != CAIRO_STATUS_SUCCESS)
+	/* the empty clip should never be propagated this far */
+	assert (status != CAIRO_INT_STATUS_NOTHING_TO_DO);
+	if (unlikely (_cairo_status_is_error (status)))
 	    return status;
+	if (status == CAIRO_INT_STATUS_UNSUPPORTED)
+	    return UNSUPPORTED ("unhandled clipmask");
     }
 
-    /* XXX If glyphs overlap, build tmp mask and composite.
-     * Could we use the stencil instead but only write if alpha !=0 ?
-     * TEXKILL? PIXELKILL?
-     * Antialiasing issues - but using glyph images cause their own anyway.
-     */
-
     status = _cairo_scaled_font_glyph_device_extents (scaled_font,
 						      glyphs, num_glyphs,
-						      &extents);
+						      &extents,
+						      &overlap);
     if (unlikely (status))
 	return status;
 
+    /* If the glyphs overlap, we need to build an intermediate mask rather
+     * then compositing directly.
+     * For the time being, just fallback.
+     */
+    if (overlap)
+	return UNSUPPORTED ("overlapping glyphs");
+
     status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
 				     extents.x, extents.y,
 				     extents.x, extents.y,
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index e8988eb..378272b 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -50,6 +50,19 @@
 #define GL_GLEXT_PROTOTYPES
 #include <GL/glext.h>
 
+#define DEBUG_GL 0
+
+#if DEBUG_GL && __GNUC__
+#define UNSUPPORTED(reason) ({ \
+    fprintf (stderr, \
+	     "cairo-gl: hit unsupported operation in %s(), line %d: %s\n", \
+	     __FUNCTION__, __LINE__, reason); \
+    CAIRO_INT_STATUS_UNSUPPORTED; \
+})
+#else
+#define UNSUPPORTED(reason) CAIRO_INT_STATUS_UNSUPPORTED
+#endif
+
 typedef struct _cairo_gl_surface {
     cairo_surface_t base;
 
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index 2b5b26d..3238c84 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -1902,6 +1902,15 @@ cairo_scaled_font_text_to_glyphs (cairo_scaled_font_t   *scaled_font,
 }
 slim_hidden_def (cairo_scaled_font_text_to_glyphs);
 
+static inline cairo_bool_t
+_range_contains_glyph (const cairo_point_int_t *min,
+		       const cairo_point_int_t *max,
+		       int left, int top,
+		       int right, int bottom)
+{
+    return right > min->x && left < max->x && bottom > min->y && top < max->y;
+}
+
 /*
  * Compute a device-space bounding box for the glyphs.
  */
@@ -1909,13 +1918,15 @@ cairo_status_t
 _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
 					 const cairo_glyph_t	 *glyphs,
 					 int                      num_glyphs,
-					 cairo_rectangle_int_t   *extents)
+					 cairo_rectangle_int_t   *extents,
+					 cairo_bool_t *overlap_out)
 {
     cairo_status_t status = CAIRO_STATUS_SUCCESS;
-    int i;
     cairo_point_int_t min = { CAIRO_RECT_INT_MAX, CAIRO_RECT_INT_MAX };
     cairo_point_int_t max = { CAIRO_RECT_INT_MIN, CAIRO_RECT_INT_MIN };
     cairo_scaled_glyph_t *glyph_cache[64];
+    cairo_bool_t overlap = overlap_out ? FALSE : TRUE;
+    int i;
 
     if (unlikely (scaled_font->status))
 	return scaled_font->status;
@@ -1954,6 +1965,11 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
 	right  = x + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x);
 	bottom = y + _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y);
 
+	if (overlap == FALSE) {
+	    overlap = _range_contains_glyph (&min, &max,
+		                             left, top, right, bottom);
+	}
+
 	if (left   < min.x) min.x = left;
 	if (right  > max.x) max.x = right;
 	if (top    < min.y) min.y = top;
@@ -1974,6 +1990,9 @@ _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
 	extents->width = extents->height = 0;
     }
 
+    if (overlap_out != NULL)
+	*overlap_out = overlap;
+
     return CAIRO_STATUS_SUCCESS;
 }
 
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index 9867eea..3bfb17d 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1151,7 +1151,8 @@ _cairo_surface_fallback_show_glyphs (cairo_surface_t		*surface,
 	status = _cairo_scaled_font_glyph_device_extents (scaled_font,
 							  glyphs,
 							  num_glyphs,
-							  &glyph_extents);
+							  &glyph_extents,
+							  NULL);
 	if (unlikely (status))
 	    return status;
 
diff --git a/src/cairo-xcb-surface.c b/src/cairo-xcb-surface.c
index 0650f8f..7c5731f 100644
--- a/src/cairo-xcb-surface.c
+++ b/src/cairo-xcb-surface.c
@@ -2558,7 +2558,8 @@ _cairo_xcb_surface_show_glyphs (void			*abstract_dst,
         status = _cairo_scaled_font_glyph_device_extents (scaled_font,
                                                           glyphs,
                                                           num_glyphs,
-                                                          &glyph_extents);
+                                                          &glyph_extents,
+							  NULL);
         if (status)
 	    goto BAIL;
 
diff --git a/src/cairo-xlib-surface.c b/src/cairo-xlib-surface.c
index 4bc244a..edcb5e6 100644
--- a/src/cairo-xlib-surface.c
+++ b/src/cairo-xlib-surface.c
@@ -4196,7 +4196,8 @@ _cairo_xlib_surface_show_glyphs (void                *abstract_dst,
         status = _cairo_scaled_font_glyph_device_extents (scaled_font,
                                                           glyphs,
                                                           num_glyphs,
-                                                          &glyph_extents);
+                                                          &glyph_extents,
+							  NULL);
         if (unlikely (status))
 	    goto BAIL0;
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 6010e20..4f83977 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1673,7 +1673,8 @@ cairo_private cairo_status_t
 _cairo_scaled_font_glyph_device_extents (cairo_scaled_font_t	 *scaled_font,
 					 const cairo_glyph_t	 *glyphs,
 					 int                      num_glyphs,
-					 cairo_rectangle_int_t   *extents);
+					 cairo_rectangle_int_t   *extents,
+					 cairo_bool_t		 *overlap);
 
 cairo_private cairo_status_t
 _cairo_scaled_font_show_glyphs (cairo_scaled_font_t *scaled_font,
commit 3d8f1d3dc83b9a86f2f104f0e2afa192a34d18c8
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 15:38:45 2009 +0100

    [gl] Only unlock the glyph cache when full.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 6911083..2e8cf77 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -171,6 +171,12 @@ cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
     return cache;
 }
 
+static void
+_cairo_gl_glyph_cache_unlock (cairo_gl_glyph_cache_t *cache)
+{
+    _cairo_rtree_unpin (&cache->rtree);
+}
+
 static cairo_bool_t
 _cairo_gl_surface_owns_font (cairo_gl_surface_t *surface,
 			     cairo_scaled_font_t *scaled_font)
@@ -260,9 +266,6 @@ _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 	    setup->num_prims = 0;
 	}
     }
-
-    for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++)
-	_cairo_rtree_unpin (&ctx->glyph_cache[i].rtree);
 }
 
 static void
@@ -551,6 +554,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
 		/* Cache is full, so flush existing prims and try again. */
 		_cairo_gl_flush_glyphs (ctx, &setup);
+		_cairo_gl_glyph_cache_unlock (cache);
 	    }
 
 	    status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph);
commit a251e34dbbcb652906f7fddd2a72cbb5992b58ae
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 13:44:19 2009 +0100

    [gl] Move check for vertex buffer support to individual operations.
    
    As we can fallback instead of using vertex buffers, do so rather than
    preventing any use of GL acceleration.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 9dc1e9a..6911083 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -343,6 +343,9 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     cairo_gl_composite_setup_t composite_setup;
     GLuint vbo = 0;
 
+    if (! GLEW_ARB_vertex_buffer_object)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
     if (! _cairo_gl_operator_is_supported (op))
         return CAIRO_INT_STATUS_UNSUPPORTED;
 
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 55575f8..72d3876 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -93,15 +93,12 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     if (glewInit () != GLEW_OK)
 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
 
-    if (! GLEW_ARB_vertex_buffer_object ||
-	! GLEW_EXT_framebuffer_object ||
+    if (! GLEW_EXT_framebuffer_object ||
 	! GLEW_ARB_texture_env_combine ||
 	! GLEW_ARB_texture_non_power_of_two)
     {
 	fprintf (stderr,
 		 "Required GL extensions not available:\n");
-	if (! GLEW_ARB_vertex_buffer_object)
-	    fprintf (stderr, "    GL_ARB_vertex_buffer_object\n");
 	if (! GLEW_EXT_framebuffer_object)
 	    fprintf (stderr, "    GL_EXT_framebuffer_object\n");
 	if (! GLEW_ARB_texture_env_combine)
@@ -1775,7 +1772,14 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
 				       cairo_antialias_t	  antialias,
 				       const cairo_composite_rectangles_t *rects)
 {
-    return _cairo_gl_operator_is_supported (op);
+    if (! _cairo_gl_operator_is_supported (op))
+	return FALSE;
+
+    if (! GLEW_ARB_vertex_buffer_object)
+	return FALSE;
+
+    return TRUE;
+
     (void) pattern;
     (void) abstract_dst;
     (void) antialias;
@@ -1881,9 +1885,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     cairo_surface_attributes_t *src_attributes;
     GLenum err;
 
-    if (!GLEW_ARB_vertex_buffer_object)
-	return _cairo_span_renderer_create_in_error (CAIRO_INT_STATUS_UNSUPPORTED);
-
     renderer = calloc (1, sizeof (*renderer));
     if (unlikely (renderer == NULL))
 	return _cairo_span_renderer_create_in_error (CAIRO_STATUS_NO_MEMORY);
commit 90657bacc306e65e07e9eb16561a805150aef778
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 13:30:31 2009 +0100

    [gl] Separate out supported operator check.
    
    By performing the check on whether the operator is supported prior to
    acquiring the context, we do not need to handle the error part way
    through the context setup. This makes the code much cleaner, and save
    some work for the unsupported cases.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index c550e34..9dc1e9a 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -343,6 +343,9 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     cairo_gl_composite_setup_t composite_setup;
     GLuint vbo = 0;
 
+    if (! _cairo_gl_operator_is_supported (op))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
     /* Just let unbounded operators go through the fallback code
      * instead of trying to do the fixups here */
     if (! _cairo_operator_bounded_by_mask (op))
@@ -452,9 +455,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 
     _cairo_gl_set_destination (dst);
 
-    status = _cairo_gl_set_operator (dst, op);
-    if (status != CAIRO_STATUS_SUCCESS)
-	goto CLEANUP_CONTEXT;
+    _cairo_gl_set_operator (dst, op);
 
     _cairo_gl_set_src_operand (ctx, &composite_setup);
 
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 454971f..e8988eb 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -152,7 +152,10 @@ _cairo_gl_context_release (cairo_gl_context_t *ctx);
 cairo_private void
 _cairo_gl_set_destination (cairo_gl_surface_t *surface);
 
-cairo_private int
+cairo_private cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op);
+
+cairo_private void
 _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op);
 
 cairo_private void
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index daa2bbc..55575f8 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -340,7 +340,13 @@ _cairo_gl_set_destination (cairo_gl_surface_t *surface)
     glLoadIdentity ();
 }
 
-int
+cairo_bool_t
+_cairo_gl_operator_is_supported (cairo_operator_t op)
+{
+    return op < CAIRO_OPERATOR_SATURATE;
+}
+
+void
 _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
 {
     struct {
@@ -365,8 +371,7 @@ _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
     };
     GLenum src_factor, dst_factor;
 
-    if (op >= ARRAY_SIZE (blend_factors))
-	return CAIRO_INT_STATUS_UNSUPPORTED;
+    assert (op < ARRAY_SIZE (blend_factors));
 
     src_factor = blend_factors[op].src;
     dst_factor = blend_factors[op].dst;
@@ -383,8 +388,6 @@ _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
 
     glEnable (GL_BLEND);
     glBlendFunc (src_factor, dst_factor);
-
-    return CAIRO_STATUS_SUCCESS;
 }
 
 static void
@@ -1268,6 +1271,9 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
     GLenum err;
     cairo_gl_composite_setup_t setup;
 
+    if (! _cairo_gl_operator_is_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
     memset (&setup, 0, sizeof (setup));
 
     status = _cairo_gl_operand_init (&setup.src, src, dst,
@@ -1295,14 +1301,7 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 
     ctx = _cairo_gl_context_acquire (dst->ctx);
     _cairo_gl_set_destination (dst);
-    status = _cairo_gl_set_operator (dst, op);
-    if (status != CAIRO_STATUS_SUCCESS) {
-	_cairo_gl_operand_destroy (&setup.src);
-	if (mask != NULL)
-	    _cairo_gl_operand_destroy (&setup.mask);
-	_cairo_gl_context_release (ctx);
-	return status;
-    }
+    _cairo_gl_set_operator (dst, op);
 
     _cairo_gl_set_src_operand (ctx, &setup);
 
@@ -1493,19 +1492,17 @@ _cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
 {
     cairo_gl_surface_t *surface = abstract_surface;
     cairo_gl_context_t *ctx;
-    cairo_int_status_t status;
     int i;
     GLfloat *vertices;
     GLfloat *colors;
 
+    if (! _cairo_gl_operator_is_supported (op))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
     ctx = _cairo_gl_context_acquire (surface->ctx);
 
     _cairo_gl_set_destination (surface);
-    status = _cairo_gl_set_operator (surface, op);
-    if (status != CAIRO_STATUS_SUCCESS) {
-	_cairo_gl_context_release (ctx);
-	return status;
-    }
+    _cairo_gl_set_operator (surface, op);
 
     vertices = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 2);
     colors = _cairo_malloc_ab (num_rects, sizeof (GLfloat) * 4 * 4);
@@ -1778,12 +1775,11 @@ _cairo_gl_surface_check_span_renderer (cairo_operator_t	  op,
 				       cairo_antialias_t	  antialias,
 				       const cairo_composite_rectangles_t *rects)
 {
-    (void) op;
+    return _cairo_gl_operator_is_supported (op);
     (void) pattern;
     (void) abstract_dst;
     (void) antialias;
     (void) rects;
-    return TRUE;
 }
 
 static void
@@ -1913,7 +1909,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     }
 
     _cairo_gl_context_acquire (dst->ctx);
-
     _cairo_gl_set_destination (dst);
 
     status = _cairo_gl_surface_set_clip_region (dst, clip_region);
@@ -1924,12 +1919,7 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
 
     src_attributes = &renderer->setup.src.operand.texture.attributes;
 
-    status = _cairo_gl_set_operator (dst, op);
-    if (status != CAIRO_STATUS_SUCCESS) {
-	_cairo_gl_surface_span_renderer_destroy (renderer);
-	return _cairo_span_renderer_create_in_error (status);
-    }
-
+    _cairo_gl_set_operator (dst, op);
     _cairo_gl_set_src_operand (dst->ctx, &renderer->setup);
 
     /* Set up the mask to source from the incoming vertex color. */
commit 21d50e2163038c8acc0eef738acf61f851835f7d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Tue Jul 28 13:24:43 2009 +0100

    [gl] Unthaw font along contended path.
    
    If we bail after freezing the font, make sure we thaw that it is thawed
    before returning.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 0aa9b41..c550e34 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -460,7 +460,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 
     _cairo_scaled_font_freeze_cache (scaled_font);
     if (! _cairo_gl_surface_owns_font (dst, scaled_font))
-	goto CLEANUP_CONTEXT;
+	goto CLEANUP_FONT;
 
     if (scaled_font->surface_private == NULL) {
 	/* XXX couple into list to remove on context destruction */
@@ -570,9 +570,9 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     status = CAIRO_STATUS_SUCCESS;
   FINISH:
     _cairo_gl_flush_glyphs (ctx, &setup);
+  CLEANUP_FONT:
     _cairo_scaled_font_thaw_cache (scaled_font);
 
-  CLEANUP_CONTEXT:
     glDisable (GL_BLEND);
     glDisable (GL_SCISSOR_TEST);
 
commit e19b2866ed095cf66e9344efa442ae01dc7913ac
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jul 27 19:51:36 2009 +0100

    [gl] Actually remove the glyph from the rtree on scaled_font_fini
    
    Mark the node as available when the scaled_glyph is finished, and then
    attempt to collapse the parent node.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index b8441c9..0aa9b41 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -198,8 +198,9 @@ _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
     if (glyph_private != NULL) {
 	glyph_private->node.owner = NULL;
 	if (! glyph_private->node.pinned) {
-	    _cairo_rtree_node_collapse (&glyph_private->cache->rtree,
-		                        glyph_private->node.parent);
+	    /* XXX thread-safety? Probably ok due to the frozen scaled-font. */
+	    _cairo_rtree_node_remove (&glyph_private->cache->rtree,
+		                      &glyph_private->node);
 	}
     }
 }
diff --git a/src/cairo-rtree-private.h b/src/cairo-rtree-private.h
index 33c6bc8..edb7e3a 100644
--- a/src/cairo-rtree-private.h
+++ b/src/cairo-rtree-private.h
@@ -88,6 +88,9 @@ cairo_private void
 _cairo_rtree_node_collapse (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
 
 cairo_private void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
+
+cairo_private void
 _cairo_rtree_node_destroy (cairo_rtree_t *rtree, cairo_rtree_node_t *node);
 
 cairo_private void
diff --git a/src/cairo-rtree.c b/src/cairo-rtree.c
index ba142b7..d4bdbd4 100644
--- a/src/cairo-rtree.c
+++ b/src/cairo-rtree.c
@@ -185,6 +185,19 @@ _cairo_rtree_node_insert (cairo_rtree_t *rtree,
     return CAIRO_STATUS_SUCCESS;
 }
 
+void
+_cairo_rtree_node_remove (cairo_rtree_t *rtree, cairo_rtree_node_t *node)
+{
+    assert (node->state == CAIRO_RTREE_NODE_OCCUPIED);
+    assert (node->pinned == FALSE);
+
+    node->state = CAIRO_RTREE_NODE_AVAILABLE;
+    cairo_list_move (&node->link, &rtree->available);
+
+    if (! node->parent->pinned)
+	_cairo_rtree_node_collapse (rtree, node->parent);
+}
+
 cairo_int_status_t
 _cairo_rtree_insert (cairo_rtree_t	     *rtree,
 		     int		      width,
commit e0b7979a30fff2a22fef5c170fd72c37c197ee0e
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date:   Mon Jul 27 17:44:10 2009 +0100

    [gl] Switch to using the common rtree implementation.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index c6d5f6d..b8441c9 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -33,7 +33,7 @@
 #include "cairoint.h"
 
 #include "cairo-gl-private.h"
-#include "cairo-freelist-private.h"
+#include "cairo-rtree-private.h"
 
 #define GLYPH_CACHE_WIDTH 1024
 #define GLYPH_CACHE_HEIGHT 1024
@@ -41,314 +41,18 @@
 #define GLYPH_CACHE_MAX_SIZE 128
 
 typedef struct _cairo_gl_glyph_private {
-    rtree_node_t node;
-    void **owner;
+    cairo_rtree_node_t node;
+    cairo_gl_glyph_cache_t *cache;
     struct { float x, y; } p1, p2;
 } cairo_gl_glyph_private_t;
 
-static void
-_rtree_node_evict (rtree_t *rtree, rtree_node_t *node)
-{
-    rtree->evict (node);
-    node->state = RTREE_NODE_AVAILABLE;
-}
-
-static rtree_node_t *
-_rtree_node_create (rtree_t		 *rtree,
-		    rtree_node_t	 *parent,
-		    int			  x,
-		    int			  y,
-		    int			  width,
-		    int			  height)
-{
-    rtree_node_t *node;
-
-    /* XXX chunked freelist */
-    node = _cairo_freelist_alloc (&rtree->node_freelist);
-    if (node == NULL) {
-	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
-	return NULL;
-    }
-
-    memset (node->children, 0, sizeof (node->children));
-    node->parent = parent;
-    node->state  = RTREE_NODE_AVAILABLE;
-    node->locked = FALSE;
-    node->x	 = x;
-    node->y	 = y;
-    node->width  = width;
-    node->height = height;
-
-    return node;
-}
-
-static void
-_rtree_node_destroy (rtree_t *rtree, rtree_node_t *node)
-{
-    int i;
-
-    if (node == NULL)
-	return;
-
-    if (node->state == RTREE_NODE_OCCUPIED) {
-	_rtree_node_evict (rtree, node);
-    } else {
-	for (i = 0; i < 4 && node->children[i] != NULL; i++)
-	    _rtree_node_destroy (rtree, node->children[i]);
-    }
-
-    _cairo_freelist_free (&rtree->node_freelist, node);
-}
-
-static cairo_int_status_t
-_rtree_insert (rtree_t	     *rtree,
-	       rtree_node_t  *node,
-	       int	      width,
-	       int	      height,
-	       rtree_node_t **out)
-{
-    cairo_status_t status;
-    int i;
-
-    switch (node->state) {
-    case RTREE_NODE_DIVIDED:
-	for (i = 0; i < 4 && node->children[i] != NULL; i++) {
-	    if (node->children[i]->width  >= width &&
-		node->children[i]->height >= height)
-	    {
-		status = _rtree_insert (rtree, node->children[i],
-					width, height,
-					out);
-		if (status != CAIRO_INT_STATUS_UNSUPPORTED)
-		    return status;
-	    }
-	}
-
-    default:
-    case RTREE_NODE_OCCUPIED:
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-
-    case RTREE_NODE_AVAILABLE:
-	if (node->width  - width  > GLYPH_CACHE_MIN_SIZE ||
-	    node->height - height > GLYPH_CACHE_MIN_SIZE)
-	{
-	    int w, h;
-
-	    w = node->width  - width;
-	    h = node->height - height;
-
-	    i = 0;
-	    node->children[i] = _rtree_node_create (rtree, node,
-						    node->x, node->y,
-						    width, height);
-	    if (unlikely (node->children[i] == NULL))
-		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	    i++;
-
-	    if (w > GLYPH_CACHE_MIN_SIZE) {
-		node->children[i] = _rtree_node_create (rtree, node,
-							node->x + width,
-							node->y,
-							w, height);
-		if (unlikely (node->children[i] == NULL))
-		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-		i++;
-	    }
-
-	    if (h > GLYPH_CACHE_MIN_SIZE) {
-		node->children[i] = _rtree_node_create (rtree, node,
-							node->x,
-							node->y + height,
-							width, h);
-		if (unlikely (node->children[i] == NULL))
-		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-		i++;
-
-		if (w > GLYPH_CACHE_MIN_SIZE) {
-		    node->children[i] = _rtree_node_create (rtree, node,
-							    node->x + width,
-							    node->y + height,
-							    w, h);
-		    if (unlikely (node->children[i] == NULL))
-			return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-		    i++;
-		}
-	    }
-
-	    node->state = RTREE_NODE_DIVIDED;
-	    node = node->children[0];
-	}
-
-	node->state = RTREE_NODE_OCCUPIED;
-	*out = node;
-	return CAIRO_STATUS_SUCCESS;
-    }
-}
-
-static cairo_int_status_t
-_rtree_add_evictable_nodes (rtree_t *rtree,
-			    rtree_node_t *node,
-			    int width,
-			    int height,
-			    cairo_array_t *evictable_nodes)
-{
-    cairo_int_status_t status;
-    cairo_bool_t child_added = FALSE;
-    int i;
-
-    switch (node->state) {
-    case RTREE_NODE_DIVIDED:
-	for (i = 0; i < 4 && node->children[i] != NULL; i++) {
-	    if (node->children[i]->width  >= width &&
-		node->children[i]->height >= height)
-	    {
-		status = _rtree_add_evictable_nodes (rtree, node->children[i],
-						     width, height,
-						     evictable_nodes);
-		if (_cairo_status_is_error (status))
-		    return status;
-
-		child_added |= status == CAIRO_STATUS_SUCCESS;
-	    }
-	}
-	if (child_added)
-	    return CAIRO_STATUS_SUCCESS;
-
-	/* fall through */
-    case RTREE_NODE_AVAILABLE:
-    case RTREE_NODE_OCCUPIED:
-	if (node->locked)
-	    return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    return _cairo_array_append (evictable_nodes, &node);
-}
-
-static uint32_t
-hars_petruska_f54_1_random (void)
-{
-#define rol(x,k) ((x << k) | (x >> (32-k)))
-    static uint32_t x;
-    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
-#undef rol
-}
-
-static cairo_int_status_t
-_rtree_evict_random (rtree_t	*rtree,
-		     rtree_node_t *root,
-		     int	 width,
-		     int	 height,
-		     rtree_node_t **out)
-{
-    cairo_array_t evictable_nodes;
-    cairo_status_t status;
-    int i;
-
-    _cairo_array_init (&evictable_nodes, sizeof (rtree_node_t *));
-
-    status = _rtree_add_evictable_nodes (rtree, root,
-					 width, height,
-					 &evictable_nodes);
-    if (status == CAIRO_STATUS_SUCCESS) {
-	rtree_node_t *node;
-
-	node = *(rtree_node_t **)
-	    _cairo_array_index (&evictable_nodes,
-				hars_petruska_f54_1_random () % evictable_nodes.num_elements);
-	if (node->state == RTREE_NODE_OCCUPIED) {
-	    _rtree_node_evict (rtree, node);
-	} else {
-	    for (i = 0; i < 4 && node->children[i] != NULL; i++) {
-		_rtree_node_destroy (rtree, node->children[i]);
-		node->children[i] = NULL;
-	    }
-	}
-
-	node->state = RTREE_NODE_AVAILABLE;
-	*out = node;
-    }
-
-    _cairo_array_fini (&evictable_nodes);
-
-    return status;
-}
-
-static void *
-_rtree_lock (rtree_t *rtree, rtree_node_t *node)
-{
-    void *ptr = node;
-
-    while (node != NULL && ! node->locked) {
-	node->locked = TRUE;
-	node = node->parent;
-    }
-
-    return ptr;
-}
-
-static void
-_rtree_unlock (rtree_t *rtree, rtree_node_t *node)
-{
-    int i;
-
-    if (! node->locked)
-	return;
-
-    node->locked = FALSE;
-    if (node->state == RTREE_NODE_DIVIDED) {
-	for (i = 0; i < 4 && node->children[i] != NULL; i++)
-	    _rtree_unlock (rtree, node->children[i]);
-    }
-}
-
-static void
-_rtree_init (rtree_t	    *rtree,
-	     int	     width,
-	     int	     height,
-	     int             node_size,
-	     void (*evict) (void *node))
-{
-    rtree->evict = evict;
-
-    assert (node_size >= (int) sizeof (rtree_node_t));
-    _cairo_freelist_init (&rtree->node_freelist, node_size);
-
-    memset (&rtree->root, 0, sizeof (rtree->root));
-    rtree->root.width = width;
-    rtree->root.height = height;
-}
-
-static void
-_rtree_fini (rtree_t *rtree)
-{
-    int i;
-
-    if (rtree->root.state == RTREE_NODE_OCCUPIED) {
-	_rtree_node_evict (rtree, &rtree->root);
-    } else {
-	for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
-	    _rtree_node_destroy (rtree, rtree->root.children[i]);
-    }
-
-    _cairo_freelist_fini (&rtree->node_freelist);
-}
-
-static void
-_glyph_evict (void *node)
-{
-    cairo_gl_glyph_private_t *glyph_private = node;
-
-    if (glyph_private->owner != NULL)
-	*glyph_private->owner = NULL;
-}
-
 static cairo_status_t
 _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
 				 cairo_scaled_glyph_t  *scaled_glyph)
 {
     cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
     cairo_gl_glyph_private_t *glyph_private;
-    rtree_node_t *node = NULL;
+    cairo_rtree_node_t *node = NULL;
     cairo_status_t status;
     int width, height;
     GLenum internal_format, format, type;
@@ -370,14 +74,15 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
 	height = GLYPH_CACHE_MIN_SIZE;
 
     /* search for an available slot */
-    status = _rtree_insert (&cache->rtree, &cache->rtree.root,
-			    width, height, &node);
+    status = _cairo_rtree_insert (&cache->rtree, width, height, &node);
     /* search for an unlocked slot */
     if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	status = _rtree_evict_random (&cache->rtree, &cache->rtree.root,
-				      width, height, &node);
-	if (status == CAIRO_STATUS_SUCCESS)
-	    status = _rtree_insert (&cache->rtree, node, width, height, &node);
+	status = _cairo_rtree_evict_random (&cache->rtree,
+				            width, height, &node);
+	if (status == CAIRO_STATUS_SUCCESS) {
+	    status = _cairo_rtree_node_insert (&cache->rtree,
+		                               node, width, height, &node);
+	}
     }
     if (status)
 	return status;
@@ -394,9 +99,10 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
     glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
 
     scaled_glyph->surface_private = node;
+    node->owner = &scaled_glyph->surface_private;
 
     glyph_private = (cairo_gl_glyph_private_t *) node;
-    glyph_private->owner = &scaled_glyph->surface_private;
+    glyph_private->cache = cache;
 
     /* compute tex coords */
     glyph_private->p1.x = node->x / (double) cache->width;
@@ -413,59 +119,14 @@ static cairo_gl_glyph_private_t *
 _cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
 			    cairo_scaled_glyph_t *scaled_glyph)
 {
-    return _rtree_lock (&cache->rtree, scaled_glyph->surface_private);
+    return _cairo_rtree_pin (&cache->rtree, scaled_glyph->surface_private);
 }
 
-static cairo_status_t
-cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache,
-			   cairo_gl_context_t *ctx,
-			   cairo_format_t format,
-			   int width, int height)
-{
-    cairo_content_t content;
-    GLenum internal_format;
-
-    assert ((width & 3) == 0);
-    assert ((height & 1) == 0);
-    cache->width = width;
-    cache->height = height;
-
-    switch (format) {
-    case CAIRO_FORMAT_A1:
-    case CAIRO_FORMAT_RGB24:
-	ASSERT_NOT_REACHED;
-    case CAIRO_FORMAT_ARGB32:
-	content = CAIRO_CONTENT_COLOR_ALPHA;
-	internal_format = GL_RGBA;
-	break;
-    case CAIRO_FORMAT_A8:
-	content = CAIRO_CONTENT_ALPHA;
-	internal_format = GL_ALPHA;
-	break;
-    }
-
-    glGenTextures (1, &cache->tex);
-    glBindTexture (GL_TEXTURE_2D, cache->tex);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
-		  width, height, 0, internal_format, GL_FLOAT, NULL);
-
-    _rtree_init (&cache->rtree,
-		 width, height,
-		 sizeof (cairo_gl_glyph_private_t),
-		 _glyph_evict);
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
+static cairo_gl_glyph_cache_t *
 cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
-				  cairo_format_t format,
-				  cairo_gl_glyph_cache_t **out)
+				  cairo_format_t format)
 {
     cairo_gl_glyph_cache_t *cache;
-    cairo_status_t status;
 
     switch (format) {
     case CAIRO_FORMAT_ARGB32:
@@ -481,15 +142,33 @@ cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
     }
 
     if (unlikely (cache->tex == 0)) {
-	status = cairo_gl_glyph_cache_init (cache, ctx, format,
-					    GLYPH_CACHE_WIDTH,
-					    GLYPH_CACHE_HEIGHT);
-	if (unlikely (status))
-	    return status;
+	GLenum internal_format;
+
+	cache->width = GLYPH_CACHE_WIDTH;
+	cache->height = GLYPH_CACHE_HEIGHT;
+
+	switch (format) {
+	    case CAIRO_FORMAT_A1:
+	    case CAIRO_FORMAT_RGB24:
+		ASSERT_NOT_REACHED;
+	    case CAIRO_FORMAT_ARGB32:
+		internal_format = GL_RGBA;
+		break;
+	    case CAIRO_FORMAT_A8:
+		internal_format = GL_ALPHA;
+		break;
+	}
+
+	glGenTextures (1, &cache->tex);
+	glBindTexture (GL_TEXTURE_2D, cache->tex);
+	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+	glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
+		      GLYPH_CACHE_WIDTH, GLYPH_CACHE_HEIGHT, 0,
+		      internal_format, GL_FLOAT, NULL);
     }
 
-    *out = cache;
-    return CAIRO_STATUS_SUCCESS;
+    return cache;
 }
 
 static cairo_bool_t
@@ -516,8 +195,13 @@ _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
     cairo_gl_glyph_private_t *glyph_private;
 
     glyph_private = scaled_glyph->surface_private;
-    if (glyph_private != NULL)
-	glyph_private->owner = NULL;
+    if (glyph_private != NULL) {
+	glyph_private->node.owner = NULL;
+	if (! glyph_private->node.pinned) {
+	    _cairo_rtree_node_collapse (&glyph_private->cache->rtree,
+		                        glyph_private->node.parent);
+	}
+    }
 }
 
 typedef struct _cairo_gl_glyphs_setup
@@ -576,10 +260,8 @@ _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 	}
     }
 
-    for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++) {
-	_rtree_unlock (&ctx->glyph_cache[i].rtree,
-		       &ctx->glyph_cache[i].rtree.root);
-    }
+    for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++)
+	_cairo_rtree_unpin (&ctx->glyph_cache[i].rtree);
 }
 
 static void
@@ -848,11 +530,8 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	    _cairo_gl_flush_glyphs (ctx, &setup);
 
 	    glActiveTexture (GL_TEXTURE1);
-	    status = cairo_gl_context_get_glyph_cache (ctx,
-						       scaled_glyph->surface->format,
-						       &cache);
-	    if (unlikely (status))
-		goto FINISH;
+	    cache = cairo_gl_context_get_glyph_cache (ctx,
+						      scaled_glyph->surface->format);
 
 	    glBindTexture (GL_TEXTURE_2D, cache->tex);
 
@@ -929,5 +608,18 @@ _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache)
 
     glDeleteTextures (1, &cache->tex);
 
-    _rtree_fini (&cache->rtree);
+    _cairo_rtree_fini (&cache->rtree);
+}
+
+void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
+{
+    cache->tex = 0;
+
+    _cairo_rtree_init (&cache->rtree,
+		       GLYPH_CACHE_WIDTH,
+		       GLYPH_CACHE_HEIGHT,
+		       GLYPH_CACHE_MIN_SIZE,
+		       sizeof (cairo_gl_glyph_private_t),
+		       NULL);
 }
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 5ca48ad..454971f 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -40,7 +40,7 @@
 #define CAIRO_GL_PRIVATE_H
 
 #include "cairoint.h"
-#include "cairo-freelist-private.h"
+#include "cairo-rtree-private.h"
 
 #include <GL/glew.h>
 
@@ -61,27 +61,8 @@ typedef struct _cairo_gl_surface {
     GLuint fb; /* GL framebuffer object wrapping our data. */
 } cairo_gl_surface_t;
 
-enum {
-    RTREE_NODE_AVAILABLE,
-    RTREE_NODE_DIVIDED,
-    RTREE_NODE_OCCUPIED,
-};
-typedef struct _rtree_node {
-    struct _rtree_node *children[4], *parent;
-    uint16_t state;
-    uint16_t locked;
-    uint16_t x, y;
-    uint16_t width, height;
-} rtree_node_t;
-
-typedef struct _rtree {
-    rtree_node_t root;
-    void (*evict) (void *node);
-    cairo_freelist_t node_freelist;
-} rtree_t;
-
 typedef struct cairo_gl_glyph_cache {
-    rtree_t rtree;
+    cairo_rtree_t rtree;
     GLuint tex;
     unsigned int width, height;
 } cairo_gl_glyph_cache_t;
@@ -133,7 +114,7 @@ typedef struct _cairo_gl_composite_setup {
     cairo_gl_composite_operand_t mask;
 } cairo_gl_composite_setup_t;
 
-extern const cairo_surface_backend_t _cairo_gl_surface_backend;
+cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
 
 cairo_private cairo_gl_context_t *
 _cairo_gl_context_create_in_error (cairo_status_t status);
@@ -147,14 +128,14 @@ _cairo_gl_surface_init (cairo_gl_context_t *ctx,
 			cairo_content_t content,
 			int width, int height);
 
-cairo_status_t
+cairo_private cairo_status_t
 _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 			      cairo_image_surface_t *src,
 			      int src_x, int src_y,
 			      int width, int height,
 			      int dst_x, int dst_y);
 
-cairo_int_status_t
+cairo_private cairo_int_status_t
 _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 			const cairo_pattern_t *pattern,
 			cairo_gl_surface_t *dst,
@@ -162,35 +143,38 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 			int dst_x, int dst_y,
 			int width, int height);
 
-cairo_gl_context_t *
+cairo_private cairo_gl_context_t *
 _cairo_gl_context_acquire (cairo_gl_context_t *ctx);
 
-void
+cairo_private void
 _cairo_gl_context_release (cairo_gl_context_t *ctx);
 
-void
+cairo_private void
 _cairo_gl_set_destination (cairo_gl_surface_t *surface);
 
-int
+cairo_private int
 _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op);
 
-void
+cairo_private void
 _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 			   cairo_gl_composite_setup_t *setup);
 
-void
+cairo_private void
 _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand);
 
-cairo_status_t
+cairo_private cairo_status_t
 _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
 				     GLenum *internal_format, GLenum *format,
 				     GLenum *type, cairo_bool_t *has_alpha);
 
-void
+cairo_private void
 _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
 				     cairo_scaled_font_t  *scaled_font);
 
-cairo_int_status_t
+cairo_private void
+_cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache);
+
+cairo_private cairo_int_status_t
 _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 			       cairo_operator_t		 op,
 			       const cairo_pattern_t	*source,
@@ -200,7 +184,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 			       cairo_clip_t		*clip,
 			       int			*remaining_glyphs);
 
-void
+cairo_private void
 _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
 
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 7055bc7..daa2bbc 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -82,6 +82,8 @@ _cairo_gl_context_create_in_error (cairo_status_t status)
 cairo_status_t
 _cairo_gl_context_init (cairo_gl_context_t *ctx)
 {
+    int n;
+
     ctx->status = CAIRO_STATUS_SUCCESS;
     CAIRO_REFERENCE_COUNT_INIT (&ctx->ref_count, 1);
     CAIRO_MUTEX_INIT (ctx->mutex);
@@ -121,6 +123,9 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     ctx->max_texture_size = 0;
     glGetIntegerv (GL_MAX_TEXTURE_SIZE, &ctx->max_texture_size);
 
+    for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++)
+	_cairo_gl_glyph_cache_init (&ctx->glyph_cache[n]);
+
     return CAIRO_STATUS_SUCCESS;
 }
 
commit 37bf06d66efa238c4ff9c74c86a0c9d037b300e6
Author: Eric Anholt <eric at anholt.net>
Date:   Mon Jul 27 09:16:46 2009 -0700

    [gl] Fix handling of clip for glyphs (clip-operator test).

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 4ab1a03..c6d5f6d 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -528,8 +528,19 @@ typedef struct _cairo_gl_glyphs_setup
     unsigned int num_prims;
     float *vb;
     cairo_gl_composite_setup_t *composite;
+    cairo_region_t *clip;
+    cairo_gl_surface_t *dst;
 } cairo_gl_glyphs_setup_t;
 
+static int
+_cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
+{
+    if (surface->fb)
+	return y;
+    else
+	return (surface->height - 1) - y;
+}
+
 static void
 _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 			cairo_gl_glyphs_setup_t *setup)
@@ -541,7 +552,26 @@ _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 	setup->vb = NULL;
 
 	if (setup->num_prims != 0) {
-	    glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
+	    if (setup->clip) {
+		int num_rectangles = cairo_region_num_rectangles (setup->clip);
+
+		glEnable (GL_SCISSOR_TEST);
+		for (i = 0; i < num_rectangles; i++) {
+		    cairo_rectangle_int_t rect;
+
+		    cairo_region_get_rectangle (setup->clip, i, &rect);
+
+		    glScissor (rect.x,
+			       _cairo_gl_y_flip (setup->dst, rect.y),
+			       rect.x + rect.width,
+			       _cairo_gl_y_flip (setup->dst,
+						 rect.y + rect.height));
+		    glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
+		}
+		glDisable (GL_SCISSOR_TEST);
+	    } else {
+		glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
+	    }
 	    setup->num_prims = 0;
 	}
     }
@@ -690,6 +720,14 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     if (! _cairo_gl_surface_owns_font (dst, scaled_font))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
+    if (clip != NULL) {
+	status = _cairo_clip_get_region (clip, &clip_region);
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    return CAIRO_STATUS_SUCCESS;
+	else if (status != CAIRO_STATUS_SUCCESS)
+	    return status;
+    }
+
     /* XXX If glyphs overlap, build tmp mask and composite.
      * Could we use the stencil instead but only write if alpha !=0 ?
      * TEXKILL? PIXELKILL?
@@ -752,6 +790,8 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
      */
     memset(&setup, 0, sizeof(setup));
     setup.composite = &composite_setup;
+    setup.clip = clip_region;
+    setup.dst = dst;
     setup.vertex_size = 4;
     if (composite_setup.src.type == OPERAND_TEXTURE)
 	setup.vertex_size += 2;
@@ -854,6 +894,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 
   CLEANUP_CONTEXT:
     glDisable (GL_BLEND);
+    glDisable (GL_SCISSOR_TEST);
 
     glDisableClientState (GL_VERTEX_ARRAY);
 
commit 74f33513194ec3d8e69a21aa71aa7fc854415e83
Author: Eric Anholt <eric at anholt.net>
Date:   Fri Jul 24 14:08:55 2009 -0700

    [gl] Switch the glyph cache to using VBOs.
    
    This is a major performance improvement for GL even on non-TNL hardware
    (i915), as we get to emit one giant DrawArrays and make GL drivers love us.
    Now we're actually faster than not having the glyph cache.
    
    Before glyph cache (HP 945GM):
    [  0]       gl             firefox-20090601  238.103  238.195   0.35%    5/6
    After:
    [  0]       gl             firefox-20090601   68.181   76.735   5.46%    6/6

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 7fe0123..4ab1a03 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -520,50 +520,91 @@ _cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
 	glyph_private->owner = NULL;
 }
 
+typedef struct _cairo_gl_glyphs_setup
+{
+    unsigned int vbo_size; /* units of floats */
+    unsigned int vb_offset; /* units of floats */
+    unsigned int vertex_size; /* units of floats */
+    unsigned int num_prims;
+    float *vb;
+    cairo_gl_composite_setup_t *composite;
+} cairo_gl_glyphs_setup_t;
+
 static void
-_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
-				cairo_gl_composite_setup_t *setup,
-				int x1, int y1,
-				int x2, int y2,
-				cairo_gl_glyph_private_t *glyph,
-				float *vertices, float *texcoord_src,
-				float *texcoord_mask)
+_cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
+			cairo_gl_glyphs_setup_t *setup)
 {
-    double s, t;
     int i;
-    cairo_surface_attributes_t *src_attributes;
 
-    src_attributes = &setup->src.operand.texture.attributes;
-
-    vertices[0] = x1;
-    vertices[1] = y1;
-    vertices[2] = x2;
-    vertices[3] = y1;
-    vertices[4] = x2;
-    vertices[5] = y2;
-    vertices[6] = x1;
-    vertices[7] = y2;
-
-    if (setup->src.type == OPERAND_TEXTURE) {
-	for (i = 0; i < 4; i++) {
-	    s = vertices[i * 2];
-	    t = vertices[i * 2 + 1];
-	    cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
-	    texcoord_src[i * 2] = s;
-	    texcoord_src[i * 2 + 1] = t;
+    if (setup->vb != NULL) {
+	glUnmapBufferARB (GL_ARRAY_BUFFER_ARB);
+	setup->vb = NULL;
+
+	if (setup->num_prims != 0) {
+	    glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
+	    setup->num_prims = 0;
 	}
     }
 
-    texcoord_mask[0] = glyph->p1.x;
-    texcoord_mask[1] = glyph->p1.y;
-    texcoord_mask[2] = glyph->p2.x;
-    texcoord_mask[3] = glyph->p1.y;
-    texcoord_mask[4] = glyph->p2.x;
-    texcoord_mask[5] = glyph->p2.y;
-    texcoord_mask[6] = glyph->p1.x;
-    texcoord_mask[7] = glyph->p2.y;
+    for (i = 0; i < ARRAY_LENGTH (ctx->glyph_cache); i++) {
+	_rtree_unlock (&ctx->glyph_cache[i].rtree,
+		       &ctx->glyph_cache[i].rtree.root);
+    }
+}
+
+static void
+_cairo_gl_glyphs_emit_vertex (cairo_gl_glyphs_setup_t *setup,
+			      int x, int y, float glyph_x, float glyph_y)
+{
+    int i = 0;
+    float *vb = &setup->vb[setup->vb_offset];
+    cairo_surface_attributes_t *src_attributes;
+
+    src_attributes = &setup->composite->src.operand.texture.attributes;
+
+    vb[i++] = x;
+    vb[i++] = y;
+
+    vb[i++] = glyph_x;
+    vb[i++] = glyph_y;
+
+    if (setup->composite->src.type == OPERAND_TEXTURE) {
+	double s = x;
+	double t = y;
+	cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+	vb[i++] = s;
+	vb[i++] = t;
+    }
+
+    setup->vb_offset += setup->vertex_size;
+}
+
+
+static void
+_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
+				cairo_gl_glyphs_setup_t *setup,
+				int x1, int y1,
+				int x2, int y2,
+				cairo_gl_glyph_private_t *glyph)
+{
+    if (setup->vb != NULL &&
+	setup->vb_offset + 4 * setup->vertex_size > setup->vbo_size) {
+	_cairo_gl_flush_glyphs (ctx, setup);
+    }
 
-    glDrawArrays (GL_QUADS, 0, 4);
+    if (setup->vb == NULL) {
+	glBufferDataARB (GL_ARRAY_BUFFER_ARB,
+			 setup->vbo_size * sizeof (GLfloat),
+			 NULL, GL_STREAM_DRAW_ARB);
+	setup->vb = glMapBufferARB (GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB);
+	setup->vb_offset = 0;
+    }
+
+    _cairo_gl_glyphs_emit_vertex (setup, x1, y1, glyph->p1.x, glyph->p1.y);
+    _cairo_gl_glyphs_emit_vertex (setup, x2, y1, glyph->p2.x, glyph->p1.y);
+    _cairo_gl_glyphs_emit_vertex (setup, x2, y2, glyph->p2.x, glyph->p2.y);
+    _cairo_gl_glyphs_emit_vertex (setup, x1, y2, glyph->p1.x, glyph->p2.y);
+    setup->num_prims++;
 }
 
 cairo_int_status_t
@@ -583,10 +624,11 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     cairo_gl_glyph_cache_t *cache = NULL;
     cairo_status_t status;
     int i = 0;
-    cairo_gl_composite_setup_t setup;
-    float vertices[4 * 2], texcoord_src[4 * 2], texcoord_mask[4 * 2];
     cairo_region_t *clip_region = NULL;
     cairo_solid_pattern_t solid_pattern;
+    cairo_gl_glyphs_setup_t setup;
+    cairo_gl_composite_setup_t composite_setup;
+    GLuint vbo = 0;
 
     /* Just let unbounded operators go through the fallback code
      * instead of trying to do the fixups here */
@@ -660,7 +702,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     if (unlikely (status))
 	return status;
 
-    status = _cairo_gl_operand_init (&setup.src, source, dst,
+    status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
 				     extents.x, extents.y,
 				     extents.x, extents.y,
 				     extents.width, extents.height);
@@ -693,7 +735,7 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     if (status != CAIRO_STATUS_SUCCESS)
 	goto CLEANUP_CONTEXT;
 
-    _cairo_gl_set_src_operand (ctx, &setup);
+    _cairo_gl_set_src_operand (ctx, &composite_setup);
 
     _cairo_scaled_font_freeze_cache (scaled_font);
     if (! _cairo_gl_surface_owns_font (dst, scaled_font))
@@ -705,15 +747,36 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	scaled_font->surface_backend = &_cairo_gl_surface_backend;
     }
 
-    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
+    /* Create our VBO so that we can accumulate a bunch of glyph primitives
+     * into one giant DrawArrays.
+     */
+    memset(&setup, 0, sizeof(setup));
+    setup.composite = &composite_setup;
+    setup.vertex_size = 4;
+    if (composite_setup.src.type == OPERAND_TEXTURE)
+	setup.vertex_size += 2;
+    setup.vbo_size = num_glyphs * 4 * setup.vertex_size;
+    if (setup.vbo_size > 4096)
+	setup.vbo_size = 4096;
+
+    glGenBuffersARB (1, &vbo);
+    glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
+
+    glVertexPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
+		     (void *)(uintptr_t)(0));
     glEnableClientState (GL_VERTEX_ARRAY);
-    if (setup.src.type == OPERAND_TEXTURE) {
+    if (composite_setup.src.type == OPERAND_TEXTURE) {
+	/* Note that we're packing texcoord 0 after texcoord 1, for
+	 * convenience.
+	 */
 	glClientActiveTexture (GL_TEXTURE0);
-	glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_src);
+	glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
+			   (void *)(uintptr_t)(4 * sizeof (GLfloat)));
 	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
     }
     glClientActiveTexture (GL_TEXTURE1);
-    glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_mask);
+    glTexCoordPointer (2, GL_FLOAT, setup.vertex_size * sizeof (GLfloat),
+		       (void *)(uintptr_t)(2 * sizeof (GLfloat)));
     glEnableClientState (GL_TEXTURE_COORD_ARRAY);
 
     for (i = 0; i < num_glyphs; i++) {
@@ -741,6 +804,9 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 	}
 
 	if (scaled_glyph->surface->format != last_format) {
+	    /* Switching textures, so flush any queued prims. */
+	    _cairo_gl_flush_glyphs (ctx, &setup);
+
 	    glActiveTexture (GL_TEXTURE1);
 	    status = cairo_gl_context_get_glyph_cache (ctx,
 						       scaled_glyph->surface->format,
@@ -759,16 +825,8 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 		goto FINISH;
 
 	    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-		int n;
-
-		/* If we were accumulating VBOs like sensible people, this is where
-		 * we'd flush because the cache was full.  But we're slackers.
-		 */
-
-		for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) {
-		    _rtree_unlock (&ctx->glyph_cache[n].rtree,
-				   &ctx->glyph_cache[n].rtree.root);
-		}
+		/* Cache is full, so flush existing prims and try again. */
+		_cairo_gl_flush_glyphs (ctx, &setup);
 	    }
 
 	    status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph);
@@ -786,12 +844,12 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 
 	_cairo_gl_emit_glyph_rectangle (ctx, &setup,
 					x1, y1, x2, y2,
-					_cairo_gl_glyph_cache_lock (cache, scaled_glyph),
-					vertices, texcoord_src, texcoord_mask);
+					_cairo_gl_glyph_cache_lock (cache, scaled_glyph));
     }
 
     status = CAIRO_STATUS_SUCCESS;
   FINISH:
+    _cairo_gl_flush_glyphs (ctx, &setup);
     _cairo_scaled_font_thaw_cache (scaled_font);
 
   CLEANUP_CONTEXT:
@@ -809,9 +867,14 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
     glActiveTexture (GL_TEXTURE1);
     glDisable (GL_TEXTURE_2D);
 
+    if (vbo != 0) {
+	glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
+	glDeleteBuffersARB (1, &vbo);
+    }
+
     _cairo_gl_context_release (ctx);
 
-    _cairo_gl_operand_destroy (&setup.src);
+    _cairo_gl_operand_destroy (&composite_setup.src);
 
     *remaining_glyphs = num_glyphs - i;
     return status;
commit ee4138e587a67c3fc4d0e89b899109af89f16e72
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Jul 23 21:12:59 2009 -0700

    [gl] Start adding glyph cache support.
    
    This is pretty much cut-and-paste from ickle's cairo-drm-i915-surface, and
    uses fixed function and doesn't use VBOs.

diff --git a/src/Makefile.sources b/src/Makefile.sources
index dd8501e..ca8d51d 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -264,7 +264,7 @@ cairo_beos_headers = cairo-beos.h
 
 cairo_gl_headers = cairo-gl.h
 cairo_gl_private = cairo-gl-private.h
-cairo_gl_sources = cairo-gl-surface.c
+cairo_gl_sources = cairo-gl-surface.c cairo-gl-glyphs.c
 cairo_glx_sources += cairo-glx-context.c
 cairo_eagle_sources += cairo-eagle-context.c
 
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
new file mode 100644
index 0000000..7fe0123
--- /dev/null
+++ b/src/cairo-gl-glyphs.c
@@ -0,0 +1,829 @@
+/* Cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2009 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is Chris Wilson.
+ */
+
+#include "cairoint.h"
+
+#include "cairo-gl-private.h"
+#include "cairo-freelist-private.h"
+
+#define GLYPH_CACHE_WIDTH 1024
+#define GLYPH_CACHE_HEIGHT 1024
+#define GLYPH_CACHE_MIN_SIZE 4
+#define GLYPH_CACHE_MAX_SIZE 128
+
+typedef struct _cairo_gl_glyph_private {
+    rtree_node_t node;
+    void **owner;
+    struct { float x, y; } p1, p2;
+} cairo_gl_glyph_private_t;
+
+static void
+_rtree_node_evict (rtree_t *rtree, rtree_node_t *node)
+{
+    rtree->evict (node);
+    node->state = RTREE_NODE_AVAILABLE;
+}
+
+static rtree_node_t *
+_rtree_node_create (rtree_t		 *rtree,
+		    rtree_node_t	 *parent,
+		    int			  x,
+		    int			  y,
+		    int			  width,
+		    int			  height)
+{
+    rtree_node_t *node;
+
+    /* XXX chunked freelist */
+    node = _cairo_freelist_alloc (&rtree->node_freelist);
+    if (node == NULL) {
+	_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+	return NULL;
+    }
+
+    memset (node->children, 0, sizeof (node->children));
+    node->parent = parent;
+    node->state  = RTREE_NODE_AVAILABLE;
+    node->locked = FALSE;
+    node->x	 = x;
+    node->y	 = y;
+    node->width  = width;
+    node->height = height;
+
+    return node;
+}
+
+static void
+_rtree_node_destroy (rtree_t *rtree, rtree_node_t *node)
+{
+    int i;
+
+    if (node == NULL)
+	return;
+
+    if (node->state == RTREE_NODE_OCCUPIED) {
+	_rtree_node_evict (rtree, node);
+    } else {
+	for (i = 0; i < 4 && node->children[i] != NULL; i++)
+	    _rtree_node_destroy (rtree, node->children[i]);
+    }
+
+    _cairo_freelist_free (&rtree->node_freelist, node);
+}
+
+static cairo_int_status_t
+_rtree_insert (rtree_t	     *rtree,
+	       rtree_node_t  *node,
+	       int	      width,
+	       int	      height,
+	       rtree_node_t **out)
+{
+    cairo_status_t status;
+    int i;
+
+    switch (node->state) {
+    case RTREE_NODE_DIVIDED:
+	for (i = 0; i < 4 && node->children[i] != NULL; i++) {
+	    if (node->children[i]->width  >= width &&
+		node->children[i]->height >= height)
+	    {
+		status = _rtree_insert (rtree, node->children[i],
+					width, height,
+					out);
+		if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+		    return status;
+	    }
+	}
+
+    default:
+    case RTREE_NODE_OCCUPIED:
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    case RTREE_NODE_AVAILABLE:
+	if (node->width  - width  > GLYPH_CACHE_MIN_SIZE ||
+	    node->height - height > GLYPH_CACHE_MIN_SIZE)
+	{
+	    int w, h;
+
+	    w = node->width  - width;
+	    h = node->height - height;
+
+	    i = 0;
+	    node->children[i] = _rtree_node_create (rtree, node,
+						    node->x, node->y,
+						    width, height);
+	    if (unlikely (node->children[i] == NULL))
+		return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+	    i++;
+
+	    if (w > GLYPH_CACHE_MIN_SIZE) {
+		node->children[i] = _rtree_node_create (rtree, node,
+							node->x + width,
+							node->y,
+							w, height);
+		if (unlikely (node->children[i] == NULL))
+		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		i++;
+	    }
+
+	    if (h > GLYPH_CACHE_MIN_SIZE) {
+		node->children[i] = _rtree_node_create (rtree, node,
+							node->x,
+							node->y + height,
+							width, h);
+		if (unlikely (node->children[i] == NULL))
+		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		i++;
+
+		if (w > GLYPH_CACHE_MIN_SIZE) {
+		    node->children[i] = _rtree_node_create (rtree, node,
+							    node->x + width,
+							    node->y + height,
+							    w, h);
+		    if (unlikely (node->children[i] == NULL))
+			return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+		    i++;
+		}
+	    }
+
+	    node->state = RTREE_NODE_DIVIDED;
+	    node = node->children[0];
+	}
+
+	node->state = RTREE_NODE_OCCUPIED;
+	*out = node;
+	return CAIRO_STATUS_SUCCESS;
+    }
+}
+
+static cairo_int_status_t
+_rtree_add_evictable_nodes (rtree_t *rtree,
+			    rtree_node_t *node,
+			    int width,
+			    int height,
+			    cairo_array_t *evictable_nodes)
+{
+    cairo_int_status_t status;
+    cairo_bool_t child_added = FALSE;
+    int i;
+
+    switch (node->state) {
+    case RTREE_NODE_DIVIDED:
+	for (i = 0; i < 4 && node->children[i] != NULL; i++) {
+	    if (node->children[i]->width  >= width &&
+		node->children[i]->height >= height)
+	    {
+		status = _rtree_add_evictable_nodes (rtree, node->children[i],
+						     width, height,
+						     evictable_nodes);
+		if (_cairo_status_is_error (status))
+		    return status;
+
+		child_added |= status == CAIRO_STATUS_SUCCESS;
+	    }
+	}
+	if (child_added)
+	    return CAIRO_STATUS_SUCCESS;
+
+	/* fall through */
+    case RTREE_NODE_AVAILABLE:
+    case RTREE_NODE_OCCUPIED:
+	if (node->locked)
+	    return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return _cairo_array_append (evictable_nodes, &node);
+}
+
+static uint32_t
+hars_petruska_f54_1_random (void)
+{
+#define rol(x,k) ((x << k) | (x >> (32-k)))
+    static uint32_t x;
+    return x = (x ^ rol (x, 5) ^ rol (x, 24)) + 0x37798849;
+#undef rol
+}
+
+static cairo_int_status_t
+_rtree_evict_random (rtree_t	*rtree,
+		     rtree_node_t *root,
+		     int	 width,
+		     int	 height,
+		     rtree_node_t **out)
+{
+    cairo_array_t evictable_nodes;
+    cairo_status_t status;
+    int i;
+
+    _cairo_array_init (&evictable_nodes, sizeof (rtree_node_t *));
+
+    status = _rtree_add_evictable_nodes (rtree, root,
+					 width, height,
+					 &evictable_nodes);
+    if (status == CAIRO_STATUS_SUCCESS) {
+	rtree_node_t *node;
+
+	node = *(rtree_node_t **)
+	    _cairo_array_index (&evictable_nodes,
+				hars_petruska_f54_1_random () % evictable_nodes.num_elements);
+	if (node->state == RTREE_NODE_OCCUPIED) {
+	    _rtree_node_evict (rtree, node);
+	} else {
+	    for (i = 0; i < 4 && node->children[i] != NULL; i++) {
+		_rtree_node_destroy (rtree, node->children[i]);
+		node->children[i] = NULL;
+	    }
+	}
+
+	node->state = RTREE_NODE_AVAILABLE;
+	*out = node;
+    }
+
+    _cairo_array_fini (&evictable_nodes);
+
+    return status;
+}
+
+static void *
+_rtree_lock (rtree_t *rtree, rtree_node_t *node)
+{
+    void *ptr = node;
+
+    while (node != NULL && ! node->locked) {
+	node->locked = TRUE;
+	node = node->parent;
+    }
+
+    return ptr;
+}
+
+static void
+_rtree_unlock (rtree_t *rtree, rtree_node_t *node)
+{
+    int i;
+
+    if (! node->locked)
+	return;
+
+    node->locked = FALSE;
+    if (node->state == RTREE_NODE_DIVIDED) {
+	for (i = 0; i < 4 && node->children[i] != NULL; i++)
+	    _rtree_unlock (rtree, node->children[i]);
+    }
+}
+
+static void
+_rtree_init (rtree_t	    *rtree,
+	     int	     width,
+	     int	     height,
+	     int             node_size,
+	     void (*evict) (void *node))
+{
+    rtree->evict = evict;
+
+    assert (node_size >= (int) sizeof (rtree_node_t));
+    _cairo_freelist_init (&rtree->node_freelist, node_size);
+
+    memset (&rtree->root, 0, sizeof (rtree->root));
+    rtree->root.width = width;
+    rtree->root.height = height;
+}
+
+static void
+_rtree_fini (rtree_t *rtree)
+{
+    int i;
+
+    if (rtree->root.state == RTREE_NODE_OCCUPIED) {
+	_rtree_node_evict (rtree, &rtree->root);
+    } else {
+	for (i = 0; i < 4 && rtree->root.children[i] != NULL; i++)
+	    _rtree_node_destroy (rtree, rtree->root.children[i]);
+    }
+
+    _cairo_freelist_fini (&rtree->node_freelist);
+}
+
+static void
+_glyph_evict (void *node)
+{
+    cairo_gl_glyph_private_t *glyph_private = node;
+
+    if (glyph_private->owner != NULL)
+	*glyph_private->owner = NULL;
+}
+
+static cairo_status_t
+_cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
+				 cairo_scaled_glyph_t  *scaled_glyph)
+{
+    cairo_image_surface_t *glyph_surface = scaled_glyph->surface;
+    cairo_gl_glyph_private_t *glyph_private;
+    rtree_node_t *node = NULL;
+    cairo_status_t status;
+    int width, height;
+    GLenum internal_format, format, type;
+    cairo_bool_t has_alpha;
+
+    status = _cairo_gl_get_image_format_and_type (glyph_surface->pixman_format,
+						  &internal_format,
+						  &format,
+						  &type,
+						  &has_alpha);
+    if (status != CAIRO_STATUS_SUCCESS)
+	return status;
+
+    width = glyph_surface->width;
+    if (width < GLYPH_CACHE_MIN_SIZE)
+	width = GLYPH_CACHE_MIN_SIZE;
+    height = glyph_surface->height;
+    if (height < GLYPH_CACHE_MIN_SIZE)
+	height = GLYPH_CACHE_MIN_SIZE;
+
+    /* search for an available slot */
+    status = _rtree_insert (&cache->rtree, &cache->rtree.root,
+			    width, height, &node);
+    /* search for an unlocked slot */
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	status = _rtree_evict_random (&cache->rtree, &cache->rtree.root,
+				      width, height, &node);
+	if (status == CAIRO_STATUS_SUCCESS)
+	    status = _rtree_insert (&cache->rtree, node, width, height, &node);
+    }
+    if (status)
+	return status;
+
+    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+    glPixelStorei (GL_UNPACK_ROW_LENGTH,
+		   glyph_surface->stride /
+		   (PIXMAN_FORMAT_BPP (glyph_surface->pixman_format) / 8));
+    glTexSubImage2D (GL_TEXTURE_2D, 0,
+		     node->x, node->y,
+		     glyph_surface->width, glyph_surface->height,
+		     format, type,
+		     glyph_surface->data);
+    glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
+
+    scaled_glyph->surface_private = node;
+
+    glyph_private = (cairo_gl_glyph_private_t *) node;
+    glyph_private->owner = &scaled_glyph->surface_private;
+
+    /* compute tex coords */
+    glyph_private->p1.x = node->x / (double) cache->width;
+    glyph_private->p1.y = node->y / (double) cache->height;
+    glyph_private->p2.x =
+	(node->x + glyph_surface->width) / (double) cache->width;
+    glyph_private->p2.y =
+	(node->y + glyph_surface->height) / (double) cache->height;
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_gl_glyph_private_t *
+_cairo_gl_glyph_cache_lock (cairo_gl_glyph_cache_t *cache,
+			    cairo_scaled_glyph_t *scaled_glyph)
+{
+    return _rtree_lock (&cache->rtree, scaled_glyph->surface_private);
+}
+
+static cairo_status_t
+cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache,
+			   cairo_gl_context_t *ctx,
+			   cairo_format_t format,
+			   int width, int height)
+{
+    cairo_content_t content;
+    GLenum internal_format;
+
+    assert ((width & 3) == 0);
+    assert ((height & 1) == 0);
+    cache->width = width;
+    cache->height = height;
+
+    switch (format) {
+    case CAIRO_FORMAT_A1:
+    case CAIRO_FORMAT_RGB24:
+	ASSERT_NOT_REACHED;
+    case CAIRO_FORMAT_ARGB32:
+	content = CAIRO_CONTENT_COLOR_ALPHA;
+	internal_format = GL_RGBA;
+	break;
+    case CAIRO_FORMAT_A8:
+	content = CAIRO_CONTENT_ALPHA;
+	internal_format = GL_ALPHA;
+	break;
+    }
+
+    glGenTextures (1, &cache->tex);
+    glBindTexture (GL_TEXTURE_2D, cache->tex);
+    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexImage2D (GL_TEXTURE_2D, 0, internal_format,
+		  width, height, 0, internal_format, GL_FLOAT, NULL);
+
+    _rtree_init (&cache->rtree,
+		 width, height,
+		 sizeof (cairo_gl_glyph_private_t),
+		 _glyph_evict);
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+cairo_gl_context_get_glyph_cache (cairo_gl_context_t *ctx,
+				  cairo_format_t format,
+				  cairo_gl_glyph_cache_t **out)
+{
+    cairo_gl_glyph_cache_t *cache;
+    cairo_status_t status;
+
+    switch (format) {
+    case CAIRO_FORMAT_ARGB32:
+    case CAIRO_FORMAT_RGB24:
+	cache = &ctx->glyph_cache[0];
+	format = CAIRO_FORMAT_ARGB32;
+	break;
+    case CAIRO_FORMAT_A8:
+    case CAIRO_FORMAT_A1:
+	cache = &ctx->glyph_cache[1];
+	format = CAIRO_FORMAT_A8;
+	break;
+    }
+
+    if (unlikely (cache->tex == 0)) {
+	status = cairo_gl_glyph_cache_init (cache, ctx, format,
+					    GLYPH_CACHE_WIDTH,
+					    GLYPH_CACHE_HEIGHT);
+	if (unlikely (status))
+	    return status;
+    }
+
+    *out = cache;
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_bool_t
+_cairo_gl_surface_owns_font (cairo_gl_surface_t *surface,
+			     cairo_scaled_font_t *scaled_font)
+{
+    cairo_gl_context_t *font_private;
+
+    font_private = scaled_font->surface_private;
+    if ((scaled_font->surface_backend != NULL &&
+	 scaled_font->surface_backend != &_cairo_gl_surface_backend) ||
+	(font_private != NULL && font_private != surface->ctx))
+    {
+	return FALSE;
+    }
+
+    return TRUE;
+}
+
+void
+_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+				     cairo_scaled_font_t  *scaled_font)
+{
+    cairo_gl_glyph_private_t *glyph_private;
+
+    glyph_private = scaled_glyph->surface_private;
+    if (glyph_private != NULL)
+	glyph_private->owner = NULL;
+}
+
+static void
+_cairo_gl_emit_glyph_rectangle (cairo_gl_context_t *ctx,
+				cairo_gl_composite_setup_t *setup,
+				int x1, int y1,
+				int x2, int y2,
+				cairo_gl_glyph_private_t *glyph,
+				float *vertices, float *texcoord_src,
+				float *texcoord_mask)
+{
+    double s, t;
+    int i;
+    cairo_surface_attributes_t *src_attributes;
+
+    src_attributes = &setup->src.operand.texture.attributes;
+
+    vertices[0] = x1;
+    vertices[1] = y1;
+    vertices[2] = x2;
+    vertices[3] = y1;
+    vertices[4] = x2;
+    vertices[5] = y2;
+    vertices[6] = x1;
+    vertices[7] = y2;
+
+    if (setup->src.type == OPERAND_TEXTURE) {
+	for (i = 0; i < 4; i++) {
+	    s = vertices[i * 2];
+	    t = vertices[i * 2 + 1];
+	    cairo_matrix_transform_point (&src_attributes->matrix, &s, &t);
+	    texcoord_src[i * 2] = s;
+	    texcoord_src[i * 2 + 1] = t;
+	}
+    }
+
+    texcoord_mask[0] = glyph->p1.x;
+    texcoord_mask[1] = glyph->p1.y;
+    texcoord_mask[2] = glyph->p2.x;
+    texcoord_mask[3] = glyph->p1.y;
+    texcoord_mask[4] = glyph->p2.x;
+    texcoord_mask[5] = glyph->p2.y;
+    texcoord_mask[6] = glyph->p1.x;
+    texcoord_mask[7] = glyph->p2.y;
+
+    glDrawArrays (GL_QUADS, 0, 4);
+}
+
+cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void			*abstract_dst,
+			       cairo_operator_t		 op,
+			       const cairo_pattern_t	*source,
+			       cairo_glyph_t		*glyphs,
+			       int			 num_glyphs,
+			       cairo_scaled_font_t	*scaled_font,
+			       cairo_clip_t		*clip,
+			       int			*remaining_glyphs)
+{
+    cairo_gl_surface_t *dst = abstract_dst;
+    cairo_gl_context_t *ctx;
+    cairo_rectangle_int_t extents;
+    cairo_format_t last_format = (cairo_format_t) -1;
+    cairo_gl_glyph_cache_t *cache = NULL;
+    cairo_status_t status;
+    int i = 0;
+    cairo_gl_composite_setup_t setup;
+    float vertices[4 * 2], texcoord_src[4 * 2], texcoord_mask[4 * 2];
+    cairo_region_t *clip_region = NULL;
+    cairo_solid_pattern_t solid_pattern;
+
+    /* Just let unbounded operators go through the fallback code
+     * instead of trying to do the fixups here */
+    if (! _cairo_operator_bounded_by_mask (op))
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* For CLEAR, cairo's rendering equation (quoting Owen's description in:
+     * http://lists.cairographics.org/archives/cairo/2005-August/004992.html)
+     * is:
+     *     mask IN clip ? src OP dest : dest
+     * or more simply:
+     *     mask IN CLIP ? 0 : dest
+     *
+     * where the ternary operator A ? B : C is (A * B) + ((1 - A) * C).
+     *
+     * The model we use in _cairo_gl_set_operator() is Render's:
+     *     src IN mask IN clip OP dest
+     * which would boil down to:
+     *     0 (bounded by the extents of the drawing).
+     *
+     * However, we can do a Render operation using an opaque source
+     * and DEST_OUT to produce:
+     *    1 IN mask IN clip DEST_OUT dest
+     * which is
+     *    mask IN clip ? 0 : dest
+     */
+    if (op == CAIRO_OPERATOR_CLEAR) {
+	_cairo_pattern_init_solid (&solid_pattern, CAIRO_COLOR_WHITE,
+				   CAIRO_CONTENT_COLOR);
+	source = &solid_pattern.base;
+	op = CAIRO_OPERATOR_DEST_OUT;
+    }
+
+    /* For SOURCE, cairo's rendering equation is:
+     *     (mask IN clip) ? src OP dest : dest
+     * or more simply:
+     *     (mask IN clip) ? src : dest.
+     *
+     * If we just used the Render equation, we would get:
+     *     (src IN mask IN clip) OP dest
+     * or:
+     *     (src IN mask IN clip) bounded by extents of the drawing.
+     *
+     * The trick is that for GL blending, we only get our 4 source values
+     * into the blender, and since we need all 4 components of source, we
+     * can't also get the mask IN clip into the blender.  But if we did
+     * two passes we could make it work:
+     *     dest = (mask IN clip) DEST_OUT dest
+     *     dest = src IN mask IN clip ADD dest
+     *
+     * But for now, fall back :)
+     */
+    if (op == CAIRO_OPERATOR_SOURCE)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX we don't need ownership of the font as we use a global
+     * glyph cache -- but we do need scaled_glyph eviction notification. :-(
+     */
+    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* XXX If glyphs overlap, build tmp mask and composite.
+     * Could we use the stencil instead but only write if alpha !=0 ?
+     * TEXKILL? PIXELKILL?
+     * Antialiasing issues - but using glyph images cause their own anyway.
+     */
+
+    status = _cairo_scaled_font_glyph_device_extents (scaled_font,
+						      glyphs, num_glyphs,
+						      &extents);
+    if (unlikely (status))
+	return status;
+
+    status = _cairo_gl_operand_init (&setup.src, source, dst,
+				     extents.x, extents.y,
+				     extents.x, extents.y,
+				     extents.width, extents.height);
+    if (unlikely (status))
+	return status;
+
+    ctx = _cairo_gl_context_acquire (dst->ctx);
+
+    /* Set up the mask to source from the incoming vertex color. */
+    glActiveTexture (GL_TEXTURE1);
+    glEnable (GL_TEXTURE_2D);
+    /* IN: dst.argb = src.argb * mask.aaaa */
+    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
+    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE);
+
+    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
+    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+
+    glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE1);
+    glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE1);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
+    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+
+    _cairo_gl_set_destination (dst);
+
+    status = _cairo_gl_set_operator (dst, op);
+    if (status != CAIRO_STATUS_SUCCESS)
+	goto CLEANUP_CONTEXT;
+
+    _cairo_gl_set_src_operand (ctx, &setup);
+
+    _cairo_scaled_font_freeze_cache (scaled_font);
+    if (! _cairo_gl_surface_owns_font (dst, scaled_font))
+	goto CLEANUP_CONTEXT;
+
+    if (scaled_font->surface_private == NULL) {
+	/* XXX couple into list to remove on context destruction */
+	scaled_font->surface_private = ctx;
+	scaled_font->surface_backend = &_cairo_gl_surface_backend;
+    }
+
+    glVertexPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, vertices);
+    glEnableClientState (GL_VERTEX_ARRAY);
+    if (setup.src.type == OPERAND_TEXTURE) {
+	glClientActiveTexture (GL_TEXTURE0);
+	glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_src);
+	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+    }
+    glClientActiveTexture (GL_TEXTURE1);
+    glTexCoordPointer (2, GL_FLOAT, sizeof (GLfloat) * 2, texcoord_mask);
+    glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+    for (i = 0; i < num_glyphs; i++) {
+	cairo_scaled_glyph_t *scaled_glyph;
+	double x_offset, y_offset;
+	double x1, x2, y1, y2;
+
+	status = _cairo_scaled_glyph_lookup (scaled_font,
+					     glyphs[i].index,
+					     CAIRO_SCALED_GLYPH_INFO_SURFACE,
+					     &scaled_glyph);
+	if (unlikely (status))
+	    goto FINISH;
+
+	if (scaled_glyph->surface->width  == 0 ||
+	    scaled_glyph->surface->height == 0)
+	{
+	    continue;
+	}
+	if (scaled_glyph->surface->width  > GLYPH_CACHE_MAX_SIZE ||
+	    scaled_glyph->surface->height > GLYPH_CACHE_MAX_SIZE)
+	{
+	    status = CAIRO_INT_STATUS_UNSUPPORTED;
+	    goto FINISH;
+	}
+
+	if (scaled_glyph->surface->format != last_format) {
+	    glActiveTexture (GL_TEXTURE1);
+	    status = cairo_gl_context_get_glyph_cache (ctx,
+						       scaled_glyph->surface->format,
+						       &cache);
+	    if (unlikely (status))
+		goto FINISH;
+
+	    glBindTexture (GL_TEXTURE_2D, cache->tex);
+
+	    last_format = scaled_glyph->surface->format;
+	}
+
+	if (scaled_glyph->surface_private == NULL) {
+	    status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph);
+	    if (unlikely (_cairo_status_is_error (status)))
+		goto FINISH;
+
+	    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+		int n;
+
+		/* If we were accumulating VBOs like sensible people, this is where
+		 * we'd flush because the cache was full.  But we're slackers.
+		 */
+
+		for (n = 0; n < ARRAY_LENGTH (ctx->glyph_cache); n++) {
+		    _rtree_unlock (&ctx->glyph_cache[n].rtree,
+				   &ctx->glyph_cache[n].rtree.root);
+		}
+	    }
+
+	    status = _cairo_gl_glyph_cache_add_glyph (cache, scaled_glyph);
+	    if (unlikely (status))
+		goto FINISH;
+	}
+
+	x_offset = scaled_glyph->surface->base.device_transform.x0;
+	y_offset = scaled_glyph->surface->base.device_transform.y0;
+
+	x1 = _cairo_lround (glyphs[i].x - x_offset);
+	y1 = _cairo_lround (glyphs[i].y - y_offset);
+	x2 = x1 + scaled_glyph->surface->width;
+	y2 = y1 + scaled_glyph->surface->height;
+
+	_cairo_gl_emit_glyph_rectangle (ctx, &setup,
+					x1, y1, x2, y2,
+					_cairo_gl_glyph_cache_lock (cache, scaled_glyph),
+					vertices, texcoord_src, texcoord_mask);
+    }
+
+    status = CAIRO_STATUS_SUCCESS;
+  FINISH:
+    _cairo_scaled_font_thaw_cache (scaled_font);
+
+  CLEANUP_CONTEXT:
+    glDisable (GL_BLEND);
+
+    glDisableClientState (GL_VERTEX_ARRAY);
+
+    glClientActiveTexture (GL_TEXTURE0);
+    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+    glActiveTexture (GL_TEXTURE0);
+    glDisable (GL_TEXTURE_2D);
+
+    glClientActiveTexture (GL_TEXTURE1);
+    glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+    glActiveTexture (GL_TEXTURE1);
+    glDisable (GL_TEXTURE_2D);
+
+    _cairo_gl_context_release (ctx);
+
+    _cairo_gl_operand_destroy (&setup.src);
+
+    *remaining_glyphs = num_glyphs - i;
+    return status;
+}
+
+void
+_cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache)
+{
+    if (cache->tex == 0)
+	return;
+
+    glDeleteTextures (1, &cache->tex);
+
+    _rtree_fini (&cache->rtree);
+}
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 5d8ff60..5ca48ad 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -40,6 +40,7 @@
 #define CAIRO_GL_PRIVATE_H
 
 #include "cairoint.h"
+#include "cairo-freelist-private.h"
 
 #include <GL/glew.h>
 
@@ -60,6 +61,31 @@ typedef struct _cairo_gl_surface {
     GLuint fb; /* GL framebuffer object wrapping our data. */
 } cairo_gl_surface_t;
 
+enum {
+    RTREE_NODE_AVAILABLE,
+    RTREE_NODE_DIVIDED,
+    RTREE_NODE_OCCUPIED,
+};
+typedef struct _rtree_node {
+    struct _rtree_node *children[4], *parent;
+    uint16_t state;
+    uint16_t locked;
+    uint16_t x, y;
+    uint16_t width, height;
+} rtree_node_t;
+
+typedef struct _rtree {
+    rtree_node_t root;
+    void (*evict) (void *node);
+    cairo_freelist_t node_freelist;
+} rtree_t;
+
+typedef struct cairo_gl_glyph_cache {
+    rtree_t rtree;
+    GLuint tex;
+    unsigned int width, height;
+} cairo_gl_glyph_cache_t;
+
 struct _cairo_gl_context {
     cairo_reference_count_t ref_count;
     cairo_status_t status;
@@ -70,12 +96,45 @@ struct _cairo_gl_context {
     GLint max_texture_size;
 
     cairo_gl_surface_t *current_target;
+    cairo_gl_glyph_cache_t glyph_cache[2];
 
     void (*make_current)(void *ctx, cairo_gl_surface_t *surface);
     void (*swap_buffers)(void *ctx, cairo_gl_surface_t *surface);
     void (*destroy) (void *ctx);
 };
 
+enum cairo_gl_composite_operand_type {
+    OPERAND_CONSTANT,
+    OPERAND_TEXTURE,
+};
+
+/* This union structure describes a potential source or mask operand to the
+ * compositing equation.
+ */
+typedef struct cairo_gl_composite_operand {
+    enum cairo_gl_composite_operand_type type;
+    union {
+	struct {
+	    GLuint tex;
+	    cairo_gl_surface_t *surface;
+	    cairo_surface_attributes_t attributes;
+	    cairo_bool_t has_alpha;
+	} texture;
+	struct {
+	    GLfloat color[4];
+	} constant;
+    } operand;
+
+    const cairo_pattern_t *pattern;
+} cairo_gl_composite_operand_t;
+
+typedef struct _cairo_gl_composite_setup {
+    cairo_gl_composite_operand_t src;
+    cairo_gl_composite_operand_t mask;
+} cairo_gl_composite_setup_t;
+
+extern const cairo_surface_backend_t _cairo_gl_surface_backend;
+
 cairo_private cairo_gl_context_t *
 _cairo_gl_context_create_in_error (cairo_status_t status);
 
@@ -88,4 +147,60 @@ _cairo_gl_surface_init (cairo_gl_context_t *ctx,
 			cairo_content_t content,
 			int width, int height);
 
+cairo_status_t
+_cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
+			      cairo_image_surface_t *src,
+			      int src_x, int src_y,
+			      int width, int height,
+			      int dst_x, int dst_y);
+
+cairo_int_status_t
+_cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
+			const cairo_pattern_t *pattern,
+			cairo_gl_surface_t *dst,
+			int src_x, int src_y,
+			int dst_x, int dst_y,
+			int width, int height);
+
+cairo_gl_context_t *
+_cairo_gl_context_acquire (cairo_gl_context_t *ctx);
+
+void
+_cairo_gl_context_release (cairo_gl_context_t *ctx);
+
+void
+_cairo_gl_set_destination (cairo_gl_surface_t *surface);
+
+int
+_cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op);
+
+void
+_cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
+			   cairo_gl_composite_setup_t *setup);
+
+void
+_cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand);
+
+cairo_status_t
+_cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
+				     GLenum *internal_format, GLenum *format,
+				     GLenum *type, cairo_bool_t *has_alpha);
+
+void
+_cairo_gl_surface_scaled_glyph_fini (cairo_scaled_glyph_t *scaled_glyph,
+				     cairo_scaled_font_t  *scaled_font);
+
+cairo_int_status_t
+_cairo_gl_surface_show_glyphs (void			*abstract_dst,
+			       cairo_operator_t		 op,
+			       const cairo_pattern_t	*source,
+			       cairo_glyph_t		*glyphs,
+			       int			 num_glyphs,
+			       cairo_scaled_font_t	*scaled_font,
+			       cairo_clip_t		*clip,
+			       int			*remaining_glyphs);
+
+void
+_cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
+
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 92c670d..7055bc7 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -59,38 +59,6 @@ int_as_float (uint32_t val)
     return fi.f;
 }
 
-enum cairo_gl_composite_operand_type {
-    OPERAND_CONSTANT,
-    OPERAND_TEXTURE,
-};
-
-/* This union structure describes a potential source or mask operand to the
- * compositing equation.
- */
-typedef struct cairo_gl_composite_operand {
-    enum cairo_gl_composite_operand_type type;
-    union {
-	struct {
-	    GLuint tex;
-	    cairo_gl_surface_t *surface;
-	    cairo_surface_attributes_t attributes;
-	    cairo_bool_t has_alpha;
-	} texture;
-	struct {
-	    GLfloat color[4];
-	} constant;
-    } operand;
-
-    const cairo_pattern_t *pattern;
-} cairo_gl_composite_operand_t;
-
-typedef struct _cairo_gl_composite_setup {
-    cairo_gl_composite_operand_t src;
-    cairo_gl_composite_operand_t mask;
-} cairo_gl_composite_setup_t;
-
-static const cairo_surface_backend_t _cairo_gl_surface_backend;
-
 static const cairo_gl_context_t _nil_context = {
     CAIRO_REFERENCE_COUNT_INVALID,
     CAIRO_STATUS_NO_MEMORY
@@ -118,6 +86,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     CAIRO_REFERENCE_COUNT_INIT (&ctx->ref_count, 1);
     CAIRO_MUTEX_INIT (ctx->mutex);
 
+    memset (ctx->glyph_cache, 0, sizeof (ctx->glyph_cache));
+
     if (glewInit () != GLEW_OK)
 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
 
@@ -173,6 +143,8 @@ slim_hidden_def (cairo_gl_context_reference);
 void
 cairo_gl_context_destroy (cairo_gl_context_t *context)
 {
+    int n;
+
     if (context == NULL ||
 	CAIRO_REFERENCE_COUNT_IS_INVALID (&context->ref_count))
     {
@@ -185,20 +157,23 @@ cairo_gl_context_destroy (cairo_gl_context_t *context)
 
     glDeleteTextures (1, &context->dummy_tex);
 
+    for (n = 0; n < ARRAY_LENGTH (context->glyph_cache); n++)
+	_cairo_gl_glyph_cache_fini (&context->glyph_cache[n]);
+
     context->destroy (context);
 
     free (context);
 }
 slim_hidden_def (cairo_gl_context_destroy);
 
-static cairo_gl_context_t *
+cairo_gl_context_t *
 _cairo_gl_context_acquire (cairo_gl_context_t *ctx)
 {
     CAIRO_MUTEX_LOCK (ctx->mutex);
     return ctx;
 }
 
-static cairo_status_t
+cairo_status_t
 _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
 				     GLenum *internal_format, GLenum *format,
 				     GLenum *type, cairo_bool_t *has_alpha)
@@ -321,13 +296,13 @@ _cairo_gl_get_image_format_and_type (pixman_format_code_t pixman_format,
     }
 }
 
-static void
+void
 _cairo_gl_context_release (cairo_gl_context_t *ctx)
 {
     CAIRO_MUTEX_UNLOCK (ctx->mutex);
 }
 
-static void
+void
 _cairo_gl_set_destination (cairo_gl_surface_t *surface)
 {
     cairo_gl_context_t *ctx = surface->ctx;
@@ -360,7 +335,7 @@ _cairo_gl_set_destination (cairo_gl_surface_t *surface)
     glLoadIdentity ();
 }
 
-static int
+int
 _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
 {
     struct {
@@ -401,6 +376,7 @@ _cairo_gl_set_operator (cairo_gl_surface_t *dst, cairo_operator_t op)
 	    src_factor = GL_ONE;
     }
 
+    glEnable (GL_BLEND);
     glBlendFunc (src_factor, dst_factor);
 
     return CAIRO_STATUS_SUCCESS;
@@ -626,7 +602,7 @@ _cairo_gl_surface_create_similar (void		 *abstract_surface,
     return cairo_gl_surface_create (surface->ctx, content, width, height);
 }
 
-static cairo_status_t
+cairo_status_t
 _cairo_gl_surface_draw_image (cairo_gl_surface_t *dst,
 			      cairo_image_surface_t *src,
 			      int src_x, int src_y,
@@ -1131,7 +1107,7 @@ _cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_int_status_t
+cairo_int_status_t
 _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		       const cairo_pattern_t *pattern,
 		       cairo_gl_surface_t *dst,
@@ -1164,7 +1140,7 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
     }
 }
 
-static void
+void
 _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
 {
     switch (operand->type) {
@@ -1218,7 +1194,7 @@ _cairo_gl_set_tex_combine_constant_color (cairo_gl_context_t *ctx, int tex_unit,
     }
 }
 
-static void
+void
 _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 			   cairo_gl_composite_setup_t *setup)
 {
@@ -1323,8 +1299,6 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 	return status;
     }
 
-    glEnable (GL_BLEND);
-
     _cairo_gl_set_src_operand (ctx, &setup);
 
     if (mask != NULL) {
@@ -1558,7 +1532,6 @@ _cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
 	vertices[i * 8 + 7] = rects[i].y + rects[i].height;
     }
 
-    glEnable (GL_BLEND);
     glVertexPointer (2, GL_FLOAT, sizeof (GLfloat)*2, vertices);
     glEnableClientState (GL_VERTEX_ARRAY);
     glColorPointer (4, GL_FLOAT, sizeof (GLfloat)*4, colors);
@@ -1952,8 +1925,6 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
 	return _cairo_span_renderer_create_in_error (status);
     }
 
-    glEnable (GL_BLEND);
-
     _cairo_gl_set_src_operand (dst->ctx, &renderer->setup);
 
     /* Set up the mask to source from the incoming vertex color. */
@@ -2027,7 +1998,7 @@ _cairo_gl_surface_paint (void *abstract_surface,
     return CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
-static const cairo_surface_backend_t _cairo_gl_surface_backend = {
+const cairo_surface_backend_t _cairo_gl_surface_backend = {
     CAIRO_SURFACE_TYPE_GL,
     _cairo_gl_surface_create_similar,
     _cairo_gl_surface_finish,
@@ -2052,12 +2023,12 @@ static const cairo_surface_backend_t _cairo_gl_surface_backend = {
     NULL, /* flush */
     NULL, /* mark_dirty_rectangle */
     NULL, /* scaled_font_fini */
-    NULL, /* scaled_glyph_fini */
+    _cairo_gl_surface_scaled_glyph_fini,
     _cairo_gl_surface_paint,
     NULL, /* mask */
     NULL, /* stroke */
     NULL, /* fill */
-    NULL, /* show_glyphs */
+    _cairo_gl_surface_show_glyphs, /* show_glyphs */
     NULL  /* snapshot */
 };
 


More information about the cairo-commit mailing list