diff options
author | Eric Anholt <eric@anholt.net> | 2010-02-03 21:34:24 -0800 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2010-02-05 00:13:30 -0800 |
commit | 696a715702ed18bbe3f7d8b97654a055fa37444e (patch) | |
tree | 14679b74581f2d41d0d577a23dcc417392c369e8 | |
parent | 297b0ab47fa63ef99e65b6834b731c260ea3e941 (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.h | 13 | ||||
-rw-r--r-- | src/cairo-gl-shaders.c | 77 | ||||
-rw-r--r-- | src/cairo-gl-surface.c | 129 |
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; } } |