[cairo-commit] 3 commits - boilerplate/cairo-boilerplate-gl.c src/cairo-gl-private.h src/cairo-gl-shaders.c src/cairo-gl-surface.c

Eric Anholt anholt at kemper.freedesktop.org
Fri Feb 5 02:18:39 PST 2010


 boilerplate/cairo-boilerplate-gl.c |   17 ++--
 src/cairo-gl-private.h             |   13 +++
 src/cairo-gl-shaders.c             |   77 ++++++++++++++++++-
 src/cairo-gl-surface.c             |  149 ++++++++++++++++++++++++++++++++++---
 4 files changed, 234 insertions(+), 22 deletions(-)

New commits:
commit 696a715702ed18bbe3f7d8b97654a055fa37444e
Author: Eric Anholt <eric at anholt.net>
Date:   Wed Feb 3 21:34:24 2010 -0800

    [gl] Add radial 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.

diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index b054477..c544c91 100644
--- a/src/cairo-gl-private.h
+++ b/src/cairo-gl-private.h
@@ -92,6 +92,7 @@ typedef enum cairo_gl_shader_source {
     CAIRO_GL_SHADER_SOURCE_TEXTURE,
     CAIRO_GL_SHADER_SOURCE_TEXTURE_ALPHA,
     CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT,
+    CAIRO_GL_SHADER_SOURCE_RADIAL_GRADIENT,
     CAIRO_GL_SHADER_SOURCE_COUNT,
 } cairo_gl_shader_source_t;
 
