diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index b0544778f..c544c91c4 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 9efd8f6fa..582eb4847 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 91686ea7c..776043b01 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; } }