[cairo-commit] 17 commits - src/cairo-gl-glyphs.c src/cairo-gl-private.h src/cairo-gl-shaders.c src/cairo-gl-surface.c

Eric Anholt anholt at kemper.freedesktop.org
Thu Feb 4 07:35:57 PST 2010


 src/cairo-gl-glyphs.c  |   66 ++-
 src/cairo-gl-private.h |  110 +++++
 src/cairo-gl-shaders.c |  925 +++++++++++++++++++++++++++++++++++++++++++++----
 src/cairo-gl-surface.c |  667 +++++++++++++++++++++++++++--------
 4 files changed, 1530 insertions(+), 238 deletions(-)

New commits:
commit e426cdd56973ff1114dac5e8b1130180d43631b0
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 16:03:52 2010 -0800

    [gl] Implement linear gradients acceleration.
    
    This is significantly cribbed from Zach Laine's work, but reworked so
    that gradients can be plugged in as either source or mask operands for
    any of the paths.
    
    This cuts the runtime of firefox-talos-svg in half on my GM45, at the
    expense of gradient-alpha.  surface-pattern-operator also now fails
    due to small rasterization differences.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 69485fa..00948d6 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -613,6 +613,7 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     glClientActiveTexture (GL_TEXTURE0);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE0);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     glClientActiveTexture (GL_TEXTURE1);
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 052ab2f..b054477 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -91,6 +91,7 @@ typedef enum cairo_gl_shader_source {
     CAIRO_GL_SHADER_SOURCE_CONSTANT,
     CAIRO_GL_SHADER_SOURCE_TEXTURE,
     CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA,
+    CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT,
     CAIRO_GL_SHADER_SOURCE_COUNT,
 } cairo_gl_shader_source_t;
 
@@ -98,6 +99,7 @@ typedef enum cairo_gl_shader_mask {
     CAIRO_GL_SHADER_MASK_CONSTANT,
     CAIRO_GL_SHADER_MASK_TEXTURE,
     CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA,
+    CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT,
     CAIRO_GL_SHADER_MASK_NONE,
     CAIRO_GL_SHADER_MASK_SPANS,
     CAIRO_GL_SHADER_MASK_COUNT,
@@ -137,6 +139,7 @@ typedef struct _cairo_gl_context {
 enum cairo_gl_composite_operand_type {
     OPERAND_CONSTANT,
     OPERAND_TEXTURE,
+    OPERAND_LINEAR_GRADIENT,
 };
 
 /* This union structure describes a potential source or mask operand to the
@@ -155,6 +158,14 @@ typedef struct cairo_gl_composite_operand {
 	struct {
 	    GLfloat color[4];
 	} constant;
+	struct {
+	    GLuint tex;
+	    cairo_matrix_t m;
+	    float segment_x;
+	    float segment_y;
+	    float first_stop_offset;
+	    float last_stop_offset;
+	} linear;
     } operand;
 
     const cairo_pattern_t *pattern;
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index b98d10e..9efd8f6 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -724,6 +724,20 @@ static const char *fs_source_texture_alpha =
     "{\n"
     "	return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n"
     "}\n";
+static const char *fs_source_linear_gradient =
+    "uniform sampler1D source_sampler;\n"
+    "uniform mat4 source_matrix;\n"
+    "uniform vec2 source_segment;\n"
+    "uniform float source_first_offset;\n"
+    "uniform float source_last_offset;\n"
+    "\n"
+    "vec4 get_source()\n"
+    "{\n"
+    "    vec2 pos = (source_matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    float t = dot (pos, source_segment) / dot (source_segment, source_segment);\n"
+    "    t = (t - source_first_offset) / (source_last_offset - source_first_offset);\n"
+    "    return texture1D (source_sampler, t);\n"
+    "}\n";
 static const char *fs_mask_constant =
     "uniform vec4 constant_mask;\n"
     "vec4 get_mask()\n"
@@ -744,6 +758,20 @@ static const char *fs_mask_texture_alpha =
     "{\n"
     "	return vec4(0, 0, 0, texture2D(mask_sampler, mask_texcoords).a);\n"
     "}\n";
+static const char *fs_mask_linear_gradient =
+    "uniform sampler1D mask_sampler;\n"
+    "uniform mat4 mask_matrix;\n"
+    "uniform vec2 mask_segment;\n"
+    "uniform float mask_first_offset;\n"
+    "uniform float mask_last_offset;\n"
+    "\n"
+    "vec4 get_mask()\n"
+    "{\n"
+    "    vec2 pos = (mask_matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    float t = dot (pos, mask_segment) / dot (mask_segment, mask_segment);\n"
+    "    t = (t - mask_first_offset) / (mask_last_offset - mask_first_offset);\n"
+    "    return texture1D (mask_sampler, t);\n"
+    "}\n";
 static const char *fs_mask_none =
     "vec4 get_mask()\n"
     "{\n"
@@ -806,11 +834,13 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 	fs_source_constant,
 	fs_source_texture,
 	fs_source_texture_alpha,
+	fs_source_linear_gradient,
     };
     const char *mask_sources[CAIRO_GL_SHADER_MASK_COUNT] = {
 	fs_mask_constant,
 	fs_mask_texture,
 	fs_mask_texture_alpha,
+	fs_mask_linear_gradient,
 	fs_mask_none,
 	fs_mask_spans,
     };
@@ -842,17 +872,20 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 			       strlen(in_source) +
 			       1);
 
-    if (source == CAIRO_GL_SHADER_SOURCE_CONSTANT) {
+    if (source == CAIRO_GL_SHADER_SOURCE_CONSTANT ||
+	source == CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT) {
 	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
 	    vs_source = vs_spans_no_coords;
-	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT ||
+		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT)
 	    vs_source = vs_no_coords;
 	else
 	    vs_source = vs_mask_coords;
     } else {
 	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
 	    vs_source = vs_spans_source_coords;
-	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT ||
+		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT)
 	    vs_source = vs_source_coords;
 	else
 	    vs_source = vs_source_mask_coords;
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index cffd095..a0634a9 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -987,12 +987,12 @@ lerp_and_set_color (GLubyte *color,
 }
 
 static void
-_cairo_gl_create_gradient_texture (cairo_gl_context_t *ctx,
+_cairo_gl_create_gradient_texture (const cairo_gl_context_t *ctx,
 				   cairo_gl_surface_t *surface,
 				   cairo_gradient_pattern_t *pattern,
 				   GLuint *tex,
-				   double *first_offset,
-				   double *last_offset)
+				   float *first_offset,
+				   float *last_offset)
 {
     const int tex_width = ctx->max_texture_size;
     int n_stops = pattern->n_stops;
@@ -1061,8 +1061,7 @@ _cairo_gl_create_gradient_texture (cairo_gl_context_t *ctx,
 
     glGenTextures (1, tex);
     glBindTexture (GL_TEXTURE_1D, *tex);
-    glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width,
-                  extend == CAIRO_EXTEND_NONE ? 1 : 0,
+    glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width, 0,
                   GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, stops == 0 ? data : 0);
 
     glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@@ -1169,8 +1168,11 @@ _cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand,
 
 static cairo_status_t
 _cairo_gl_gradient_operand_init(cairo_gl_composite_operand_t *operand,
-				cairo_gradient_pattern_t *gradient)
+				cairo_gl_surface_t *dst)
 {
+    const cairo_gl_context_t *ctx = (cairo_gl_context_t *) dst->base.device;
+    cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *)operand->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.
@@ -1202,6 +1204,48 @@ _cairo_gl_gradient_operand_init(cairo_gl_composite_operand_t *operand,
 	}
     }
 
+    if (!ctx->using_glsl)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+	cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+        double x0, y0, x1, y1;
+
+	x0 = _cairo_fixed_to_double (linear->p1.x);
+	x1 = _cairo_fixed_to_double (linear->p2.x);
+	y0 = _cairo_fixed_to_double (linear->p1.y);
+	y1 = _cairo_fixed_to_double (linear->p2.y);
+
+        if ((unsigned int)ctx->max_texture_size / 2 <= gradient->n_stops) {
+            return CAIRO_INT_STATUS_UNSUPPORTED;
+        }
+
+        _cairo_gl_create_gradient_texture (ctx,
+					   dst,
+					   gradient,
+					   &operand->operand.linear.tex,
+					   &operand->operand.linear.first_stop_offset,
+					   &operand->operand.linear.last_stop_offset);
+
+	/* Translation matrix from the destination fragment coordinates
+	 * (pixels from lower left = 0,0) to the coordinates in the
+	 */
+	cairo_matrix_init_translate (&operand->operand.linear.m, -x0, -y0);
+	cairo_matrix_multiply (&operand->operand.linear.m,
+			       &operand->pattern->matrix,
+			       &operand->operand.linear.m);
+	cairo_matrix_translate (&operand->operand.linear.m, 0, dst->height);
+	cairo_matrix_scale (&operand->operand.linear.m, 1.0, -1.0);
+
+	operand->operand.linear.segment_x = x1 - x0;
+	operand->operand.linear.segment_y = y1 - y0;
+
+	operand->type = OPERAND_LINEAR_GRADIENT;
+	operand->source = CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT;
+	operand->mask = CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT;
+        return CAIRO_STATUS_SUCCESS;
+    }
+
     return CAIRO_INT_STATUS_UNSUPPORTED;
 }
 
@@ -1222,8 +1266,7 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		                             &((cairo_solid_pattern_t *) pattern)->color);
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
-	status = _cairo_gl_gradient_operand_init (operand,
-						  (cairo_gradient_pattern_t *) pattern);
+	status = _cairo_gl_gradient_operand_init (operand, dst);
 	if (!_cairo_status_is_error (status))
 	    return status;
 
@@ -1246,6 +1289,9 @@ _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
     switch (operand->type) {
     case OPERAND_CONSTANT:
 	break;
+    case OPERAND_LINEAR_GRADIENT:
+	glDeleteTextures (1, &operand->operand.linear.tex);
+	break;
     case OPERAND_TEXTURE:
 	if (operand->operand.texture.surface != NULL) {
 	    cairo_gl_surface_t *surface = operand->operand.texture.surface;
@@ -1318,11 +1364,10 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 {
     cairo_surface_attributes_t *src_attributes;
     GLfloat constant_color[4] = {0.0, 0.0, 0.0, 0.0};
+    cairo_status_t status;
 
     src_attributes = &setup->src.operand.texture.attributes;
 
-    (void)_cairo_gl_create_gradient_texture;
-
     switch (setup->src.type) {
     case OPERAND_CONSTANT:
 	_cairo_gl_set_tex_combine_constant_color (ctx, setup, 0,
@@ -1351,8 +1396,35 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
 	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
 	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-	    break;
 	}
+	break;
+
+    case OPERAND_LINEAR_GRADIENT:
+	glActiveTexture (GL_TEXTURE0);
+	glBindTexture (GL_TEXTURE_1D, setup->src.operand.linear.tex);
+	glEnable (GL_TEXTURE_1D);
+
+	status = bind_matrix_to_shader (setup->shader->program,
+					"source_matrix",
+					&setup->src.operand.linear.m);
+	assert (!_cairo_status_is_error (status));
+
+	status = bind_vec2_to_shader (setup->shader->program,
+				      "source_segment",
+				      setup->src.operand.linear.segment_x,
+				      setup->src.operand.linear.segment_y);
+	assert (!_cairo_status_is_error (status));
+
+	status = bind_float_to_shader (setup->shader->program,
+				       "source_first_offset",
+				       setup->src.operand.linear.first_stop_offset);
+	assert (!_cairo_status_is_error (status));
+	status = bind_float_to_shader (setup->shader->program,
+				       "source_last_offset",
+				       setup->src.operand.linear.last_stop_offset);
+	assert (!_cairo_status_is_error (status));
+
+	break;
     }
 }
 
@@ -1393,9 +1465,42 @@ _cairo_gl_set_src_alpha_operand (cairo_gl_context_t *ctx,
 	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 	}
 	break;
+    case OPERAND_LINEAR_GRADIENT:
+	assert(0);
     }
 }
 
+static void
+_cairo_gl_set_linear_gradient_mask_operand (cairo_gl_composite_setup_t *setup)
+{
+    cairo_status_t status;
+
+    assert(setup->shader);
+
+    glActiveTexture (GL_TEXTURE1);
+    glBindTexture (GL_TEXTURE_1D, setup->mask.operand.linear.tex);
+    glEnable (GL_TEXTURE_1D);
+
+    status = bind_matrix_to_shader (setup->shader->program,
+			   "mask_matrix", &setup->mask.operand.linear.m);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_vec2_to_shader (setup->shader->program,
+			 "mask_segment",
+			 setup->mask.operand.linear.segment_x,
+			 setup->mask.operand.linear.segment_y);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_float_to_shader (setup->shader->program,
+			  "mask_first_offset",
+			  setup->mask.operand.linear.first_stop_offset);
+    assert (!_cairo_status_is_error (status));
+    status = bind_float_to_shader (setup->shader->program,
+			  "mask_last_offset",
+			  setup->mask.operand.linear.last_stop_offset);
+    assert (!_cairo_status_is_error (status));
+}
+
 /* This is like _cairo_gl_set_src_alpha_operand, for component alpha setup
  * of the mask part of IN to produce a "source alpha" value.
  */
@@ -1464,6 +1569,10 @@ _cairo_gl_set_component_alpha_mask_operand (cairo_gl_context_t *ctx,
 	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
 	}
 	break;
+
+    case OPERAND_LINEAR_GRADIENT:
+	_cairo_gl_set_linear_gradient_mask_operand (setup);
+	break;
     }
 }
 
@@ -1702,11 +1811,13 @@ _cairo_gl_surface_composite_component_alpha (cairo_operator_t op,
     glClientActiveTexture (GL_TEXTURE0);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE0);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     glClientActiveTexture (GL_TEXTURE1);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE1);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     while ((err = glGetError ()))
@@ -1847,6 +1958,9 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 		glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
 	    }
 	    break;
+	case OPERAND_LINEAR_GRADIENT:
+	    _cairo_gl_set_linear_gradient_mask_operand (&setup);
+	    break;
 	}
     }
 
@@ -1941,11 +2055,13 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
     glClientActiveTexture (GL_TEXTURE0);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE0);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     glClientActiveTexture (GL_TEXTURE1);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE1);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     while ((err = glGetError ()))