@@ -100,6 +101,7 @@ typedef enum cairo_gl_shader_mask {
     CAIRO_GL_SHADER_MASK_TEXTURE,
     CAIRO_GL_SHADER_MASK_TEXTURE_ALPHA,
     CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT,
+    CAIRO_GL_SHADER_MASK_RADIAL_GRADIENT,
     CAIRO_GL_SHADER_MASK_NONE,
     CAIRO_GL_SHADER_MASK_SPANS,
     CAIRO_GL_SHADER_MASK_COUNT,
@@ -140,6 +142,7 @@ enum cairo_gl_composite_operand_type {
     OPERAND_CONSTANT,
     OPERAND_TEXTURE,
     OPERAND_LINEAR_GRADIENT,
+    OPERAND_RADIAL_GRADIENT,
 };
 
 /* This union structure describes a potential source or mask operand to the
@@ -166,6 +169,16 @@ typedef struct cairo_gl_composite_operand {
 	    float first_stop_offset;
 	    float last_stop_offset;
 	} linear;
+	struct {
+	    GLuint tex;
+	    cairo_matrix_t m;
+	    float circle_1_x;
+	    float circle_1_y;
+	    float radius_0;
+	    float radius_1;
+	    float first_stop_offset;
+	    float last_stop_offset;
+	} radial;
     } operand;
 
     const cairo_pattern_t *pattern;
diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c
index 9efd8f6..582eb48 100644
--- a/src/cairo-gl-shaders.c
+++ b/src/cairo-gl-shaders.c
@@ -738,6 +738,39 @@ static const char *fs_source_linear_gradient =
     "    t = (t - source_first_offset) / (source_last_offset - source_first_offset);\n"
     "    return texture1D (source_sampler, t);\n"
     "}\n";
+static const char *fs_source_radial_gradient =
+    "uniform sampler1D source_sampler;\n"
+    "uniform mat4 source_matrix;\n"
+    "uniform vec2 source_circle_1;\n"
+    "uniform float source_radius_0;\n"
+    "uniform float source_radius_1;\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"
+    "    \n"
+    "    float dr = source_radius_1 - source_radius_0;\n"
+    "    float dot_circle_1 = dot (source_circle_1, source_circle_1);\n"
+    "    float dot_pos_circle_1 = dot (pos, source_circle_1);\n"
+    "    \n"
+    "    float A = dot_circle_1 - dr * dr;\n"
+    "    float B = -2.0 * (dot_pos_circle_1 + source_radius_0 * dr);\n"
+    "    float C = dot (pos, pos) - source_radius_0 * source_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 - 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"
@@ -772,6 +805,39 @@ static const char *fs_mask_linear_gradient =
     "    t = (t - mask_first_offset) / (mask_last_offset - mask_first_offset);\n"
     "    return texture1D (mask_sampler, t);\n"
     "}\n";
+static const char *fs_mask_radial_gradient =
+    "uniform sampler1D mask_sampler;\n"
+    "uniform mat4 mask_matrix;\n"
+    "uniform vec2 mask_circle_1;\n"
+    "uniform float mask_radius_0;\n"
+    "uniform float mask_radius_1;\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"
+    "    \n"
+    "    float dr = mask_radius_1 - mask_radius_0;\n"
+    "    float dot_circle_1 = dot (mask_circle_1, mask_circle_1);\n"
+    "    float dot_pos_circle_1 = dot (pos, mask_circle_1);\n"
+    "    \n"
+    "    float A = dot_circle_1 - dr * dr;\n"
+    "    float B = -2.0 * (dot_pos_circle_1 + mask_radius_0 * dr);\n"
+    "    float C = dot (pos, pos) - mask_radius_0 * mask_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 - 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"
@@ -835,12 +901,14 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 	fs_source_texture,
 	fs_source_texture_alpha,
 	fs_source_linear_gradient,
+	fs_source_radial_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_radial_gradient,
 	fs_mask_none,
 	fs_mask_spans,
     };
@@ -873,11 +941,13 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 			       1);
 
     if (source == CAIRO_GL_SHADER_SOURCE_CONSTANT ||
-	source == CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT) {
+	source == CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT ||
+	source == CAIRO_GL_SHADER_SOURCE_RADIAL_GRADIENT) {
 	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
 	    vs_source = vs_spans_no_coords;
 	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT ||
-		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT)
+		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT ||
+		 mask == CAIRO_GL_SHADER_MASK_RADIAL_GRADIENT)
 	    vs_source = vs_no_coords;
 	else
 	    vs_source = vs_mask_coords;
@@ -885,7 +955,8 @@ _cairo_gl_get_program (cairo_gl_context_t *ctx,
 	if (mask == CAIRO_GL_SHADER_MASK_SPANS)
 	    vs_source = vs_spans_source_coords;
 	else if (mask == CAIRO_GL_SHADER_MASK_CONSTANT ||
-		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT)
+		 mask == CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT ||
+		 mask == CAIRO_GL_SHADER_MASK_RADIAL_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 91686ea..776043b 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -1242,6 +1242,46 @@ _cairo_gl_gradient_operand_init(cairo_gl_composite_operand_t *operand,
 	operand->source = CAIRO_GL_SHADER_SOURCE_LINEAR_GRADIENT;
 	operand->mask = CAIRO_GL_SHADER_MASK_LINEAR_GRADIENT;
         return CAIRO_STATUS_SUCCESS;
+    } else {
+	cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+        double x0, y0, r0, x1, y1, r1;
+
+	x0 = _cairo_fixed_to_double (radial->c1.x);
+	x1 = _cairo_fixed_to_double (radial->c2.x);
+	y0 = _cairo_fixed_to_double (radial->c1.y);
+	y1 = _cairo_fixed_to_double (radial->c2.y);
+	r0 = _cairo_fixed_to_double (radial->r1);
+	r1 = _cairo_fixed_to_double (radial->r2);
+
+        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.radial.tex,
+					   &operand->operand.radial.first_stop_offset,
+					   &operand->operand.radial.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.radial.m, -x0, -y0);
+	cairo_matrix_multiply (&operand->operand.radial.m,
+			       &operand->pattern->matrix,
+			       &operand->operand.radial.m);
+	cairo_matrix_translate (&operand->operand.radial.m, 0, dst->height);
+	cairo_matrix_scale (&operand->operand.radial.m, 1.0, -1.0);
+
+	operand->operand.radial.circle_1_x = x1 - x0;
+	operand->operand.radial.circle_1_y = y1 - y0;
+	operand->operand.radial.radius_0 = r0;
+	operand->operand.radial.radius_1 = r1;
+
+	operand->type = OPERAND_RADIAL_GRADIENT;
+	operand->source = CAIRO_GL_SHADER_SOURCE_RADIAL_GRADIENT;
+	operand->mask = CAIRO_GL_SHADER_MASK_RADIAL_GRADIENT;
+        return CAIRO_STATUS_SUCCESS;
     }
 
     return CAIRO_INT_STATUS_UNSUPPORTED;
@@ -1290,6 +1330,9 @@ _cairo_gl_operand_destroy (cairo_gl_composite_operand_t *operand)
     case OPERAND_LINEAR_GRADIENT:
 	glDeleteTextures (1, &operand->operand.linear.tex);
 	break;
+    case OPERAND_RADIAL_GRADIENT:
+	glDeleteTextures (1, &operand->operand.radial.tex);
+	break;
     case OPERAND_TEXTURE:
 	if (operand->operand.texture.surface != NULL) {
 	    cairo_gl_surface_t *surface = operand->operand.texture.surface;
@@ -1423,6 +1466,42 @@ _cairo_gl_set_src_operand (cairo_gl_context_t *ctx,
 	assert (!_cairo_status_is_error (status));
 
 	break;
+
+    case OPERAND_RADIAL_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.radial.m);
+	assert (!_cairo_status_is_error (status));
+
+        status = bind_vec2_to_shader (setup->shader->program,
+				      "source_circle_1",
+				      setup->src.operand.radial.circle_1_x,
+				      setup->src.operand.radial.circle_1_y);
+	assert (!_cairo_status_is_error (status));
+
+        status = bind_float_to_shader (setup->shader->program,
+				       "source_radius_0",
+				       setup->src.operand.radial.radius_0);
+	assert (!_cairo_status_is_error (status));
+
+        status = bind_float_to_shader (setup->shader->program,
+				       "source_radius_1",
+				       setup->src.operand.radial.radius_1);
+	assert (!_cairo_status_is_error (status));
+
+        status = bind_float_to_shader (setup->shader->program,
+				       "source_first_offset",
+				       setup->src.operand.radial.first_stop_offset);
+	assert (!_cairo_status_is_error (status));
+
+        status = bind_float_to_shader (setup->shader->program,
+				       "source_last_offset",
+				       setup->src.operand.radial.last_stop_offset);
+	break;
     }
 }
 
@@ -1464,6 +1543,7 @@ _cairo_gl_set_src_alpha_operand (cairo_gl_context_t *ctx,
 	}
 	break;
     case OPERAND_LINEAR_GRADIENT:
+    case OPERAND_RADIAL_GRADIENT:
 	assert(0);
     }
 }
@@ -1499,6 +1579,48 @@ _cairo_gl_set_linear_gradient_mask_operand (cairo_gl_composite_setup_t *setup)
     assert (!_cairo_status_is_error (status));
 }
 
+static void
+_cairo_gl_set_radial_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.radial.tex);
+    glEnable (GL_TEXTURE_1D);
+
+    status = bind_matrix_to_shader (setup->shader->program,
+				    "mask_matrix",
+				    &setup->mask.operand.radial.m);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_vec2_to_shader (setup->shader->program,
+				  "mask_circle_1",
+				  setup->mask.operand.radial.circle_1_x,
+				  setup->mask.operand.radial.circle_1_y);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_float_to_shader (setup->shader->program,
+				   "mask_radius_0",
+				   setup->mask.operand.radial.radius_0);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_float_to_shader (setup->shader->program,
+				   "mask_radius_1",
+				   setup->mask.operand.radial.radius_1);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_float_to_shader (setup->shader->program,
+				   "mask_first_offset",
+				   setup->mask.operand.radial.first_stop_offset);
+    assert (!_cairo_status_is_error (status));
+
+    status = bind_float_to_shader (setup->shader->program,
+				   "mask_last_offset",
+				   setup->mask.operand.radial.last_stop_offset);
+}
+
 /* 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.
  */
@@ -1571,6 +1693,10 @@ _cairo_gl_set_component_alpha_mask_operand (cairo_gl_context_t *ctx,
     case OPERAND_LINEAR_GRADIENT:
 	_cairo_gl_set_linear_gradient_mask_operand (setup);
 	break;
+
+    case OPERAND_RADIAL_GRADIENT:
+	_cairo_gl_set_radial_gradient_mask_operand (setup);
+	break;
     }
 }
 
