summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2010-02-03 21:34:24 -0800
committerEric Anholt <eric@anholt.net>2010-02-05 00:13:30 -0800
commit696a715702ed18bbe3f7d8b97654a055fa37444e (patch)
tree14679b74581f2d41d0d577a23dcc417392c369e8
parent297b0ab47fa63ef99e65b6834b731c260ea3e941 (diff)
[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.
-rw-r--r--src/cairo-gl-private.h13
-rw-r--r--src/cairo-gl-shaders.c77
-rw-r--r--src/cairo-gl-surface.c129
3 files changed, 216 insertions, 3 deletions
diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h
index b0544778..c544c91c 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 9efd8f6f..582eb484 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 91686ea7..776043b0 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;
}
}