@@ -2465,6 +2581,7 @@ _cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
     glClientActiveTexture (GL_TEXTURE0);
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE0);
+    glDisable (GL_TEXTURE_1D);
     glDisable (GL_TEXTURE_2D);
 
     if (!renderer->setup.shader) {
commit 05b18fc09af0e0887c652bd482d68f00bec6d4d7
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Feb 4 00:12:58 2010 -0800

    [gl] Fix create_gradient_texture to premultiply alpha.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 5340c41..cffd095 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1025,6 +1025,9 @@ _cairo_gl_create_gradient_texture (cairo_gl_context_t *ctx,
 
             cairo_pattern_get_color_stop_rgba (&pattern->base, i,
                                                &offset, &r,  &g, &b, &a);
+	    r = r * a;
+	    g = g * a;
+	    b = b * a;
             stop_index = (stops[i].offset - *first_offset) / delta_offsets * tex_width;
             if (stop_index == tex_width)
                 stop_index = tex_width - 1;
commit 31e706454b7f0fa510047f582250a7a2db24dd31
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 15:48:40 2010 -0800

    [gl] Pull the gradient operand setup out to its own function.
    
    Hopefully this clarifies what's going on, including the fallthrough to
    texture setup on failure.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index f7882fd..5340c41 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1164,6 +1164,44 @@ _cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_gl_gradient_operand_init(cairo_gl_composite_operand_t *operand,
+				cairo_gradient_pattern_t *gradient)
+{
+    /* 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 (gradient->n_stops < 2) {
+	if (gradient->n_stops) {
+	    return _cairo_gl_solid_operand_init (operand,
+						 &gradient->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 < gradient->n_stops; i++) {
+	    if (! _cairo_color_equal (&gradient->stops[0].color,
+				      &gradient->stops[i].color))
+		{
+		    break;
+		}
+	}
+	if (i == gradient->n_stops) {
+	    return _cairo_gl_solid_operand_init (operand,
+						 &gradient->stops->color);
+	}
+    }
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
 cairo_int_status_t
 _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		       const cairo_pattern_t *pattern,
@@ -1173,6 +1211,7 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *operand,
 		       int width, int height)
 {
     operand->pattern = pattern;
+    cairo_status_t status;
 
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
@@ -1180,40 +1219,10 @@ _cairo_gl_operand_init (cairo_gl_composite_operand_t *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);
-		}
-	    }
-	}
+	status = _cairo_gl_gradient_operand_init (operand,
+						  (cairo_gradient_pattern_t *) pattern);
+	if (!_cairo_status_is_error (status))
+	    return status;
 
 	/* fall through */
 
commit 745f263c1c2d263f7c5e78964bf7181f650301cc
Author: T. Zachary Laine <whatwasthataddress at gmail.com>
Date:   Wed Feb 3 15:38:02 2010 -0800

    [gl] Add function for creating a 1D texture to use for gradient lookups.
    
    Rather than have fragment shaders try to walk a variable-length set of
    stops and try to compute the interpolation between the nearest stops
    per fragment, we make a little texture representing the gradient along
    a line and have the shaders compute texture coordinates along the line
    and sample.  This should be a good tradeoff between CPU work and GPU
    work.
    
    [anholt: I pulled this set of code out of Zach Laine's tree to use for
    gradient acceleration, and wanted to make sure attribution was
    appropriate.  I applied only minor tweaking]

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 92a0926..052ab2f 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -114,6 +114,7 @@ typedef struct _cairo_gl_context {
     cairo_device_t base;
 
     GLuint dummy_tex;
+    GLuint texture_load_pbo;
     GLint max_framebuffer_size;
     GLint max_texture_size;
     cairo_bool_t using_glsl;
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index b613103..f7882fd 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -137,6 +137,9 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0,
 		  GL_RGBA, GL_UNSIGNED_BYTE, NULL);
 
+    /* PBO for any sort of texture upload */
+    glGenBuffersARB (1, &ctx->texture_load_pbo);
+
     ctx->max_framebuffer_size = 0;
     glGetIntegerv (GL_MAX_RENDERBUFFER_SIZE, &ctx->max_framebuffer_size);
     ctx->max_texture_size = 0;
@@ -969,6 +972,118 @@ _cairo_gl_get_traps_pattern (cairo_gl_surface_t *dst,
     return CAIRO_STATUS_SUCCESS;
 }
 
+
+static inline void
+lerp_and_set_color (GLubyte *color,
+                    double t0, double t1, double t,
+                    double r0, double g0, double b0, double a0,
+                    double r1, double g1, double b1, double a1)
+{
+    double alpha = (t - t0) / (t1 - t0);
+    color[0] = ((1.0 - alpha) * b0 + alpha * b1) * 255.0;
+    color[1] = ((1.0 - alpha) * g0 + alpha * g1) * 255.0;
+    color[2] = ((1.0 - alpha) * r0 + alpha * r1) * 255.0;
+    color[3] = ((1.0 - alpha) * a0 + alpha * a1) * 255.0;
+}
+
+static void
+_cairo_gl_create_gradient_texture (cairo_gl_context_t *ctx,
+				   cairo_gl_surface_t *surface,
+				   cairo_gradient_pattern_t *pattern,
+				   GLuint *tex,
+				   double *first_offset,
+				   double *last_offset)
+{
+    const int tex_width = ctx->max_texture_size;
+    int n_stops = pattern->n_stops;
+    cairo_gradient_stop_t *stops = pattern->stops;
+    GLubyte *data = 0;
+    GLubyte data_stack[4] = { 0, 0, 0, 0 };
+    cairo_extend_t extend = pattern->base.extend;
+
+    if (stops == 0) {
+        data = data_stack;
+        *first_offset = 0.0;
+        *last_offset = 1.0;
+    } else {
+        int i, j = 0, stop_index;
+        double offset, r, g, b, a;
+        double prev_r = 0.0, prev_g = 0.0, prev_b = 0.0, prev_a = 0.0;
+        double delta_offsets;
+        GLuint size = tex_width * sizeof (GLubyte) * 4;
+
+        glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, ctx->texture_load_pbo);
+	glBufferDataARB (GL_PIXEL_UNPACK_BUFFER_ARB, size, 0, GL_STREAM_DRAW);
+        data = glMapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY);
+
+        *first_offset = stops[0].offset;
+        *last_offset = stops[n_stops - 1].offset;
+        delta_offsets = *last_offset - *first_offset;
+
+        for (i = 0; i < n_stops; ++i) {
+            GLubyte *color = 0;
+
+            cairo_pattern_get_color_stop_rgba (&pattern->base, i,
+                                               &offset, &r,  &g, &b, &a);
+            stop_index = (stops[i].offset - *first_offset) / delta_offsets * tex_width;
+            if (stop_index == tex_width)
+                stop_index = tex_width - 1;
+            while (j < stop_index) {
+                color = data + j * 4;
+                lerp_and_set_color (color,
+                                    (stops[i - 1].offset - *first_offset) / delta_offsets,
+                                    (stops[i].offset - *first_offset) / delta_offsets,
+                                    (j + 0.5) / tex_width,
+                                    prev_r, prev_g, prev_b, prev_a,
+                                    r, g, b, a);
+                ++j;
+            }
+
+            /* This is the exact texel for this stop; just set it. */
+            color = data + j * 4;
+            color[0] = b * 255.0;
+            color[1] = g * 255.0;
+            color[2] = r * 255.0;
+            color[3] = a * 255.0;
+            ++j;
+
+            prev_r = r;
+            prev_g = g;
+            prev_b = b;
+            prev_a = a;
+        }
+
+        glUnmapBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB);
+    }
+
+    glGenTextures (1, tex);
+    glBindTexture (GL_TEXTURE_1D, *tex);
+    glTexImage1D (GL_TEXTURE_1D, 0, GL_RGBA8, tex_width,
+                  extend == CAIRO_EXTEND_NONE ? 1 : 0,
+                  GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, stops == 0 ? data : 0);
+
+    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+    if (stops != 0)
+        glBindBufferARB (GL_PIXEL_UNPACK_BUFFER_ARB, 0);
+
+    switch (extend) {
+    case CAIRO_EXTEND_NONE:
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+	break;
+    case CAIRO_EXTEND_PAD:
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+	break;
+    case CAIRO_EXTEND_REPEAT:
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+	break;
+    case CAIRO_EXTEND_REFLECT:
+	glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
+	break;
+    }
+}
+
 /**
  * Like cairo_pattern_acquire_surface(), but returns a matrix that transforms
  * from dest to src coords.
@@ -1194,6 +1309,8 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 
     src_attributes = &setup->src.operand.texture.attributes;
 
+    (void)_cairo_gl_create_gradient_texture;
+
     switch (setup->src.type) {
     case OPERAND_CONSTANT:
 	_cairo_gl_set_tex_combine_constant_color (ctx, setup, 0,
commit 88c8304e9072e5a46890a0eb32fcdbc857eb1a12
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 17:06:45 2010 -0800

    [gl] Sanity check the sampler uniform binding.

diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 9d4d203..b98d10e 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -874,12 +874,14 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 
     _cairo_gl_use_program (program);
     if (source != CAIRO_GL_SHADER_SOURCE_CONSTANT) {
-	bind_texture_to_shader (program->program, "source_sampler", 0);
+	status = bind_texture_to_shader (program->program, "source_sampler", 0);
+	assert (!_cairo_status_is_error (status));
     }
     if (mask != CAIRO_GL_SHADER_MASK_CONSTANT &&
 	mask != CAIRO_GL_SHADER_MASK_SPANS &&
 	mask != CAIRO_GL_SHADER_MASK_NONE) {
-	bind_texture_to_shader (program->program, "mask_sampler", 1);
+	status = bind_texture_to_shader (program->program, "mask_sampler", 1);
+	assert (!_cairo_status_is_error (status));
     }
 
     _cairo_gl_use_program (NULL);
commit 42597170f2303475bb0507f4c6e948eead28c7bd
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 16:55:45 2010 -0800

    [gl] Convert the spans code to using GLSL when available.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index e2f08e9..92a0926 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -99,6 +99,7 @@ typedef enum cairo_gl_shader_mask {
     CAIRO_GL_SHADER_MASK_TEXTURE,
     CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA,
     CAIRO_GL_SHADER_MASK_NONE,
+    CAIRO_GL_SHADER_MASK_SPANS,
     CAIRO_GL_SHADER_MASK_COUNT,
 } cairo_gl_shader_mask_t;
 
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 5d4ee4a..9d4d203 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -688,6 +688,22 @@ static const char *vs_source_mask_coords =
     "	source_texcoords = gl_MultiTexCoord0.xy;\n"
     "	mask_texcoords = gl_MultiTexCoord1.xy;\n"
     "}\n";
+static const char *vs_spans_no_coords =
+    "varying float coverage;\n"
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "   coverage = gl_Color.a;\n"
+    "}\n";
+static const char *vs_spans_source_coords =
+    "varying vec2 source_texcoords;\n"
+    "varying float coverage;\n"
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "	source_texcoords = gl_MultiTexCoord0.xy;\n"
+    "   coverage = gl_Color.a;\n"
+    "}\n";
 static const char *fs_source_constant =
     "uniform vec4 constant_source;\n"
     "vec4 get_source()\n"
@@ -733,6 +749,12 @@ static const char *fs_mask_none =
     "{\n"
     "	return vec4(0, 0, 0, 1);\n"
     "}\n";
+static const char *fs_mask_spans =
+    "varying float coverage;\n"
+    "vec4 get_mask()\n"
+    "{\n"
+    "	return vec4(0, 0, 0, coverage);\n"
+    "}\n";
 static const char *fs_in_normal =
     "void main()\n"
     "{\n"
@@ -790,6 +812,7 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 	fs_mask_texture,
 	fs_mask_texture_alpha,
 	fs_mask_none,
+	fs_mask_spans,
     };
     const char *in_sources[CAIRO_GL_SHADER_IN_COUNT] = {
 	fs_in_normal,
@@ -820,12 +843,16 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 			       1);
 
     if (source == CAIRO_GL_SHADER_SOURCE_CONSTANT) {
-	if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
+	    vs_source = vs_spans_no_coords;
+	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
 	    vs_source = vs_no_coords;
 	else
 	    vs_source = vs_mask_coords;
     } else {
-	if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
+	    vs_source = vs_spans_source_coords;
+	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
 	    vs_source = vs_source_coords;
 	else
 	    vs_source = vs_source_mask_coords;
@@ -849,7 +876,9 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
     if (source != CAIRO_GL_SHADER_SOURCE_CONSTANT) {
 	bind_texture_to_shader (program->program, "source_sampler", 0);
     }
-    if (mask != CAIRO_GL_SHADER_MASK_CONSTANT) {
+    if (mask != CAIRO_GL_SHADER_MASK_CONSTANT &&
+	mask != CAIRO_GL_SHADER_MASK_SPANS &&
+	mask != CAIRO_GL_SHADER_MASK_NONE) {
 	bind_texture_to_shader (program->program, "mask_sampler", 1);
     }
 
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 4e11d7d..b613103 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -2338,10 +2338,13 @@ _cairo_gl_surface_span_renderer_finish (void *abstract_renderer)
     glActiveTexture (GL_TEXTURE0);
     glDisable (GL_TEXTURE_2D);
 
-    glActiveTexture (GL_TEXTURE1);
-    glDisable (GL_TEXTURE_2D);
+    if (!renderer->setup.shader) {
+	glActiveTexture (GL_TEXTURE1);
+	glDisable (GL_TEXTURE_2D);
+    }
 
     glDisable (GL_BLEND);
+    _cairo_gl_use_program (NULL);
 
     return CAIRO_STATUS_SUCCESS;
 }
@@ -2417,29 +2420,38 @@ _cairo_gl_surface_create_span_renderer (cairo_operator_t	 op,
     }
     _cairo_gl_set_destination (dst);
 
+    status = _cairo_gl_get_program (renderer->ctx,
+				    renderer->setup.src.source,
+				    CAIRO_GL_SHADER_MASK_SPANS,
+				    CAIRO_GL_SHADER_IN_NORMAL,
+				    &renderer->setup.shader);
+
     src_attributes = &renderer->setup.src.operand.texture.attributes;
 
+    _cairo_gl_use_program (renderer->setup.shader);
     _cairo_gl_set_operator (dst, op, FALSE);
     _cairo_gl_set_src_operand (renderer->ctx, &renderer->setup);
 
-    /* Set up the mask to source from the incoming vertex color. */
-    glActiveTexture (GL_TEXTURE1);
-    /* Have to have a dummy texture bound in order to use the combiner unit. */
-    glBindTexture (GL_TEXTURE_2D, renderer->ctx->dummy_tex);
-    glEnable (GL_TEXTURE_2D);
-    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_PRIMARY_COLOR);
-    glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR);
-    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
-    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+    if (!renderer->setup.shader) {
+	/* Set up the mask to source from the incoming vertex color. */
+	glActiveTexture (GL_TEXTURE1);
+	/* Have to have a dummy texture bound in order to use the combiner unit. */
+	glBindTexture (GL_TEXTURE_2D, renderer->ctx->dummy_tex);
+	glEnable (GL_TEXTURE_2D);
+	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_PRIMARY_COLOR);
+	glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PRIMARY_COLOR);
+	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_ALPHA);
+	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+    }
 
     while ((err = glGetError ()))
 	fprintf (stderr, "GL error 0x%08x\n", (int) err);