@@ -1959,6 +2085,9 @@ _cairo_gl_surface_composite (cairo_operator_t		  op,
 	case OPERAND_LINEAR_GRADIENT:
 	    _cairo_gl_set_linear_gradient_mask_operand (&setup);
 	    break;
+	case OPERAND_RADIAL_GRADIENT:
+	    _cairo_gl_set_radial_gradient_mask_operand (&setup);
+	    break;
 	}
     }
 
commit 297b0ab47fa63ef99e65b6834b731c260ea3e941
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Feb 4 23:57:43 2010 -0800

    [gl] When making a boilerplate GLX window, ensure it has alpha.
    
    cairo_gl_surface_create_for_window assumes CONTENT_COLOR_ALPHA, so
    make sure the fbconfig we choose is good enough.  Fixes gl-window
    testcase results to basically match the non-window testcases.

diff --git a/boilerplate/cairo-boilerplate-gl.c b/boilerplate/cairo-boilerplate-gl.c
index d4f5e37..05c2d04 100644
--- a/boilerplate/cairo-boilerplate-gl.c
+++ b/boilerplate/cairo-boilerplate-gl.c
@@ -149,12 +149,13 @@ _cairo_boilerplate_gl_create_window (const char			 *name,
 				     int			  id,
 				     void			**closure)
 {
-    int rgb_attribs[] = { GLX_RGBA,
-			  GLX_RED_SIZE, 1,
-			  GLX_GREEN_SIZE, 1,
-			  GLX_BLUE_SIZE, 1,
-			  GLX_DOUBLEBUFFER,
-			  None };
+    int rgba_attribs[] = { GLX_RGBA,
+			   GLX_RED_SIZE, 1,
+			   GLX_GREEN_SIZE, 1,
+			   GLX_BLUE_SIZE, 1,
+			   GLX_ALPHA_SIZE, 1,
+			   GLX_DOUBLEBUFFER,
+			   None };
     XVisualInfo *vi;
     GLXContext ctx;
     gl_target_closure_t *gltc;
@@ -181,9 +182,9 @@ _cairo_boilerplate_gl_create_window (const char			 *name,
     if (mode == CAIRO_BOILERPLATE_MODE_TEST)
 	XSynchronize (gltc->dpy, 1);
 
-    vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgb_attribs);
+    vi = glXChooseVisual (dpy, DefaultScreen (dpy), rgba_attribs);
     if (vi == NULL) {
-	fprintf (stderr, "Failed to create RGB, double-buffered visual\n");
+	fprintf (stderr, "Failed to create RGBA, double-buffered visual\n");
 	XCloseDisplay (dpy);
 	free (gltc);
 	return NULL;
commit 6708bc0593a649d083ede429ae73f06691edc018
Author: Eric Anholt <eric at anholt.net>
Date:   Thu Feb 4 23:47:33 2010 -0800

    [gl] When filling a gradient texture, multiply alpha after interpolating.
    
    Fixes gradient-alpha testcase.

diff --git a/src/cairo-gl-surface.c b/src/cairo-gl-surface.c
index a0634a9..91686ea 100644
--- a/src/cairo-gl-surface.c
+++ b/src/cairo-gl-surface.c
@@ -980,10 +980,11 @@ lerp_and_set_color (GLubyte *color,
                     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;
+    double a = ((1.0 - alpha) * a0 + alpha * a1);
+    color[0] = ((1.0 - alpha) * b0 + alpha * b1) * a * 255.0;
+    color[1] = ((1.0 - alpha) * g0 + alpha * g1) * a * 255.0;
+    color[2] = ((1.0 - alpha) * r0 + alpha * r1) * a * 255.0;
+    color[3] = a * 255.0;
 }
 
 static void
@@ -1025,9 +1026,6 @@ _cairo_gl_create_gradient_texture (const 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;
@@ -1036,7 +1034,7 @@ _cairo_gl_create_gradient_texture (const cairo_gl_context_t *ctx,
                 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,
+                                    (float)j / tex_width,
                                     prev_r, prev_g, prev_b, prev_a,
                                     r, g, b, a);
                 ++j;
@@ -1044,9 +1042,9 @@ _cairo_gl_create_gradient_texture (const cairo_gl_context_t *ctx,
 
             /* 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[0] = b * a * 255.0;
+            color[1] = g * a * 255.0;
+            color[2] = r * a * 255.0;
             color[3] = a * 255.0;
             ++j;
 


More information about the cairo-commit mailing list