diff --git a/src/cairo-gl-glyphs.c b/src/cairo-gl-glyphs.c index 69485fa75..00948d61a 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 052ab2fa8..b0544778f 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 b98d10e45..9efd8f6fa 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 cffd09590..a0634a97d 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) {