commit 8d16fb10ea9940c0c9f311ff08c51d572c068c17
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 15:31:57 2010 -0800

    [gl] Test for required EXT_bgra and explain what it's used for.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 650c3c7..4e11d7d 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -100,7 +100,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
 
     if (! GLEW_EXT_framebuffer_object ||
 	! GLEW_ARB_texture_env_combine ||
-	! GLEW_ARB_texture_non_power_of_two)
+	! GLEW_ARB_texture_non_power_of_two ||
+	! GLEW_EXT_bgra)
     {
 	fprintf (stderr,
 		 "Required GL extensions not available:\n");
@@ -110,6 +111,12 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
 	    fprintf (stderr, "    GL_ARB_texture_env_combine\n");
 	if (! GLEW_ARB_texture_non_power_of_two)
 	    fprintf (stderr, "    GL_ARB_texture_non_power_of_two\n");
+	/* EXT_bgra is used in two places:
+	 * - draw_image to upload common pixman formats without hand-swizzling.
+	 * - get_image to download common pixman formats without hand-swizzling.
+	 */
+	if (! GLEW_EXT_bgra)
+	    fprintf (stderr, "    GL_EXT_bgra\n");
 
 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
     }
commit b2d8c9cbf2faec570450d163da140c54e64bbdc8
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 15:25:47 2010 -0800

    [gl] Use the shader source choice from operand setup instead of DIY.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index bf05f8d..69485fa 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -244,7 +244,6 @@ typedef struct _cairo_gl_glyphs_setup
     cairo_gl_surface_t *dst;
     cairo_operator_t op;
     cairo_bool_t component_alpha;
-    cairo_gl_shader_source_t source;
     cairo_gl_shader_in_t in;
 } cairo_gl_glyphs_setup_t;
 
@@ -303,7 +302,7 @@ _cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
 	cairo_status_t status;
 
 	status = _cairo_gl_get_program (ctx,
-					setup->source,
+					setup->composite->src.source,
 					CAIRO_GL_SHADER_MASK_TEXTURE,
 					in,
 					&setup->composite->shader);
@@ -506,18 +505,6 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     setup.op = op;
     setup.in = CAIRO_GL_SHADER_IN_COUNT; /* unset */
 
-    if (setup.composite->src.type == OPERAND_CONSTANT) {
-	setup.source = CAIRO_GL_SHADER_SOURCE_CONSTANT;
-    } else {
-	if (setup.composite->src.operand.texture.surface->base.content !=
-	    CAIRO_CONTENT_ALPHA)
- 	{
-	    setup.source = CAIRO_GL_SHADER_SOURCE_TEXTURE;
-	} else {
-	    setup.source = CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA;
-	}
-    }
-
     glGenBuffersARB (1, &vbo);
     glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
 
commit 0fceac4d52d4b574b9c1f911b83d15dc7e01691d
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 12:24:41 2010 -0800

    [gl] Move the component alpha composite to using GLSL when available.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index c5abbbd..650c3c7 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1125,9 +1125,29 @@ _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
 }
 
 static void
-_cairo_gl_set_tex_combine_constant_color (cairo_gl_context_t *ctx, int tex_unit,
+_cairo_gl_set_tex_combine_constant_color (cairo_gl_context_t *ctx,
+					  cairo_gl_composite_setup_t *setup,
+					  int tex_unit,
 					  GLfloat *color)
 {
+    if (setup->shader) {
+	const char *uniform_name;
+
+	if (tex_unit == 0)
+	    uniform_name = "constant_source";
+	else
+	    uniform_name = "constant_mask";
+
+	bind_vec4_to_shader(setup->shader->program,
+			    uniform_name,
+			    color[0],
+			    color[1],
+			    color[2],
+			    color[3]);
+	return;
+    }
+
+    /* Fall back to fixed function */
     glActiveTexture (GL_TEXTURE0 + tex_unit);
     /* Have to have a dummy texture bound in order to use the combiner unit. */
     glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
@@ -1169,17 +1189,8 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 
     switch (setup->src.type) {
     case OPERAND_CONSTANT:
-	if (setup->shader) {
-	    bind_vec4_to_shader(setup->shader->program,
-				"constant_source",
-				setup->src.operand.constant.color[0],
-				setup->src.operand.constant.color[1],
-				setup->src.operand.constant.color[2],
-				setup->src.operand.constant.color[3]);
-	} else {
-	    _cairo_gl_set_tex_combine_constant_color (ctx, 0,
-						      setup->src.operand.constant.color);
-	}
+	_cairo_gl_set_tex_combine_constant_color (ctx, setup, 0,
+						  setup->src.operand.constant.color);
 	break;
     case OPERAND_TEXTURE:
 	_cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex,
@@ -1228,24 +1239,23 @@ _cairo_gl_set_src_alpha_operand (cairo_gl_context_t *ctx,
 	constant_color[1] = setup->src.operand.constant.color[3];
 	constant_color[2] = setup->src.operand.constant.color[3];
 	constant_color[3] = setup->src.operand.constant.color[3];
-       _cairo_gl_set_tex_combine_constant_color (ctx, 0, constant_color);
+	_cairo_gl_set_tex_combine_constant_color (ctx, setup, 0,
+						  constant_color);
 	break;
     case OPERAND_TEXTURE:
-	constant_color[0] = 0.0;
-	constant_color[1] = 0.0;
-	constant_color[2] = 0.0;
-	constant_color[3] = 1.0;
 	_cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex,
 				       src_attributes);
-	/* Set up the combiner to just set color to the sampled texture. */
-	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-	glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
-	glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+	if (!setup->shader) {
+	    /* Set up the combiner to just set color to the sampled texture. */
+	    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	}
 	break;
     }
 }
@@ -1262,49 +1272,61 @@ _cairo_gl_set_component_alpha_mask_operand (cairo_gl_context_t *ctx,
 
     mask_attributes = &setup->mask.operand.texture.attributes;
 
-    glActiveTexture (GL_TEXTURE1);
-    glEnable (GL_TEXTURE_2D);
-    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);
+    if (!setup->shader) {
+	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_SRC1_RGB, GL_PREVIOUS);
-    glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS);
-    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
-    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+	glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
+	glTexEnvi (GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS);
+	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
+	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA);
+    }
 
     switch (setup->mask.type) {
     case OPERAND_CONSTANT:
 	/* Have to have a dummy texture bound in order to use the combiner unit. */
-	glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
-
-	glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
-		    setup->mask.operand.constant.color);
+	if (setup->shader) {
+	    bind_vec4_to_shader(setup->shader->program,
+				"constant_mask",
+				setup->src.operand.constant.color[0],
+				setup->src.operand.constant.color[1],
+				setup->src.operand.constant.color[2],
+				setup->src.operand.constant.color[3]);
+	} else {
+	    glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
+	    glActiveTexture (GL_TEXTURE1);
+	    glEnable (GL_TEXTURE_2D);
 
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	    glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR,
+			setup->mask.operand.constant.color);
 
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_CONSTANT);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	}
 	break;
     case OPERAND_TEXTURE:
 	_cairo_gl_set_texture_surface (1, setup->mask.operand.texture.tex,
 				       mask_attributes);
-	/* Set up the constant color we use to set color to 0 if needed. */
-	glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
+	if (!setup->shader) {
+	    /* Set up the constant color we use to set color to 0 if needed. */
+	    glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
 
-	/* Force the mask color to 0 if the surface should be alpha-only.
-	 * We may have a teximage with color bits if the implementation doesn't
-	 * support GL_ALPHA FBOs.
-	 */
-	if (setup->mask.operand.texture.surface->base.content !=
-	    CAIRO_CONTENT_ALPHA)
-	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE1);
-	else
-	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE1);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	    /* Force the mask color to 0 if the surface should be
+	     * alpha-only.  We may have a teximage with color bits if
+	     * the implementation doesn't support GL_ALPHA FBOs.
+	     */
+	    if (setup->mask.operand.texture.surface->base.content !=
+		CAIRO_CONTENT_ALPHA)
+		glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE1);
+	    else
+		glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE1);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	}
 	break;
     }
 }
@@ -1387,6 +1409,8 @@ _cairo_gl_surface_composite_component_alpha (cairo_operator_t op,
     int num_vertices, i;
     GLenum err;
     cairo_gl_composite_setup_t setup;
+    cairo_gl_shader_program_t *ca_source_program = NULL;
+    cairo_gl_shader_program_t *ca_source_alpha_program = NULL;
 
     if (op != CAIRO_OPERATOR_OVER)
 	return UNSUPPORTED ("unsupported component alpha operator");
@@ -1418,6 +1442,27 @@ _cairo_gl_surface_composite_component_alpha (cairo_operator_t op,
 	return status;
     }
 
+    status = _cairo_gl_get_program (ctx,
+				    setup.src.source,
+				    setup.mask.mask,
+				    CAIRO_GL_SHADER_IN_CA_SOURCE,
+				    &ca_source_program);
+    if (!_cairo_status_is_error (status)) {
+	status = _cairo_gl_get_program (ctx,
+					setup.src.source,
+					setup.mask.mask,
+					CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
+					&ca_source_alpha_program);
+	if (_cairo_status_is_error (status)) {
+	    /* We'll fall back to fixed function instead. */
+	    ca_source_program = NULL;
+	    status = CAIRO_STATUS_SUCCESS;
+	}
+    } else {
+	/* We'll fall back to fixed function instead. */
+	status = CAIRO_STATUS_SUCCESS;
+    }
+
     _cairo_gl_set_destination (dst);
 
     if (clip_region != NULL) {
@@ -1499,16 +1544,22 @@ _cairo_gl_surface_composite_component_alpha (cairo_operator_t op,
 	glEnableClientState (GL_TEXTURE_COORD_ARRAY);
     }
 
+    setup.shader = ca_source_alpha_program;
+    _cairo_gl_use_program (setup.shader);
     _cairo_gl_set_operator (dst, CAIRO_OPERATOR_DEST_OUT, TRUE);
     _cairo_gl_set_src_alpha_operand (ctx, &setup);
     _cairo_gl_set_component_alpha_mask_operand (ctx, &setup);
     glDrawArrays (GL_QUADS, 0, num_vertices);
 
+    setup.shader = ca_source_program;
+    _cairo_gl_use_program (setup.shader);
     _cairo_gl_set_operator (dst, CAIRO_OPERATOR_ADD, TRUE);
     _cairo_gl_set_src_operand (ctx, &setup);
+    _cairo_gl_set_component_alpha_mask_operand (ctx, &setup);
     glDrawArrays (GL_QUADS, 0, num_vertices);
 
     glDisable (GL_BLEND);
+    _cairo_gl_use_program (NULL);
 
     glDisableClientState (GL_VERTEX_ARRAY);
 
@@ -1635,7 +1686,7 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
     if (mask != NULL) {
 	switch (setup.mask.type) {
 	case OPERAND_CONSTANT:
-	    _cairo_gl_set_tex_combine_constant_color (ctx, 1,
+	    _cairo_gl_set_tex_combine_constant_color (ctx, &setup, 1,
 						      setup.mask.operand.constant.color);
 	    break;
 
commit 8357336ed7be91ad359d205b1f223486d13215f2
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 11:54:24 2010 -0800

    [gl] Move normal composites to using GLSL when available.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index ce82ef9..e2f08e9 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -142,6 +142,8 @@ enum cairo_gl_composite_operand_type {
  */
 typedef struct cairo_gl_composite_operand {
     enum cairo_gl_composite_operand_type type;
+    cairo_gl_shader_source_t source;
+    cairo_gl_shader_mask_t mask;
     union {
 	struct {
 	    GLuint tex;
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 28524f2..c5abbbd 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -995,7 +995,13 @@ _cairo_gl_pattern_texture_setup (cairo_gl_composite_operand_t *operand,
 
     operand->operand.texture.surface = surface;
     operand->operand.texture.tex = surface->tex;
-
+    if (surface->base.content != CAIRO_CONTENT_ALPHA) {
+	operand->source = CAIRO_GL_SHADER_SOURCE_TEXTURE;
+	operand->mask = CAIRO_GL_SHADER_MASK_TEXTURE;
+    } else {
+	operand->source = CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA;
+	operand->mask = CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA;
+    }
     /* Translate the matrix from
      * (unnormalized src -> unnormalized src) to
      * (unnormalized dst -> unnormalized src)
@@ -1027,6 +1033,8 @@ _cairo_gl_solid_operand_init (cairo_gl_composite_operand_t *operand,
 	                      const cairo_color_t *color)
 {
     operand->type = OPERAND_CONSTANT;
+    operand->source = CAIRO_GL_SHADER_SOURCE_CONSTANT;
+    operand->mask = CAIRO_GL_SHADER_MASK_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;
@@ -1599,15 +1607,29 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 	    return status;
 	}
 	mask_attributes = &setup.mask.operand.texture.attributes;
+    } else {
+	setup.mask.mask = CAIRO_GL_SHADER_MASK_NONE;
     }
 
     status = _cairo_gl_context_acquire (dst->base.device, &ctx);
     if (unlikely (status))
 	goto CLEANUP_SHADER;
 
+    status = _cairo_gl_get_program (ctx,
+				    setup.src.source,
+				    setup.mask.mask,
+				    CAIRO_GL_SHADER_IN_NORMAL,
+				    &setup.shader);
+    if (_cairo_status_is_error (status)) {
+	/* We'll fall back to fixed function instead. */
+	setup.shader = NULL;
+	status = CAIRO_STATUS_SUCCESS;
+    }
+
     _cairo_gl_set_destination (dst);
     _cairo_gl_set_operator (dst, op, FALSE);
 
+    _cairo_gl_use_program (setup.shader);
     _cairo_gl_set_src_operand (ctx, &setup);
 
     if (mask != NULL) {
@@ -1621,20 +1643,22 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 	    _cairo_gl_set_texture_surface (1, setup.mask.operand.texture.tex,
 					   mask_attributes);
 
-	    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);
-
-	    /* IN: dst.argb = src.argb * mask.aaaa */
-	    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);
+	    if (!setup.shader) {
+		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);
+
+		/* IN: dst.argb = src.argb * mask.aaaa */
+		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);
+	    }
 	    break;
 	}
     }
@@ -1722,6 +1746,7 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 
     glDrawArrays (GL_QUADS, 0, num_vertices);
 
+    _cairo_gl_use_program (NULL);
     glDisable (GL_BLEND);
 
     glDisableClientState (GL_VERTEX_ARRAY);
commit 106bc158bafa4eecfd7f79422fc19d267c20dcdb
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 15:15:02 2010 -0800

    [gl] Move the glyphs shader construction to a more general location.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 768e5c9..bf05f8d 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -136,128 +136,6 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
     return CAIRO_STATUS_SUCCESS;
 }
 
-static cairo_status_t
-_cairo_gl_glyphs_setup_shaders (cairo_gl_context_t *ctx)
-{
-    static const char *vs_source_constant =
-	"varying vec2 mask_texcoords;\n"
-	"void main()\n"
-	"{\n"
-	"	gl_Position = ftransform();\n"
-	"	mask_texcoords = gl_MultiTexCoord1.xy;\n"
-	"}\n";
-    static const char *vs_source_texture =
-	"varying vec2 source_texcoords;\n"
-	"varying vec2 mask_texcoords;\n"
-	"void main()\n"
-	"{\n"
-	"	gl_Position = ftransform();\n"
-	"	source_texcoords = gl_MultiTexCoord0.xy;\n"
-	"	mask_texcoords = gl_MultiTexCoord1.xy;\n"
-	"}\n";
-    static const char *glyphs_source_constant =
-	"uniform vec4 constant_source;\n"
-	"vec4 get_source()\n"
-	"{\n"
-	"	return constant_source;\n"
-	"}\n";
-    static const char *glyphs_source_texture =
-	"uniform sampler2D source_sampler;\n"
-	"varying vec2 source_texcoords;\n"
-	"vec4 get_source()\n"
-	"{\n"
-	"	return texture2D(source_sampler, source_texcoords);\n"
-	"}\n";
-    static const char *glyphs_source_texture_alpha =
-	"uniform sampler2D source_sampler;\n"
-	"varying vec2 source_texcoords;\n"
-	"vec4 get_source()\n"
-	"{\n"
-	"	return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n"
-	"}\n";
-    static const char *glyphs_in_normal =
-	"uniform sampler2D mask_sampler;\n"
-	"varying vec2 mask_texcoords;\n"
-	"void main()\n"
-	"{\n"
-	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
-	"	gl_FragColor = get_source() * mask.a;\n"
-	"}\n";
-    static const char *glyphs_in_component_alpha_source =
-	"uniform sampler2D mask_sampler;\n"
-	"varying vec2 mask_texcoords;\n"
-	"void main()\n"
-	"{\n"
-	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
-	"	gl_FragColor = get_source() * mask;\n"
-	"}\n";
-    static const char *glyphs_in_component_alpha_alpha =
-	"uniform sampler2D mask_sampler;\n"
-	"varying vec2 mask_texcoords;\n"
-	"void main()\n"
-	"{\n"
-	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
-	"	gl_FragColor = get_source().a * mask;\n"
-	"}\n";
-    const char *glyphs_source_sources[CAIRO_GL_GLYPHS_SOURCE_COUNT] = {
-	glyphs_source_constant,
-	glyphs_source_texture,
-	glyphs_source_texture_alpha,
-    };
-    const char *glyphs_in_sources[CAIRO_GL_GLYPHS_SHADER_COUNT] = {
-	glyphs_in_normal,
-	glyphs_in_component_alpha_source,
-	glyphs_in_component_alpha_alpha,
-    };
-    enum cairo_gl_glyphs_shader_source source;
-    enum cairo_gl_glyphs_shader in;
-
-    for (source = 0; source < CAIRO_GL_GLYPHS_SOURCE_COUNT; source++) {
-	for (in = 0; in < CAIRO_GL_GLYPHS_SHADER_COUNT; in++) {
-	    const char *source_source = glyphs_source_sources[source];
-	    const char *in_source = glyphs_in_sources[in];
-	    char *fs_source = _cairo_malloc (strlen(source_source) +
-					     strlen(in_source) +
-					     1);
-	    const char *vs_source;
-	    cairo_status_t status;
-	    cairo_gl_shader_program_t *program = &ctx->glyphs_shaders[source][in];
-
-	    if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT)
-		vs_source = vs_source_constant;
-	    else
-		vs_source = vs_source_texture;
-
-	    if (!fs_source)
-		return CAIRO_STATUS_NO_MEMORY;
-
-	    sprintf(fs_source, "%s%s", source_source, in_source);
-
-	    init_shader_program (program);
-	    status = create_shader_program (program,
-					    vs_source,
-					    fs_source);
-	    free (fs_source);
-
-	    if (_cairo_status_is_error (status))
-		return status;
-
-	    _cairo_gl_use_program (program);
-	    bind_texture_to_shader (program->program, "mask_sampler", 1);
-	    if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT) {
-		vs_source = vs_source_constant;
-	    } else {
-		bind_texture_to_shader (program->program, "source_sampler", 0);
-		vs_source = vs_source_texture;
-	    }
-	}
-    }
-
-    _cairo_gl_use_program (NULL);
-
-    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)
@@ -364,22 +242,23 @@ typedef struct _cairo_gl_glyphs_setup
     cairo_gl_composite_setup_t *composite;
     cairo_region_t *clip;
     cairo_gl_surface_t *dst;
-    cairo_gl_glyphs_shader_t shader;
     cairo_operator_t op;
     cairo_bool_t component_alpha;
+    cairo_gl_shader_source_t source;
+    cairo_gl_shader_in_t in;
 } cairo_gl_glyphs_setup_t;
 
 static void
 _cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx,
 				   cairo_gl_glyphs_setup_t *setup,
