summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Anholt <eric@anholt.net>2010-02-03 16:03:52 -0800
committerEric Anholt <eric@anholt.net>2010-02-04 07:05:41 -0800
commite426cdd56973ff1114dac5e8b1130180d43631b0 (patch)
tree420a39c120052545f915819bf2b8e3fa206eef20
parent05b18fc09af0e0887c652bd482d68f00bec6d4d7 (diff)
[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.
-rw-r--r--src/cairo-gl-glyphs.c1
-rw-r--r--src/cairo-gl-private.h11
-rw-r--r--src/cairo-gl-shaders.c39
-rw-r--r--src/cairo-gl-surface.c139
4 files changed, 176 insertions, 14 deletions
diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c
index 69485fa7..00948d61 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 052ab2fa..b0544778 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 b98d10e4..9efd8f6f 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 cffd0959..a0634a97 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) {