-				   cairo_gl_glyphs_shader_t shader)
+				   cairo_gl_shader_in_t in)
 {
-    if (setup->shader == shader)
+    if (setup->in == in)
 	return;
 
-    setup->shader = shader;
+    setup->in = in;
 
-    if (shader != CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA)
+    if (in != CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA)
 	_cairo_gl_set_src_operand (ctx, setup->composite);
     else
 	_cairo_gl_set_src_alpha_operand (ctx, setup->composite);
@@ -400,7 +279,7 @@ _cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx,
 
     glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE1);
     glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE1);
-    if (setup->shader == CAIRO_GL_GLYPHS_SHADER_NORMAL)
+    if (in == CAIRO_GL_SHADER_IN_NORMAL)
 	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_ALPHA);
     else
 	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
@@ -413,43 +292,30 @@ _cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx,
 }
 
 static void
-_cairo_gl_glyphs_set_shader_glsl (cairo_gl_context_t *ctx,
-				  cairo_gl_glyphs_setup_t *setup,
-				  cairo_gl_glyphs_shader_t shader)
+_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
+			     cairo_gl_glyphs_setup_t *setup,
+			     cairo_gl_shader_in_t in)
 {
-    enum cairo_gl_glyphs_shader_source glyphs_source;
-
-    if (setup->shader == shader)
+    if (setup->in == in)
 	return;
 
-    if (setup->composite->src.type == OPERAND_CONSTANT) {
-	glyphs_source = CAIRO_GL_GLYPHS_SOURCE_CONSTANT;
-    } else {
-	if (setup->composite->src.operand.texture.surface->base.content !=
-	    CAIRO_CONTENT_ALPHA)
- 	{
-	    glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE;
-	} else {
-	    glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA;
+    if (ctx->using_glsl) {
+	cairo_status_t status;
+
+	status = _cairo_gl_get_program (ctx,
+					setup->source,
+					CAIRO_GL_SHADER_MASK_TEXTURE,
+					in,
+					&setup->composite->shader);
+	if (!_cairo_status_is_error (status)) {
+	    setup->in = in;
+	    _cairo_gl_use_program (setup->composite->shader);
+	    _cairo_gl_set_src_operand (ctx, setup->composite);
+	    return;
 	}
     }
 
-    setup->composite->shader = &ctx->glyphs_shaders[glyphs_source][shader];
-    _cairo_gl_use_program (setup->composite->shader);
-    _cairo_gl_set_src_operand (ctx, setup->composite);
-
-    setup->shader = shader;
-}
-
-static void
-_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
-			     cairo_gl_glyphs_setup_t *setup,
-			     cairo_gl_glyphs_shader_t shader)
-{
-    if (ctx->using_glsl_glyphs)
-	_cairo_gl_glyphs_set_shader_glsl (ctx, setup, shader);
-    else
-	_cairo_gl_glyphs_set_shader_fixed (ctx, setup, shader);
+    _cairo_gl_glyphs_set_shader_fixed (ctx, setup, in);
 }
 
 static void
@@ -461,12 +327,12 @@ _cairo_gl_glyphs_draw (cairo_gl_context_t *ctx,
     } else {
 	_cairo_gl_set_operator (setup->dst, CAIRO_OPERATOR_DEST_OUT, TRUE);
 	_cairo_gl_glyphs_set_shader(ctx, setup,
-				    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA);
+				    CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA);
 	glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
 
 	_cairo_gl_set_operator (setup->dst, CAIRO_OPERATOR_ADD, TRUE);
 	_cairo_gl_glyphs_set_shader(ctx, setup,
-				    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE);
+				    CAIRO_GL_SHADER_IN_CA_SOURCE);
 	glDrawArrays (GL_QUADS, 0, 4 * setup->num_prims);
     }
 }
@@ -488,10 +354,10 @@ _cairo_gl_flush_glyphs (cairo_gl_context_t *ctx,
 
     if (!setup->component_alpha) {
 	_cairo_gl_set_operator (setup->dst, setup->op, FALSE);
-	_cairo_gl_glyphs_set_shader(ctx, setup, CAIRO_GL_GLYPHS_SHADER_NORMAL);
+	_cairo_gl_glyphs_set_shader(ctx, setup, CAIRO_GL_SHADER_IN_NORMAL);
     } else if (setup->op == CAIRO_OPERATOR_ADD) {
 	_cairo_gl_set_operator (setup->dst, setup->op, FALSE);
-	_cairo_gl_glyphs_set_shader(ctx, setup, CAIRO_GL_GLYPHS_SHADER_CA_SOURCE);
+	_cairo_gl_glyphs_set_shader(ctx, setup, CAIRO_GL_SHADER_IN_CA_SOURCE);
     }
 
     if (setup->clip) {
@@ -610,13 +476,6 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     if (unlikely (status))
 	return status;
 
-    if (ctx->using_glsl && !ctx->glsl_glyphs_inited) {
-	cairo_status_t status = _cairo_gl_glyphs_setup_shaders (ctx);
-	if (!_cairo_status_is_error (status))
-	    ctx->using_glsl_glyphs = TRUE;
-	ctx->glsl_glyphs_inited = TRUE;
-    }
-
     _cairo_gl_set_destination (dst);
 
     _cairo_scaled_font_freeze_cache (scaled_font);
@@ -645,7 +504,19 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     if (setup.vbo_size > 4096)
 	setup.vbo_size = 4096;
     setup.op = op;
-    setup.shader = CAIRO_GL_GLYPHS_SHADER_COUNT; /* unset */
+    setup.in = CAIRO_GL_SHADER_IN_COUNT; /* unset */
+
+    if (setup.composite->src.type == OPERAND_CONSTANT) {
+	setup.source = CAIRO_GL_SHADER_SOURCE_CONSTANT;
+    } else {
+	if (setup.composite->src.operand.texture.surface->base.content !=
+	    CAIRO_CONTENT_ALPHA)
+ 	{
+	    setup.source = CAIRO_GL_SHADER_SOURCE_TEXTURE;
+	} else {
+	    setup.source = CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA;
+	}
+    }
 
     glGenBuffersARB (1, &vbo);
     glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 14b6489..ce82ef9 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -87,19 +87,27 @@ typedef struct cairo_gl_shader_program {
     cairo_bool_t build_failure;
 } cairo_gl_shader_program_t;
 
-enum cairo_gl_glyphs_shader_source {
-    CAIRO_GL_GLYPHS_SOURCE_CONSTANT,
-    CAIRO_GL_GLYPHS_SOURCE_TEXTURE,
-    CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA,
-    CAIRO_GL_GLYPHS_SOURCE_COUNT,
-};
-
-typedef enum cairo_gl_glyphs_shader {
-    CAIRO_GL_GLYPHS_SHADER_NORMAL,
-    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE,
-    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA,
-    CAIRO_GL_GLYPHS_SHADER_COUNT,
-} cairo_gl_glyphs_shader_t;
+typedef enum cairo_gl_shader_source {
+    CAIRO_GL_SHADER_SOURCE_CONSTANT,
+    CAIRO_GL_SHADER_SOURCE_TEXTURE,
+    CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA,
+    CAIRO_GL_SHADER_SOURCE_COUNT,
+} cairo_gl_shader_source_t;
+
+typedef enum cairo_gl_shader_mask {
+    CAIRO_GL_SHADER_MASK_CONSTANT,
+    CAIRO_GL_SHADER_MASK_TEXTURE,
+    CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA,
+    CAIRO_GL_SHADER_MASK_NONE,
+    CAIRO_GL_SHADER_MASK_COUNT,
+} cairo_gl_shader_mask_t;
+
+typedef enum cairo_gl_shader_in {
+    CAIRO_GL_SHADER_IN_NORMAL,
+    CAIRO_GL_SHADER_IN_CA_SOURCE,
+    CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA,
+    CAIRO_GL_SHADER_IN_COUNT,
+} cairo_gl_shader_in_t;
 
 typedef struct _cairo_gl_context {
     cairo_device_t base;
@@ -108,13 +116,12 @@ typedef struct _cairo_gl_context {
     GLint max_framebuffer_size;
     GLint max_texture_size;
     cairo_bool_t using_glsl;
-    cairo_bool_t glsl_glyphs_inited;
-    cairo_bool_t using_glsl_glyphs;
 
     cairo_bool_t using_shaders;
     cairo_gl_shader_program_t fill_rectangles_shader;
-    cairo_gl_shader_program_t glyphs_shaders[CAIRO_GL_GLYPHS_SOURCE_COUNT]
-					[CAIRO_GL_GLYPHS_SHADER_COUNT];
+    cairo_gl_shader_program_t shaders[CAIRO_GL_SHADER_SOURCE_COUNT]
+					[CAIRO_GL_SHADER_MASK_COUNT]
+					[CAIRO_GL_SHADER_IN_COUNT];
 
     cairo_gl_surface_t *current_target;
     cairo_gl_surface_t *glyphs_temporary_mask;
@@ -309,6 +316,13 @@ bind_texture_to_shader (GLuint program, const char *name, GLuint tex_unit);
 void
 _cairo_gl_use_program (cairo_gl_shader_program_t *shader);
 
+cairo_status_t
+_cairo_gl_get_program (cairo_gl_context_t *ctx,
+		       cairo_gl_shader_source_t source,
+		       cairo_gl_shader_mask_t mask,
+		       cairo_gl_shader_in_t in,
+		       cairo_gl_shader_program_t **out_program);
+
 slim_hidden_proto (cairo_gl_surface_create);
 
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index b449b03..5d4ee4a 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -659,3 +659,203 @@ _cairo_gl_use_program (cairo_gl_shader_program_t *program)
 {
     get_impl()->use_program (program);
 }
+
+static const char *vs_no_coords =
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "}\n";
+static const char *vs_source_coords =
+    "varying vec2 source_texcoords;\n"
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "	source_texcoords = gl_MultiTexCoord0.xy;\n"
+    "}\n";
+static const char *vs_mask_coords =
+    "varying vec2 mask_texcoords;\n"
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "	mask_texcoords = gl_MultiTexCoord1.xy;\n"
+    "}\n";
+static const char *vs_source_mask_coords =
+    "varying vec2 source_texcoords;\n"
+    "varying vec2 mask_texcoords;\n"
+    "void main()\n"
+    "{\n"
+    "	gl_Position = ftransform();\n"
+    "	source_texcoords = gl_MultiTexCoord0.xy;\n"
+    "	mask_texcoords = gl_MultiTexCoord1.xy;\n"
+    "}\n";
+static const char *fs_source_constant =
+    "uniform vec4 constant_source;\n"
+    "vec4 get_source()\n"
+    "{\n"
+    "	return constant_source;\n"
+    "}\n";
+static const char *fs_source_texture =
+    "uniform sampler2D source_sampler;\n"
+    "varying vec2 source_texcoords;\n"
+    "vec4 get_source()\n"
+    "{\n"
+    "	return texture2D(source_sampler, source_texcoords);\n"
+    "}\n";
+static const char *fs_source_texture_alpha =
+    "uniform sampler2D source_sampler;\n"
+    "varying vec2 source_texcoords;\n"
+    "vec4 get_source()\n"
+    "{\n"
+    "	return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n"
+    "}\n";
+static const char *fs_mask_constant =
+    "uniform vec4 constant_mask;\n"
+    "vec4 get_mask()\n"
+    "{\n"
+    "	return constant_mask;\n"
+    "}\n";
+static const char *fs_mask_texture =
+    "uniform sampler2D mask_sampler;\n"
+    "varying vec2 mask_texcoords;\n"
+    "vec4 get_mask()\n"
+    "{\n"
+    "	return texture2D(mask_sampler, mask_texcoords);\n"
+    "}\n";
+static const char *fs_mask_texture_alpha =
+    "uniform sampler2D mask_sampler;\n"
+    "varying vec2 mask_texcoords;\n"
+    "vec4 get_mask()\n"
+    "{\n"
+    "	return vec4(0, 0, 0, texture2D(mask_sampler, mask_texcoords).a);\n"
+    "}\n";
+static const char *fs_mask_none =
+    "vec4 get_mask()\n"
+    "{\n"
+    "	return vec4(0, 0, 0, 1);\n"
+    "}\n";
+static const char *fs_in_normal =
+    "void main()\n"
+    "{\n"
+    "	gl_FragColor = get_source() * get_mask().a;\n"
+    "}\n";
+static const char *fs_in_component_alpha_source =
+    "void main()\n"
+    "{\n"
+    "	gl_FragColor = get_source() * get_mask();\n"
+    "}\n";
+static const char *fs_in_component_alpha_alpha =
+    "void main()\n"
+    "{\n"
+    "	gl_FragColor = get_source().a * get_mask();\n"
+    "}\n";
+
+/**
+ * This function reduces the GLSL program combinations we compile when
+ * there are non-functional differences.
+ */
+static cairo_gl_shader_program_t *
+_cairo_gl_select_program (cairo_gl_context_t *ctx,
+			  cairo_gl_shader_source_t source,
+			  cairo_gl_shader_mask_t mask,
+			  cairo_gl_shader_in_t in)
+{
+    if (in == CAIRO_GL_SHADER_IN_NORMAL &&
+	mask == CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA)
+    {
+	mask = CAIRO_GL_SHADER_MASK_TEXTURE;
+    }
+    if (in == CAIRO_GL_SHADER_IN_CA_SOURCE_ALPHA &&
+	source == CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA)
+    {
+	source = CAIRO_GL_SHADER_SOURCE_TEXTURE;
+    }
+
+    return &ctx->shaders[source][mask][in];
+}
+
+cairo_status_t
+_cairo_gl_get_program (cairo_gl_context_t *ctx,
+		       cairo_gl_shader_source_t source,
+		       cairo_gl_shader_mask_t mask,
+		       cairo_gl_shader_in_t in,
+		       cairo_gl_shader_program_t **out_program)
+{
+    const char *source_sources[CAIRO_GL_SHADER_SOURCE_COUNT] = {
+	fs_source_constant,
+	fs_source_texture,
+	fs_source_texture_alpha,
+    };
+    const char *mask_sources[CAIRO_GL_SHADER_MASK_COUNT] = {
+	fs_mask_constant,
+	fs_mask_texture,
+	fs_mask_texture_alpha,
+	fs_mask_none,
+    };
+    const char *in_sources[CAIRO_GL_SHADER_IN_COUNT] = {
+	fs_in_normal,
+	fs_in_component_alpha_source,
+	fs_in_component_alpha_alpha,
+    };
+    cairo_gl_shader_program_t *program;
+    const char *source_source, *mask_source, *in_source;
+    const char *vs_source;
+    char *fs_source;
+    cairo_status_t status;
+
+    program = _cairo_gl_select_program(ctx, source, mask, in);
+    if (program->program) {
+	*out_program = program;
+	return CAIRO_STATUS_SUCCESS;
+    }
+
+    if (program->build_failure)
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    source_source = source_sources[source];
+    mask_source = mask_sources[mask];
+    in_source = in_sources[in];
+    fs_source = _cairo_malloc (strlen(source_source) +
+			       strlen(mask_source) +
+			       strlen(in_source) +
+			       1);
+
+    if (source == CAIRO_GL_SHADER_SOURCE_CONSTANT) {
+	if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	    vs_source = vs_no_coords;
+	else
+	    vs_source = vs_mask_coords;
+    } else {
+	if (mask == CAIRO_GL_SHADER_MASK_CONSTANT)
+	    vs_source = vs_source_coords;
+	else
+	    vs_source = vs_source_mask_coords;
+    }
+
+    if (!fs_source)
+	return CAIRO_STATUS_NO_MEMORY;
+
+    sprintf(fs_source, "%s%s%s", source_source, mask_source, in_source);
+
+    init_shader_program (program);
+    status = create_shader_program (program,
+				    vs_source,
+				    fs_source);
+    free (fs_source);
+
+    if (_cairo_status_is_error (status))
+	return status;
+
+    _cairo_gl_use_program (program);
+    if (source != CAIRO_GL_SHADER_SOURCE_CONSTANT) {
+	bind_texture_to_shader (program->program, "source_sampler", 0);
+    }
+    if (mask != CAIRO_GL_SHADER_MASK_CONSTANT) {
+	bind_texture_to_shader (program->program, "mask_sampler", 1);
+    }
+
+    _cairo_gl_use_program (NULL);
+
+    *out_program = program;
+
+    return CAIRO_STATUS_SUCCESS;
+}
commit f475351f75a3ec079628a2eb2643ebc09f0e5cc8
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Feb 2 19:23:17 2010 -0800

    [gl] Use GLSL when available for glyph rendering.
    
    This reduces the CPU work in translating fixed function state to
    shaders, but currently is a slight cost on GM45 because we end up
    changing shaders more frequently since other parts of the pipeline are
    doing fixed function still.

diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index e3e2a99..768e5c9 100644
--- a/src/cairo-gl-glyphs.c
+++ b/src/cairo-gl-glyphs.c
@@ -1,6 +1,7 @@
 /* Cairo - a vector graphics library with display and print output
  *
  * Copyright © 2009 Chris Wilson
+ * Copyright © 2010 Intel Corporation
  *
  * This library is free software; you can redistribute it and/or
  * modify it either under the terms of the GNU Lesser General Public
@@ -42,13 +43,6 @@
 #define GLYPH_CACHE_MIN_SIZE 4
 #define GLYPH_CACHE_MAX_SIZE 128
 
-typedef enum _cairo_gl_glyphs_shader {
-    CAIRO_GL_GLYPHS_SHADER_UNSET,
-    CAIRO_GL_GLYPHS_SHADER_NORMAL,
-    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA,
-    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE,
-} cairo_gl_glyphs_shader_t;
-
 typedef struct _cairo_gl_glyph_private {
     cairo_rtree_node_t node;
     cairo_gl_glyph_cache_t *cache;
@@ -142,6 +136,128 @@ _cairo_gl_glyph_cache_add_glyph (cairo_gl_glyph_cache_t *cache,
     return CAIRO_STATUS_SUCCESS;
 }
 
+static cairo_status_t
+_cairo_gl_glyphs_setup_shaders (cairo_gl_context_t *ctx)
+{
+    static const char *vs_source_constant =
+	"varying vec2 mask_texcoords;\n"
+	"void main()\n"
+	"{\n"
+	"	gl_Position = ftransform();\n"
+	"	mask_texcoords = gl_MultiTexCoord1.xy;\n"
+	"}\n";
+    static const char *vs_source_texture =
+	"varying vec2 source_texcoords;\n"
+	"varying vec2 mask_texcoords;\n"
+	"void main()\n"
+	"{\n"
+	"	gl_Position = ftransform();\n"
+	"	source_texcoords = gl_MultiTexCoord0.xy;\n"
+	"	mask_texcoords = gl_MultiTexCoord1.xy;\n"
+	"}\n";
+    static const char *glyphs_source_constant =
+	"uniform vec4 constant_source;\n"
+	"vec4 get_source()\n"
+	"{\n"
+	"	return constant_source;\n"
+	"}\n";
+    static const char *glyphs_source_texture =
+	"uniform sampler2D source_sampler;\n"
+	"varying vec2 source_texcoords;\n"
+	"vec4 get_source()\n"
+	"{\n"
+	"	return texture2D(source_sampler, source_texcoords);\n"
+	"}\n";
+    static const char *glyphs_source_texture_alpha =
+	"uniform sampler2D source_sampler;\n"
+	"varying vec2 source_texcoords;\n"
+	"vec4 get_source()\n"
+	"{\n"
+	"	return vec4(0, 0, 0, texture2D(source_sampler, source_texcoords).a);\n"
+	"}\n";
+    static const char *glyphs_in_normal =
+	"uniform sampler2D mask_sampler;\n"
+	"varying vec2 mask_texcoords;\n"
+	"void main()\n"
+	"{\n"
+	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
+	"	gl_FragColor = get_source() * mask.a;\n"
+	"}\n";
+    static const char *glyphs_in_component_alpha_source =
+	"uniform sampler2D mask_sampler;\n"
+	"varying vec2 mask_texcoords;\n"
+	"void main()\n"
+	"{\n"
+	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
+	"	gl_FragColor = get_source() * mask;\n"
+	"}\n";
+    static const char *glyphs_in_component_alpha_alpha =
+	"uniform sampler2D mask_sampler;\n"
+	"varying vec2 mask_texcoords;\n"
+	"void main()\n"
+	"{\n"
+	"	vec4 mask = texture2D(mask_sampler, mask_texcoords);\n"
+	"	gl_FragColor = get_source().a * mask;\n"
+	"}\n";
+    const char *glyphs_source_sources[CAIRO_GL_GLYPHS_SOURCE_COUNT] = {
+	glyphs_source_constant,
+	glyphs_source_texture,
+	glyphs_source_texture_alpha,
+    };
+    const char *glyphs_in_sources[CAIRO_GL_GLYPHS_SHADER_COUNT] = {
+	glyphs_in_normal,
+	glyphs_in_component_alpha_source,
+	glyphs_in_component_alpha_alpha,
+    };
+    enum cairo_gl_glyphs_shader_source source;
+    enum cairo_gl_glyphs_shader in;
+
+    for (source = 0; source < CAIRO_GL_GLYPHS_SOURCE_COUNT; source++) {
+	for (in = 0; in < CAIRO_GL_GLYPHS_SHADER_COUNT; in++) {
+	    const char *source_source = glyphs_source_sources[source];
+	    const char *in_source = glyphs_in_sources[in];
+	    char *fs_source = _cairo_malloc (strlen(source_source) +
+					     strlen(in_source) +
+					     1);
+	    const char *vs_source;
+	    cairo_status_t status;
+	    cairo_gl_shader_program_t *program = &ctx->glyphs_shaders[source][in];
+
+	    if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT)
+		vs_source = vs_source_constant;
+	    else
+		vs_source = vs_source_texture;
+
+	    if (!fs_source)
+		return CAIRO_STATUS_NO_MEMORY;
+
+	    sprintf(fs_source, "%s%s", source_source, in_source);
+
+	    init_shader_program (program);
+	    status = create_shader_program (program,
+					    vs_source,
+					    fs_source);
+	    free (fs_source);
+
+	    if (_cairo_status_is_error (status))
+		return status;
+
+	    _cairo_gl_use_program (program);
+	    bind_texture_to_shader (program->program, "mask_sampler", 1);
+	    if (source == CAIRO_GL_GLYPHS_SOURCE_CONSTANT) {
+		vs_source = vs_source_constant;
+	    } else {
+		bind_texture_to_shader (program->program, "source_sampler", 0);
+		vs_source = vs_source_texture;
+	    }
+	}
+    }
+
+    _cairo_gl_use_program (NULL);
+
+    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)
@@ -254,9 +370,9 @@ typedef struct _cairo_gl_glyphs_setup
 } cairo_gl_glyphs_setup_t;
 
 static void
-_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
-			     cairo_gl_glyphs_setup_t *setup,
-			     cairo_gl_glyphs_shader_t shader)
+_cairo_gl_glyphs_set_shader_fixed (cairo_gl_context_t *ctx,
+				   cairo_gl_glyphs_setup_t *setup,
+				   cairo_gl_glyphs_shader_t shader)
 {
     if (setup->shader == shader)
 	return;
@@ -297,6 +413,46 @@ _cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
 }
 
 static void
+_cairo_gl_glyphs_set_shader_glsl (cairo_gl_context_t *ctx,
+				  cairo_gl_glyphs_setup_t *setup,
+				  cairo_gl_glyphs_shader_t shader)
+{
+    enum cairo_gl_glyphs_shader_source glyphs_source;
+
+    if (setup->shader == shader)
+	return;
+
+    if (setup->composite->src.type == OPERAND_CONSTANT) {
+	glyphs_source = CAIRO_GL_GLYPHS_SOURCE_CONSTANT;
+    } else {
+	if (setup->composite->src.operand.texture.surface->base.content !=
+	    CAIRO_CONTENT_ALPHA)
+ 	{
+	    glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE;
+	} else {
+	    glyphs_source = CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA;
+	}
+    }
+
+    setup->composite->shader = &ctx->glyphs_shaders[glyphs_source][shader];
+    _cairo_gl_use_program (setup->composite->shader);
+    _cairo_gl_set_src_operand (ctx, setup->composite);
+
+    setup->shader = shader;
+}
+
+static void
+_cairo_gl_glyphs_set_shader (cairo_gl_context_t *ctx,
+			     cairo_gl_glyphs_setup_t *setup,
+			     cairo_gl_glyphs_shader_t shader)
+{
+    if (ctx->using_glsl_glyphs)
+	_cairo_gl_glyphs_set_shader_glsl (ctx, setup, shader);
+    else
+	_cairo_gl_glyphs_set_shader_fixed (ctx, setup, shader);
+}
+
+static void
 _cairo_gl_glyphs_draw (cairo_gl_context_t *ctx,
 		       cairo_gl_glyphs_setup_t *setup)
 {
@@ -440,6 +596,8 @@ _render_glyphs (cairo_gl_surface_t	*dst,
 
     *has_component_alpha = FALSE;
 
+    memset (&composite_setup, 0, sizeof(composite_setup));
+
     status = _cairo_gl_operand_init (&composite_setup.src, source, dst,
 				     glyph_extents->x, glyph_extents->y,
 				     dst_x, dst_y,
@@ -452,6 +610,13 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     if (unlikely (status))
 	return status;
 
+    if (ctx->using_glsl && !ctx->glsl_glyphs_inited) {
+	cairo_status_t status = _cairo_gl_glyphs_setup_shaders (ctx);
+	if (!_cairo_status_is_error (status))
+	    ctx->using_glsl_glyphs = TRUE;
+	ctx->glsl_glyphs_inited = TRUE;
+    }
+
     _cairo_gl_set_destination (dst);
 
     _cairo_scaled_font_freeze_cache (scaled_font);
@@ -480,7 +645,7 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     if (setup.vbo_size > 4096)
 	setup.vbo_size = 4096;
     setup.op = op;
-    setup.shader = CAIRO_GL_GLYPHS_SHADER_UNSET;
+    setup.shader = CAIRO_GL_GLYPHS_SHADER_COUNT; /* unset */
 
     glGenBuffersARB (1, &vbo);
     glBindBufferARB (GL_ARRAY_BUFFER_ARB, vbo);
@@ -596,6 +761,7 @@ _render_glyphs (cairo_gl_surface_t	*dst,
     glDisableClientState (GL_TEXTURE_COORD_ARRAY);
     glActiveTexture (GL_TEXTURE1);
     glDisable (GL_TEXTURE_2D);
+    _cairo_gl_use_program (NULL);
 
     if (vbo != 0) {
 	glBindBufferARB (GL_ARRAY_BUFFER_ARB, 0);
@@ -878,3 +1044,4 @@ _cairo_gl_glyph_cache_init (cairo_gl_glyph_cache_t *cache)
 		       sizeof (cairo_gl_glyph_private_t),
 		       NULL);
 }
+
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 927595a..14b6489 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -87,6 +87,20 @@ typedef struct cairo_gl_shader_program {
     cairo_bool_t build_failure;
 } cairo_gl_shader_program_t;
 
+enum cairo_gl_glyphs_shader_source {
+    CAIRO_GL_GLYPHS_SOURCE_CONSTANT,
+    CAIRO_GL_GLYPHS_SOURCE_TEXTURE,
+    CAIRO_GL_GLYPHS_SOURCE_TEXTURE_ALPHA,
+    CAIRO_GL_GLYPHS_SOURCE_COUNT,
+};
+
+typedef enum cairo_gl_glyphs_shader {
+    CAIRO_GL_GLYPHS_SHADER_NORMAL,
+    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE,
+    CAIRO_GL_GLYPHS_SHADER_CA_SOURCE_ALPHA,
+    CAIRO_GL_GLYPHS_SHADER_COUNT,
+} cairo_gl_glyphs_shader_t;
+
 typedef struct _cairo_gl_context {
     cairo_device_t base;
 
@@ -94,9 +108,13 @@ typedef struct _cairo_gl_context {
     GLint max_framebuffer_size;
     GLint max_texture_size;
     cairo_bool_t using_glsl;
+    cairo_bool_t glsl_glyphs_inited;
+    cairo_bool_t using_glsl_glyphs;
 
     cairo_bool_t using_shaders;
     cairo_gl_shader_program_t fill_rectangles_shader;
+    cairo_gl_shader_program_t glyphs_shaders[CAIRO_GL_GLYPHS_SOURCE_COUNT]
+					[CAIRO_GL_GLYPHS_SHADER_COUNT];
 
     cairo_gl_surface_t *current_target;
     cairo_gl_surface_t *glyphs_temporary_mask;
@@ -134,6 +152,7 @@ typedef struct cairo_gl_composite_operand {
 typedef struct _cairo_gl_composite_setup {
     cairo_gl_composite_operand_t src;
     cairo_gl_composite_operand_t mask;
+    cairo_gl_shader_program_t *shader;
 } cairo_gl_composite_setup_t;
 
 cairo_private extern const cairo_surface_backend_t _cairo_gl_surface_backend;
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 7e60263..b449b03 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -317,7 +317,10 @@ bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
 static void
 use_program_arb (cairo_gl_shader_program_t *program)
 {
-    glUseProgramObjectARB (program->program);
+    if (program)
+	glUseProgramObjectARB (program->program);
+    else
+	glUseProgramObjectARB (0);
 }
 
 /* OpenGL Core 2.0 API. */
@@ -488,7 +491,10 @@ bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_un
 static void
 use_program_core_2_0 (cairo_gl_shader_program_t *program)
 {
-    glUseProgram (program->program);
+    if (program)
+	glUseProgram (program->program);
+    else
+	glUseProgram (0);
 }
 
 static const shader_impl_t shader_impl_core_2_0 = {
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 41a9bbc..28524f2 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -117,7 +117,8 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
     if (GLEW_VERSION_2_0 ||
 	(GLEW_ARB_fragment_shader &&
 	 GLEW_ARB_vertex_shader &&
-	 GLEW_ARB_shader_objects)) {
+	 GLEW_ARB_shader_objects))
+    {
 	ctx->using_glsl = TRUE;
     }
 
@@ -1160,32 +1161,43 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 
     switch (setup->src.type) {
     case OPERAND_CONSTANT:
-	_cairo_gl_set_tex_combine_constant_color (ctx, 0,
-						  setup->src.operand.constant.color);
+	if (setup->shader) {
+	    bind_vec4_to_shader(setup->shader->program,
+				"constant_source",
+				setup->src.operand.constant.color[0],
+				setup->src.operand.constant.color[1],
+				setup->src.operand.constant.color[2],
+				setup->src.operand.constant.color[3]);
+	} else {
+	    _cairo_gl_set_tex_combine_constant_color (ctx, 0,
+						      setup->src.operand.constant.color);
+	}
 	break;
     case OPERAND_TEXTURE:
 	_cairo_gl_set_texture_surface (0, setup->src.operand.texture.tex,
 				       src_attributes);
-	/* Set up the constant color we use to set color to 0 if needed. */
-	glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
-	/* Set up the combiner to just set color to the sampled texture. */
-	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
-	glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
-	glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
+	if (!setup->shader) {
+	    /* Set up the constant color we use to set color to 0 if needed. */
+	    glTexEnvfv (GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, constant_color);
+	    /* Set up the combiner to just set color to the sampled texture. */
+	    glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE);
 
-	/* Force the src color to 0 if the surface should be alpha-only.
-	 * We may have a teximage with color bits if the implementation doesn't
-	 * support GL_ALPHA FBOs.
-	 */
-	if (setup->src.operand.texture.surface->base.content !=
-	    CAIRO_CONTENT_ALPHA)
-	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
-	else
-	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
-	glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
-	glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
-	break;
+	    /* Force the src color to 0 if the surface should be
+	     * alpha-only.  We may have a teximage with color bits if
+	     * the implementation doesn't support GL_ALPHA FBOs.
+	     */
+	    if (setup->src.operand.texture.surface->base.content !=
+		CAIRO_CONTENT_ALPHA)
+		glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE0);
+	    else
+		glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_RGB, GL_CONSTANT);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE0);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
+	    glTexEnvi (GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA);
+	    break;
+	}
     }
 }
 
commit 672973caa0c3877b3b9f1c5f5966449ce0d7e239
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Feb 2 11:17:43 2010 -0800

    [gl] Convert fill_rectangles to the new GLSL core/arb wrapper.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index e4fe000..927595a 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -91,12 +91,13 @@ typedef struct _cairo_gl_context {
     cairo_device_t base;
 
     GLuint dummy_tex;
-    GLint fill_rectangles_shader;
-    GLint fill_rectangles_color_uniform;
     GLint max_framebuffer_size;
     GLint max_texture_size;
     cairo_bool_t using_glsl;
 
+    cairo_bool_t using_shaders;
+    cairo_gl_shader_program_t fill_rectangles_shader;
+
     cairo_gl_surface_t *current_target;
     cairo_gl_surface_t *glyphs_temporary_mask;
     cairo_gl_glyph_cache_t glyph_cache[2];
@@ -236,10 +237,6 @@ _cairo_gl_surface_show_glyphs (void			*abstract_dst,
 cairo_private void
 _cairo_gl_glyph_cache_fini (cairo_gl_glyph_cache_t *cache);
 
-cairo_private cairo_status_t
-_cairo_gl_load_glsl (GLint *shader,
-		     const char *vs_source, const char *fs_source);
-
 static inline int
 _cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
 {
@@ -290,6 +287,9 @@ bind_matrix_to_shader (GLuint program, const char *name, cairo_matrix_t* m);
 cairo_status_t
 bind_texture_to_shader (GLuint program, const char *name, GLuint tex_unit);
 
+void
+_cairo_gl_use_program (cairo_gl_shader_program_t *shader);
+
 slim_hidden_proto (cairo_gl_surface_create);
 
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index cff0518..7e60263 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -35,89 +35,6 @@
 #include "cairo-gl-private.h"
 #include "cairo-error-private.h"
 
-static GLint
-_cairo_gl_compile_glsl(GLenum type, GLint *shader_out, const char *source)
-{
-    GLint ok;
-    GLint shader;
-
-    shader = glCreateShaderObjectARB (type);
-    glShaderSourceARB (shader, 1, (const GLchar **)&source, NULL);
-    glCompileShaderARB (shader);
-    glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &ok);
-    if (!ok) {
-	GLchar *info;
-	GLint size;
-
-	glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB,
-				   &size);
-	info = malloc (size);
-
-	if (info)
-	    glGetInfoLogARB (shader, size, NULL, info);
-	fprintf (stderr, "Failed to compile %s: %s\n",
-		 type == GL_FRAGMENT_SHADER ? "FS" : "VS",
-		 info);
-	fprintf (stderr, "Shader source:\n%s", source);
-	fprintf (stderr, "GLSL compile failure\n");
-
-	glDeleteObjectARB (shader);
-
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    *shader_out = shader;
-
-    return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_status_t
-_cairo_gl_load_glsl (GLint *shader_out,
-		     const char *vs_source, const char *fs_source)
-{
-    GLint ok;
-    GLint shader, vs, fs;
-    cairo_status_t status;
-
-    shader = glCreateProgramObjectARB ();
-
-    status = _cairo_gl_compile_glsl (GL_VERTEX_SHADER_ARB, &vs, vs_source);
-    if (_cairo_status_is_error (status))
-	goto fail;
-    status = _cairo_gl_compile_glsl (GL_FRAGMENT_SHADER_ARB, &fs, fs_source);
-    if (_cairo_status_is_error (status))
-	goto fail;
-
-    glAttachObjectARB (shader, vs);
-    glAttachObjectARB (shader, fs);
-    glLinkProgram (shader);
-    glGetObjectParameterivARB (shader, GL_OBJECT_LINK_STATUS_ARB, &ok);
-    if (!ok) {
-	GLchar *info;
-	GLint size;
-
-	glGetObjectParameterivARB (shader, GL_OBJECT_INFO_LOG_LENGTH_ARB,
-				   &size);
-	info = malloc (size);
-
-	if (info)
-	    glGetInfoLogARB (shader, size, NULL, info);
-	fprintf (stderr, "Failed to link: %s\n", info);
-	free (info);
-	status = CAIRO_INT_STATUS_UNSUPPORTED;
-
-	return CAIRO_INT_STATUS_UNSUPPORTED;
-    }
-
-    *shader_out = shader;
-
-    return CAIRO_STATUS_SUCCESS;
-
-fail:
-    glDeleteObjectARB (shader);
-    return status;
-}
-
 typedef struct _shader_impl {
     cairo_status_t
     (*compile_shader) (GLuint *shader, GLenum type, const char *text);
@@ -157,6 +74,9 @@ typedef struct _shader_impl {
 
     cairo_status_t
     (*bind_texture_to_shader) (GLuint program, const char *name, GLuint tex_unit);
+
+    void
+    (*use_program) (cairo_gl_shader_program_t *program);
 } shader_impl_t;
 
 static const shader_impl_t*
@@ -394,6 +314,12 @@ bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+use_program_arb (cairo_gl_shader_program_t *program)
+{
+    glUseProgramObjectARB (program->program);
+}
+
 /* OpenGL Core 2.0 API. */
 static cairo_status_t
 compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text)
@@ -559,6 +485,12 @@ bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_un
     return CAIRO_STATUS_SUCCESS;
 }
 
+static void
+use_program_core_2_0 (cairo_gl_shader_program_t *program)
+{
+    glUseProgram (program->program);
+}
+
 static const shader_impl_t shader_impl_core_2_0 = {
     compile_shader_core_2_0,
     link_shader_core_2_0,
@@ -571,6 +503,7 @@ static const shader_impl_t shader_impl_core_2_0 = {
     bind_vec4_to_shader_core_2_0,
     bind_matrix_to_shader_core_2_0,
     bind_texture_to_shader_core_2_0,
+    use_program_core_2_0,
 };
 
 static const shader_impl_t shader_impl_arb = {
@@ -585,6 +518,7 @@ static const shader_impl_t shader_impl_arb = {
     bind_vec4_to_shader_arb,
     bind_matrix_to_shader_arb,
     bind_texture_to_shader_arb,
+    use_program_arb,
 };
 
 static const shader_impl_t*
@@ -713,3 +647,9 @@ bind_texture_to_shader (GLuint program, const char *name, GLuint tex_unit)
 {
     return get_impl()->bind_texture_to_shader(program, name, tex_unit);
 }
+
+void
+_cairo_gl_use_program (cairo_gl_shader_program_t *program)
+{
+    get_impl()->use_program (program);
+}
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index 9d6e416..41a9bbc 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -114,12 +114,15 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
     }
 
-    if (GLEW_ARB_fragment_shader &&
-	GLEW_ARB_vertex_shader &&
-	GLEW_ARB_shader_objects) {
+    if (GLEW_VERSION_2_0 ||
+	(GLEW_ARB_fragment_shader &&
+	 GLEW_ARB_vertex_shader &&
+	 GLEW_ARB_shader_objects)) {
 	ctx->using_glsl = TRUE;
     }
 
+    init_shader_program (&ctx->fill_rectangles_shader);
+
     /* Set up the dummy texture for tex_env_combine with constant color. */
     glGenTextures (1, &ctx->dummy_tex);
     glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
@@ -1889,7 +1892,6 @@ _cairo_gl_surface_fill_rectangles_glsl (void                  *abstract_surface,
 #define N_STACK_RECTS 4
     cairo_gl_surface_t *surface = abstract_surface;
     GLfloat vertices_stack[N_STACK_RECTS*4*2];
-    GLfloat gl_color[4];
     cairo_gl_context_t *ctx;
     int i;
     GLfloat *vertices;
@@ -1913,16 +1915,12 @@ _cairo_gl_surface_fill_rectangles_glsl (void                  *abstract_surface,
     if (unlikely (status))
 	return status;
 
-    if (ctx->fill_rectangles_shader == 0) {
-	status = _cairo_gl_load_glsl (&ctx->fill_rectangles_shader,
-				      fill_vs_source, fill_fs_source);
-	if (_cairo_status_is_error (status)) {
-	    _cairo_gl_context_release (ctx);
-	    return status;
-	}
-
-	ctx->fill_rectangles_color_uniform =
-	    glGetUniformLocationARB (ctx->fill_rectangles_shader, "color");
+    status = create_shader_program (&ctx->fill_rectangles_shader,
+				    fill_vs_source,
+				    fill_fs_source);
+    if (unlikely (status)) {
+	_cairo_gl_context_release (ctx);
+	return status;
     }
 
     if (num_rects > N_STACK_RECTS) {
@@ -1936,16 +1934,17 @@ _cairo_gl_surface_fill_rectangles_glsl (void                  *abstract_surface,
 	vertices = vertices_stack;
     }
 
-    glUseProgramObjectARB (ctx->fill_rectangles_shader);
+    _cairo_gl_use_program (&ctx->fill_rectangles_shader);
 
     _cairo_gl_set_destination (surface);
     _cairo_gl_set_operator (surface, op, FALSE);
 
-    gl_color[0] = color->red * color->alpha;
-    gl_color[1] = color->green * color->alpha;
-    gl_color[2] = color->blue * color->alpha;
-    gl_color[3] = color->alpha;
-    glUniform4fvARB (ctx->fill_rectangles_color_uniform, 1, gl_color);
+    bind_vec4_to_shader (ctx->fill_rectangles_shader.program,
+			 "color",
+			 color->red * color->alpha,
+			 color->green * color->alpha,
+			 color->blue * color->alpha,
+			 color->alpha);
 
     for (i = 0; i < num_rects; i++) {
 	vertices[i * 8 + 0] = rects[i].x;
commit 416273db34a715a6fa180f52ddfd11b82b6e8b6c
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Feb 2 10:37:42 2010 -0800

    [gl] Cut out the abstraction of GL_VERTEX_SHADER vs GL_VERTEX_SHADER_ARB.
    
    They're the same number, so just use the core 2.0 name.

diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 59f12ba..cff0518 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -157,12 +157,6 @@ typedef struct _shader_impl {
 
     cairo_status_t
     (*bind_texture_to_shader) (GLuint program, const char *name, GLuint tex_unit);
-
-    GLenum
-    (*vertex_enumerator) (void);
-
-    GLenum
-    (*fragment_enumerator) (void);
 } shader_impl_t;
 
 static const shader_impl_t*
@@ -400,18 +394,6 @@ bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
     return CAIRO_STATUS_SUCCESS;
 }
 
-static GLenum
-vertex_enumerator_arb (void)
-{
-    return GL_VERTEX_SHADER_ARB;
-}
-
-static GLenum
-fragment_enumerator_arb (void)
-{
-    return GL_FRAGMENT_SHADER_ARB;
-}
-
 /* OpenGL Core 2.0 API. */
 static cairo_status_t
 compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text)
@@ -577,18 +559,6 @@ bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_un
     return CAIRO_STATUS_SUCCESS;
 }
 
-static GLenum
-vertex_enumerator_core_2_0 (void)
-{
-    return GL_VERTEX_SHADER;
-}
-
-static GLenum
-fragment_enumerator_core_2_0 (void)
-{
-    return GL_FRAGMENT_SHADER;
-}
-
 static const shader_impl_t shader_impl_core_2_0 = {
     compile_shader_core_2_0,
     link_shader_core_2_0,
@@ -601,8 +571,6 @@ static const shader_impl_t shader_impl_core_2_0 = {
     bind_vec4_to_shader_core_2_0,
     bind_matrix_to_shader_core_2_0,
     bind_texture_to_shader_core_2_0,
-    vertex_enumerator_core_2_0,
-    fragment_enumerator_core_2_0
 };
 
 static const shader_impl_t shader_impl_arb = {
@@ -617,8 +585,6 @@ static const shader_impl_t shader_impl_arb = {
     bind_vec4_to_shader_arb,
     bind_matrix_to_shader_arb,
     bind_texture_to_shader_arb,
-    vertex_enumerator_arb,
-    fragment_enumerator_arb
 };
 
 static const shader_impl_t*
@@ -665,13 +631,13 @@ create_shader_program (cairo_gl_shader_program_t *program,
         return CAIRO_INT_STATUS_UNSUPPORTED;
 
     status = get_impl()->compile_shader (&program->vertex_shader,
-                                         get_impl()->vertex_enumerator(),
+                                         GL_VERTEX_SHADER,
                                          vertex_text);
     if (unlikely (status))
         goto FAILURE;
 
     status = get_impl()->compile_shader (&program->fragment_shader,
-                                         get_impl()->fragment_enumerator(),
+                                         GL_FRAGMENT_SHADER,
                                          fragment_text);
     if (unlikely (status))
         goto FAILURE;
commit 48a5a6eefc923db47c35ad7da3b635d205736716
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Feb 2 10:36:16 2010 -0800

    [gl] Avoid macro obfuscation of the shader impl structures.

diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 8d3e4e8..59f12ba 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -589,27 +589,37 @@ fragment_enumerator_core_2_0 (void)
     return GL_FRAGMENT_SHADER;
 }
 
-#define SHADER_IMPL_DECL(x)                                             \
-    static const shader_impl_t shader_impl_ ## x = {                    \
-        compile_shader_ ## x,                                           \
-        link_shader_ ## x,                                              \
-        destroy_shader_program_ ## x,                                   \
-        create_linear_gradient_shader_program_ ## x,                    \
-        create_radial_gradient_shader_program_ ## x,                    \
-        bind_float_to_shader_ ## x,                                     \
-        bind_vec2_to_shader_ ## x,                                      \
-        bind_vec3_to_shader_ ## x,                                      \
-        bind_vec4_to_shader_ ## x,                                      \
-        bind_matrix_to_shader_ ## x,                                    \
-        bind_texture_to_shader_ ## x,                                   \
-        vertex_enumerator_ ## x,                                        \
-        fragment_enumerator_ ## x                                       \
-    }
-
-SHADER_IMPL_DECL(core_2_0);
-SHADER_IMPL_DECL(arb);
-
-#undef SHADER_IMPL_DECL
+static const shader_impl_t shader_impl_core_2_0 = {
+    compile_shader_core_2_0,
+    link_shader_core_2_0,
+    destroy_shader_program_core_2_0,
+    create_linear_gradient_shader_program_core_2_0,
+    create_radial_gradient_shader_program_core_2_0,
+    bind_float_to_shader_core_2_0,
+    bind_vec2_to_shader_core_2_0,
+    bind_vec3_to_shader_core_2_0,
+    bind_vec4_to_shader_core_2_0,
+    bind_matrix_to_shader_core_2_0,
+    bind_texture_to_shader_core_2_0,
+    vertex_enumerator_core_2_0,
+    fragment_enumerator_core_2_0
+};
+
+static const shader_impl_t shader_impl_arb = {
+    compile_shader_arb,
+    link_shader_arb,
+    destroy_shader_program_arb,
+    create_linear_gradient_shader_program_arb,
+    create_radial_gradient_shader_program_arb,
+    bind_float_to_shader_arb,
+    bind_vec2_to_shader_arb,
+    bind_vec3_to_shader_arb,
+    bind_vec4_to_shader_arb,
+    bind_matrix_to_shader_arb,
+    bind_texture_to_shader_arb,
+    vertex_enumerator_arb,
+    fragment_enumerator_arb
+};
 
 static const shader_impl_t*
 get_impl (void)
commit 25ccc5dcb036554430b66fb1bf5f74766daa5612
Author: Zach Laine <whatwasthataddress at gmail.com>
Date:   Thu Jan 14 11:49:04 2010 -0600

    [gl] Add shader support code for GL versions < 3.0.
    
    Adds cairo_gl_shader_program_t, and functions to manipulate same.  Multiple GL
    entry points for shaders are provided -- one for the pre-GL 2.0 extenstions
    entry points, and one for GL 2.0.  This code is well tested, but currently
    unused in the GL backend.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index b0f60a6..e4fe000 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -34,6 +34,7 @@
  * Contributor(s):
  *	Carl Worth <cworth at cworth.org>
  *	Chris Wilson <chris at chris-wilson.co.uk>
+ *      T. Zachary Laine <whatwasthataddress at gmail.com>
  */
 
 #ifndef CAIRO_GL_PRIVATE_H
@@ -79,6 +80,13 @@ typedef struct cairo_gl_glyph_cache {
     unsigned int width, height;
 } cairo_gl_glyph_cache_t;
 
+typedef struct cairo_gl_shader_program {
+    GLuint vertex_shader;
+    GLuint fragment_shader;
+    GLuint program;
+    cairo_bool_t build_failure;
+} cairo_gl_shader_program_t;
+
 typedef struct _cairo_gl_context {
     cairo_device_t base;
 
@@ -241,6 +249,47 @@ _cairo_gl_y_flip (cairo_gl_surface_t *surface, int y)
 	return (surface->height - 1) - y;
 }
 
+void
+init_shader_program (cairo_gl_shader_program_t *program);
+
+void
+destroy_shader_program (cairo_gl_shader_program_t *program);
+
+cairo_status_t
+create_shader_program (cairo_gl_shader_program_t *program,
+                       const char *vertex_text,
+                       const char *fragment_text);
+
+cairo_status_t
+create_linear_gradient_shader_program (cairo_gl_shader_program_t *program);
+
+cairo_status_t
+create_radial_gradient_shader_program (cairo_gl_shader_program_t *program);
+
+cairo_status_t
+bind_float_to_shader (GLuint program, const char *name,
+                      float value);
+
+cairo_status_t
+bind_vec2_to_shader (GLuint program, const char *name,
+                     float value0, float value1);
+
+cairo_status_t
+bind_vec3_to_shader (GLuint program, const char *name,
+                     float value0, float value1,
+                     float value2);
+
+cairo_status_t
+bind_vec4_to_shader (GLuint program, const char *name,
+                     float value0, float value1,
+                     float value2, float value3);
+
+cairo_status_t
+bind_matrix_to_shader (GLuint program, const char *name, cairo_matrix_t* m);
+
+cairo_status_t
+bind_texture_to_shader (GLuint program, const char *name, GLuint tex_unit);
+
 slim_hidden_proto (cairo_gl_surface_create);
 
 #endif /* CAIRO_GL_PRIVATE_H */
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 5eb8e68..8d3e4e8 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -1,6 +1,7 @@
 /* cairo - a vector graphics library with display and print output
  *
  * Copyright © 2010 Eric Anholt
+ * Copyright © 2009 T. Zachary Laine
  *
  * This library is free software; you can redistribute it and/or
  * modify it either under the terms of the GNU Lesser General Public
@@ -27,7 +28,7 @@
  *
  * The Original Code is the cairo graphics library.
  *
- * The Initial Developer of the Original Code is Eric Anholt.
+ * The Initial Developer of the Original Code is T. Zachary Laine.
  */
 
 #include "cairoint.h"
@@ -116,3 +117,623 @@ fail:
     glDeleteObjectARB (shader);
     return status;
 }
+
+typedef struct _shader_impl {
+    cairo_status_t
+    (*compile_shader) (GLuint *shader, GLenum type, const char *text);
+
+    cairo_status_t
+    (*link_shader) (GLuint *program, GLuint vert, GLuint frag);
+
+    void
+    (*destroy_shader_program) (cairo_gl_shader_program_t *program);
+
+    cairo_status_t
+    (*create_linear_gradient_shader_program) (cairo_gl_shader_program_t *program);
+
+    cairo_status_t
+    (*create_radial_gradient_shader_program) (cairo_gl_shader_program_t *program);
+
+    cairo_status_t
+    (*bind_float_to_shader) (GLuint program, const char *name,
+                             float value);
+
+    cairo_status_t
+    (*bind_vec2_to_shader) (GLuint program, const char *name,
+                            float value0, float value1);
+
+    cairo_status_t
+    (*bind_vec3_to_shader) (GLuint program, const char *name,
+                            float value0, float value1,
+                            float value2);
+
+    cairo_status_t
+    (*bind_vec4_to_shader) (GLuint program, const char *name,
+                            float value0, float value1,
+                            float value2, float value3);
+
+    cairo_status_t
+    (*bind_matrix_to_shader) (GLuint program, const char *name, cairo_matrix_t* m);
+
+    cairo_status_t
+    (*bind_texture_to_shader) (GLuint program, const char *name, GLuint tex_unit);
+
+    GLenum
+    (*vertex_enumerator) (void);
+
+    GLenum
+    (*fragment_enumerator) (void);
+} shader_impl_t;
+
+static const shader_impl_t*
+get_impl (void);
+
+static const char * const minimal_vert_text_110 =
+    "#version 110\n"
+    "\n"
+    "void main ()\n"
+    "{ gl_Position = ftransform(); }\n";
+
+/* This fragment shader was adapted from Argiris Kirtzidis' cairo-gral
+ * library, found at git://github.com/akyrtzi/cairo-gral.git.  Argiris' shader
+ * was adapted from the original algorithm in pixman.
+ */
+static const char * const radial_gradient_frag_text_110 =
+    "#version 110\n"
+    "\n"
+    "uniform sampler1D tex;\n"
+    "uniform mat4 matrix;\n"
+    "uniform vec2 circle_1;\n"
+    "uniform float radius_0;\n"
+    "uniform float radius_1;\n"
+    "uniform float first_offset;\n"
+    "uniform float last_offset;\n"
+    "\n"
+    "void main ()\n"
+    "{\n"
+    "    vec2 pos = (matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    \n"
+    "    float dr = radius_1 - radius_0;\n"
+    "    float dot_circle_1 = dot (circle_1, circle_1);\n"
+    "    float dot_pos_circle_1 = dot (pos, circle_1);\n"
+    "    \n"
+    "    float A = dot_circle_1 - dr * dr;\n"
+    "    float B = -2.0 * (dot_pos_circle_1 + radius_0 * dr);\n"
+    "    float C = dot (pos, pos) - radius_0 * radius_0;\n"
+    "    float det = B * B - 4.0 * A * C;\n"
+    "    det = max (det, 0.0);\n"
+    "    \n"
+    "    float sqrt_det = sqrt (det);\n"
+    "    /* This complicated bit of logic acts as\n"
+    "     * \"if (A < 0.0) sqrt_det = -sqrt_det\", without the branch.\n"
+    "     */\n"
+    "    sqrt_det *= 1.0 + 2.0 * sign (min (A, 0.0));\n"
+    "    \n"
+    "    float t = (-B + sqrt_det) / (2.0 * A);\n"
+    "    t = (t - first_offset) / (last_offset - first_offset);\n"
+    "    gl_FragColor = texture1D (tex, t);\n"
+    "}\n";
+
+static const char * const linear_gradient_frag_text_110 =
+    "#version 110\n"
+    "\n"
+    "uniform sampler1D tex;\n"
+    "uniform mat4 matrix;\n"
+    "uniform vec2 segment;\n"
+    "uniform float first_offset;\n"
+    "uniform float last_offset;\n"
+    "\n"
+    "void main ()\n"
+    "{\n"
+    "    vec2 pos = (matrix * vec4 (gl_FragCoord.xy, 0.0, 1.0)).xy;\n"
+    "    float t = dot (pos, segment) / dot (segment, segment);\n"
+    "    t = (t - first_offset) / (last_offset - first_offset);\n"
+    "    gl_FragColor = texture1D (tex, t);\n"
+    "}\n";
+
+/* ARB_shader_objects / ARB_vertex_shader / ARB_fragment_shader extensions
+   API. */
+static cairo_status_t
+compile_shader_arb (GLuint *shader, GLenum type, const char *text)
+{
+    const char* strings[1] = { text };
+    GLint gl_status;
+
+    *shader = glCreateShaderObjectARB (type);
+    glShaderSourceARB (*shader, 1, strings, 0);
+    glCompileShaderARB (*shader);
+    glGetObjectParameterivARB (*shader, GL_OBJECT_COMPILE_STATUS_ARB, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetObjectParameterivARB (*shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetInfoLogARB (*shader, log_size, &chars, log);
+            printf ("OpenGL shader compilation failed.  Shader:\n"
+                    "%s\n"
+                    "OpenGL compilation log:\n"
+                    "%s\n",
+                    text, log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader compilation failed.\n");
+        }
+
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+link_shader_arb (GLuint *program, GLuint vert, GLuint frag)
+{
+    GLint gl_status;
+
+    *program = glCreateProgramObjectARB ();
+    glAttachObjectARB (*program, vert);
+    glAttachObjectARB (*program, frag);
+    glLinkProgramARB (*program);
+    glGetObjectParameterivARB (*program, GL_OBJECT_LINK_STATUS_ARB, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetObjectParameterivARB (*program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetInfoLogARB (*program, log_size, &chars, log);
+            printf ("OpenGL shader link failed:\n%s\n", log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader link failed.\n");
+        }
+
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+destroy_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    if (program->vertex_shader)
+        glDeleteObjectARB (program->vertex_shader);
+    if (program->fragment_shader)
+        glDeleteObjectARB (program->fragment_shader);
+    if (program->program)
+        glDeleteObjectARB (program->program);
+}
+
+static cairo_status_t
+create_linear_gradient_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    return create_shader_program (program,
+                                  minimal_vert_text_110,
+                                  linear_gradient_frag_text_110);
+}
+
+static cairo_status_t
+create_radial_gradient_shader_program_arb (cairo_gl_shader_program_t *program)
+{
+    return create_shader_program (program,
+                                  minimal_vert_text_110,
+                                  radial_gradient_frag_text_110);
+}
+
+static cairo_status_t
+bind_float_to_shader_arb (GLuint program, const char *name,
+                               float value)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform1fARB (location, value);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec2_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform2fARB (location, value0, value1);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec3_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform3fARB (location, value0, value1, value2);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec4_to_shader_arb (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2, float value3)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform4fARB (location, value0, value1, value2, value3);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_matrix_to_shader_arb (GLuint program, const char *name, cairo_matrix_t* m)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    float gl_m[16] = {
+        m->xx, m->xy, 0,     m->x0,
+        m->yx, m->yy, 0,     m->y0,
+        0,     0,     1,     0,
+        0,     0,     0,     1
+    };
+    glUniformMatrix4fvARB (location, 1, GL_TRUE, gl_m);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_texture_to_shader_arb (GLuint program, const char *name, GLuint tex_unit)
+{
+    GLint location = glGetUniformLocationARB (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform1iARB (location, tex_unit);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static GLenum
+vertex_enumerator_arb (void)
+{
+    return GL_VERTEX_SHADER_ARB;
+}
+
+static GLenum
+fragment_enumerator_arb (void)
+{
+    return GL_FRAGMENT_SHADER_ARB;
+}
+
+/* OpenGL Core 2.0 API. */
+static cairo_status_t
+compile_shader_core_2_0 (GLuint *shader, GLenum type, const char *text)
+{
+    const char* strings[1] = { text };
+    GLint gl_status;
+
+    *shader = glCreateShader (type);
+    glShaderSource (*shader, 1, strings, 0);
+    glCompileShader (*shader);
+    glGetShaderiv (*shader, GL_COMPILE_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetShaderiv (*shader, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetShaderInfoLog (*shader, log_size, &chars, log);
+            printf ("OpenGL shader compilation failed.  Shader:\n"
+                    "%s\n"
+                    "OpenGL compilation log:\n"
+                    "%s\n",
+                    text, log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader compilation failed.\n");
+        }
+
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+link_shader_core_2_0 (GLuint *program, GLuint vert, GLuint frag)
+{
+    GLint gl_status;
+
+    *program = glCreateProgram ();
+    glAttachShader (*program, vert);
+    glAttachShader (*program, frag);
+    glLinkProgram (*program);
+    glGetProgramiv (*program, GL_LINK_STATUS, &gl_status);
+    if (gl_status == GL_FALSE) {
+        GLint log_size;
+        glGetProgramiv (*program, GL_INFO_LOG_LENGTH, &log_size);
+        if (0 < log_size) {
+            char *log = _cairo_malloc (log_size);
+            GLint chars;
+
+            log[log_size - 1] = '\0';
+            glGetProgramInfoLog (*program, log_size, &chars, log);
+            printf ("OpenGL shader link failed:\n%s\n", log);
+
+            free (log);
+        } else {
+            printf ("OpenGL shader link failed.\n");
+        }
+
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    }
+
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static void
+destroy_shader_program_core_2_0 (cairo_gl_shader_program_t *program)
+{
+    glDeleteShader (program->vertex_shader);
+    glDeleteShader (program->fragment_shader);
+    glDeleteProgram (program->program);
+}
+
+static cairo_status_t
+create_linear_gradient_shader_program_core_2_0 (cairo_gl_shader_program_t *program)
+{
+    return create_shader_program (program,
+                                  minimal_vert_text_110,
+                                  linear_gradient_frag_text_110);
+}
+
+static cairo_status_t
+create_radial_gradient_shader_program_core_2_0 (cairo_gl_shader_program_t *program)
+{
+    return create_shader_program (program,
+                                  minimal_vert_text_110,
+                                  radial_gradient_frag_text_110);
+}
+
+static cairo_status_t
+bind_float_to_shader_core_2_0 (GLuint program, const char *name,
+                               float value)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform1f (location, value);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec2_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform2f (location, value0, value1);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec3_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform3f (location, value0, value1, value2);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_vec4_to_shader_core_2_0 (GLuint program, const char *name,
+                              float value0, float value1,
+                              float value2, float value3)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform4f (location, value0, value1, value2, value3);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_matrix_to_shader_core_2_0 (GLuint program, const char *name, cairo_matrix_t* m)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    float gl_m[16] = {
+        m->xx, m->xy, 0,     m->x0,
+        m->yx, m->yy, 0,     m->y0,
+        0,     0,     1,     0,
+        0,     0,     0,     1
+    };
+    glUniformMatrix4fv (location, 1, GL_TRUE, gl_m);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+bind_texture_to_shader_core_2_0 (GLuint program, const char *name, GLuint tex_unit)
+{
+    GLint location = glGetUniformLocation (program, name);
+    if (location == -1)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+    glUniform1i (location, tex_unit);
+    return CAIRO_STATUS_SUCCESS;
+}
+
+static GLenum
+vertex_enumerator_core_2_0 (void)
+{
+    return GL_VERTEX_SHADER;
+}
+
+static GLenum
+fragment_enumerator_core_2_0 (void)
+{
+    return GL_FRAGMENT_SHADER;
+}
+
+#define SHADER_IMPL_DECL(x)                                             \
+    static const shader_impl_t shader_impl_ ## x = {                    \
+        compile_shader_ ## x,                                           \
+        link_shader_ ## x,                                              \
+        destroy_shader_program_ ## x,                                   \
+        create_linear_gradient_shader_program_ ## x,                    \
+        create_radial_gradient_shader_program_ ## x,                    \
+        bind_float_to_shader_ ## x,                                     \
+        bind_vec2_to_shader_ ## x,                                      \
+        bind_vec3_to_shader_ ## x,                                      \
+        bind_vec4_to_shader_ ## x,                                      \
+        bind_matrix_to_shader_ ## x,                                    \
+        bind_texture_to_shader_ ## x,                                   \
+        vertex_enumerator_ ## x,                                        \
+        fragment_enumerator_ ## x                                       \
+    }
+
+SHADER_IMPL_DECL(core_2_0);
+SHADER_IMPL_DECL(arb);
+
+#undef SHADER_IMPL_DECL
+
+static const shader_impl_t*
+get_impl (void)
+{
+    if (GLEW_VERSION_2_0) {
+        return &shader_impl_core_2_0;
+    } else if (GLEW_ARB_shader_objects &&
+               GLEW_ARB_fragment_shader &&
+               GLEW_ARB_vertex_program) {
+        return &shader_impl_arb;
+    }
+
+    ASSERT_NOT_REACHED;
+    return NULL;
+}
+
+void
+init_shader_program (cairo_gl_shader_program_t *program)
+{
+    program->vertex_shader = 0;
+    program->fragment_shader = 0;
+    program->program = 0;
+    program->build_failure = FALSE;
+}
+
+void
+destroy_shader_program (cairo_gl_shader_program_t *program)
+{
+    return get_impl()->destroy_shader_program(program);
+}
+
+cairo_status_t
+create_shader_program (cairo_gl_shader_program_t *program,
+                       const char *vertex_text,
+                       const char *fragment_text)
+{
+    cairo_status_t status;
+
+    if (program->program != 0)
+        return CAIRO_STATUS_SUCCESS;
+
+    if (program->build_failure)
+        return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = get_impl()->compile_shader (&program->vertex_shader,
+                                         get_impl()->vertex_enumerator(),
+                                         vertex_text);
+    if (unlikely (status))
+        goto FAILURE;
+
+    status = get_impl()->compile_shader (&program->fragment_shader,
+                                         get_impl()->fragment_enumerator(),
+                                         fragment_text);
+    if (unlikely (status))
+        goto FAILURE;
+
+    status = get_impl()->link_shader (&program->program,
+                                      program->vertex_shader,
+                                      program->fragment_shader);
+    if (unlikely (status))
+        goto FAILURE;
+
+    return CAIRO_STATUS_SUCCESS;
+
+ FAILURE:
+    destroy_shader_program (program);
+    program->vertex_shader = 0;
+    program->fragment_shader = 0;
+    program->program = 0;
+    program->build_failure = TRUE;
+
+    return CAIRO_INT_STATUS_UNSUPPORTED;
+}
+
+cairo_status_t
+create_linear_gradient_shader_program (cairo_gl_shader_program_t *program)
+{
+    return get_impl()->create_linear_gradient_shader_program(program);
+}
+
+cairo_status_t
+create_radial_gradient_shader_program (cairo_gl_shader_program_t *program)
+{
+    return get_impl()->create_radial_gradient_shader_program(program);
+}
+
+cairo_status_t
+bind_float_to_shader (GLuint program, const char *name,
+                      float value)
+{
+    return get_impl()->bind_float_to_shader(program, name, value);
+}
+
+cairo_status_t
+bind_vec2_to_shader (GLuint program, const char *name,
+                     float value0, float value1)
+{
+    return get_impl()->bind_vec2_to_shader(program, name, value0, value1);
+}
+
+cairo_status_t
+bind_vec3_to_shader (GLuint program, const char *name,
+                     float value0, float value1,
+                     float value2)
+{
+    return get_impl()->bind_vec3_to_shader(program, name, value0, value1, value2);
+}
+
+cairo_status_t
+bind_vec4_to_shader (GLuint program, const char *name,
+                     float value0, float value1,
+                     float value2, float value3)
+{
+    return get_impl()->bind_vec4_to_shader(program, name, value0, value1, value2, value3);
+}
+
+cairo_status_t
+bind_matrix_to_shader (GLuint program, const char *name, cairo_matrix_t* m)
+{
+    return get_impl()->bind_matrix_to_shader(program, name, m);
+}
+
+cairo_status_t
+bind_texture_to_shader (GLuint program, const char *name, GLuint tex_unit)
+{
+    return get_impl()->bind_texture_to_shader(program, name, tex_unit);
+}
commit a6897ad3867611bd74f8ab7de4b1e8d8d1c12c71
Author: Eric Anholt <eric at anholt.net>
Date:   Tue Feb 2 11:24:36 2010 -0800

    [gl] Check for GLSL support once at context init instead of per draw.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index 84f0143..b0f60a6 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -87,6 +87,7 @@ typedef struct _cairo_gl_context {
     GLint fill_rectangles_color_uniform;
     GLint max_framebuffer_size;
     GLint max_texture_size;
+    cairo_bool_t using_glsl;
 
     cairo_gl_surface_t *current_target;
     cairo_gl_surface_t *glyphs_temporary_mask;
diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index df0cb14..9d6e416 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -114,6 +114,12 @@ _cairo_gl_context_init (cairo_gl_context_t *ctx)
 	return _cairo_error (CAIRO_STATUS_INVALID_FORMAT); /* XXX */
     }
 
+    if (GLEW_ARB_fragment_shader &&
+	GLEW_ARB_vertex_shader &&
+	GLEW_ARB_shader_objects) {
+	ctx->using_glsl = TRUE;
+    }
+
     /* Set up the dummy texture for tex_env_combine with constant color. */
     glGenTextures (1, &ctx->dummy_tex);
     glBindTexture (GL_TEXTURE_2D, ctx->dummy_tex);
@@ -1977,7 +1983,10 @@ _cairo_gl_surface_fill_rectangles (void			   *abstract_surface,
 				   cairo_rectangle_int_t   *rects,
 				   int			    num_rects)
 {
-    if (GLEW_ARB_fragment_shader && GLEW_ARB_vertex_shader) {
+    cairo_gl_surface_t *surface = abstract_surface;
+    cairo_gl_context_t *ctx = (cairo_gl_context_t *) surface->base.device;
+
+    if (ctx->using_glsl) {
 	return _cairo_gl_surface_fill_rectangles_glsl(abstract_surface,
 						      op,
 						      color,


More information about the cairo-commit mailing list