From 31e116f084d0ff073bed9d0e9c1c6ca1e5db4843 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Sat, 13 Nov 2010 15:48:03 -0800 Subject: [PATCH] gl: Avoid using gl_FragCoord for linear gradients. The issue is that we store our data flipped based on whether we're rendering to an FBO or to a window. By not flipping our gl_FragCoord usage based on that (either with math or ARB_frag_coord_conventions), this caused linear gradients to be flipped when rendering either to a window or to an FBO. To avoid this, pass in appropriate texcoords. And, if we're passing in texcoords, just do the projection to the linear gradient factor on the CPU side per vertex instead of providing a bunch of uniforms to do the math per fragment. Fixes 18 testcases. --- src/cairo-gl-composite.c | 74 ++++++++++++++++++++++++---------------- src/cairo-gl-private.h | 4 +-- src/cairo-gl-shaders.c | 12 +++---- 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/cairo-gl-composite.c b/src/cairo-gl-composite.c index 5e3eb8f17..97407bbde 100644 --- a/src/cairo-gl-composite.c +++ b/src/cairo-gl-composite.c @@ -161,12 +161,8 @@ _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, 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); + double x0, y0; + float dx, dy, sf, offset; status = _cairo_gl_create_gradient_texture (dst, gradient, @@ -174,18 +170,30 @@ _cairo_gl_gradient_operand_init (cairo_gl_operand_t *operand, if (unlikely (status)) return status; - /* Translation matrix from the destination fragment coordinates - * (pixels from lower left = 0,0) to the coordinates in the - */ - cairo_matrix_init_translate (&operand->linear.m, -x0, -y0); - cairo_matrix_multiply (&operand->linear.m, - &pattern->matrix, - &operand->linear.m); - cairo_matrix_translate (&operand->linear.m, 0, dst->height); - cairo_matrix_scale (&operand->linear.m, 1.0, -1.0); + dx = _cairo_fixed_to_double (linear->p2.x - linear->p1.x); + dy = _cairo_fixed_to_double (linear->p2.y - linear->p1.y); + sf = 1.0 / (dx * dx + dy * dy); + dx *= sf; + dy *= sf; - operand->linear.segment_x = x1 - x0; - operand->linear.segment_y = y1 - y0; + x0 = _cairo_fixed_to_double (linear->p1.x); + y0 = _cairo_fixed_to_double (linear->p1.y); + offset = dx * x0 + dy * y0; + + if (_cairo_matrix_is_identity (&linear->base.base.matrix)) { + operand->linear.m.xx = dx; + operand->linear.m.xy = dy; + operand->linear.m.x0 = -offset; + } else { + cairo_matrix_t m; + + cairo_matrix_init (&m, dx, 0, dy, 0, -offset, 0); + cairo_matrix_multiply (&operand->linear.m, + &linear->base.base.matrix, &m); + } + operand->linear.m.yx = 0.0; + operand->linear.m.yy = 1.0; + operand->linear.m.y0 = 0.0; operand->linear.extend = pattern->extend; @@ -368,15 +376,6 @@ _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, operand->constant.color[3]); break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: - strcpy (custom_part, "_matrix"); - _cairo_gl_shader_bind_matrix (ctx, - uniform_name, - &operand->linear.m); - strcpy (custom_part, "_segment"); - _cairo_gl_shader_bind_vec2 (ctx, - uniform_name, - operand->linear.segment_x, - operand->linear.segment_y); strcpy (custom_part, "_sampler"); _cairo_gl_shader_bind_texture(ctx, uniform_name, @@ -640,7 +639,12 @@ _cairo_gl_context_setup_operand (cairo_gl_context_t *ctx, _cairo_gl_texture_set_extend (ctx, GL_TEXTURE_1D, operand->linear.extend); _cairo_gl_texture_set_filter (ctx, GL_TEXTURE_1D, CAIRO_FILTER_BILINEAR); glEnable (GL_TEXTURE_1D); - break; + + glClientActiveTexture (GL_TEXTURE0 + tex_unit); + glTexCoordPointer (2, GL_FLOAT, vertex_size, + (void *) (uintptr_t) vertex_offset); + glEnableClientState (GL_TEXTURE_COORD_ARRAY); + break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: _cairo_gl_gradient_reference (operand->radial.gradient); glActiveTexture (GL_TEXTURE0 + tex_unit); @@ -686,6 +690,8 @@ _cairo_gl_context_destroy_operand (cairo_gl_context_t *ctx, _cairo_gl_gradient_destroy (ctx->operands[tex_unit].linear.gradient); glActiveTexture (GL_TEXTURE0 + tex_unit); glDisable (GL_TEXTURE_1D); + glClientActiveTexture (GL_TEXTURE0 + tex_unit); + glDisableClientState (GL_TEXTURE_COORD_ARRAY); break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: _cairo_gl_gradient_destroy (ctx->operands[tex_unit].radial.gradient); @@ -784,12 +790,12 @@ _cairo_gl_operand_get_vertex_size (cairo_gl_operand_type_t type) ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT: return 0; case CAIRO_GL_OPERAND_SPANS: return 4 * sizeof (GLbyte); case CAIRO_GL_OPERAND_TEXTURE: + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: return 2 * sizeof (GLfloat); } } @@ -1075,7 +1081,6 @@ _cairo_gl_operand_emit (cairo_gl_operand_t *operand, ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT: break; case CAIRO_GL_OPERAND_SPANS: @@ -1092,6 +1097,17 @@ _cairo_gl_operand_emit (cairo_gl_operand_t *operand, *(*vb)++ = fi.f; } break; + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: + { + double s = x; + double t = y; + + cairo_matrix_transform_point (&operand->linear.m, &s, &t); + + *(*vb)++ = s; + *(*vb)++ = 0.0; + } + break; case CAIRO_GL_OPERAND_TEXTURE: { cairo_surface_attributes_t *src_attributes = &operand->texture.attributes; diff --git a/src/cairo-gl-private.h b/src/cairo-gl-private.h index 54f226f42..042c6a152 100644 --- a/src/cairo-gl-private.h +++ b/src/cairo-gl-private.h @@ -154,8 +154,8 @@ typedef struct cairo_gl_operand { struct { cairo_gl_gradient_t *gradient; cairo_matrix_t m; - float segment_x; - float segment_y; + float x0, y0, dx, dy; + float scale; cairo_extend_t extend; } linear; struct { diff --git a/src/cairo-gl-shaders.c b/src/cairo-gl-shaders.c index ea329dd8e..abb4dd2e5 100644 --- a/src/cairo-gl-shaders.c +++ b/src/cairo-gl-shaders.c @@ -568,9 +568,9 @@ cairo_gl_operand_get_var_type (cairo_gl_operand_type_t type) ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: case CAIRO_GL_OPERAND_CONSTANT: - case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_RADIAL_GRADIENT: return CAIRO_GL_VAR_NONE; + case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_TEXTURE: return CAIRO_GL_VAR_TEXCOORDS; case CAIRO_GL_OPERAND_SPANS: @@ -700,18 +700,14 @@ cairo_gl_shader_emit_color (cairo_output_stream_t *stream, break; case CAIRO_GL_OPERAND_LINEAR_GRADIENT: _cairo_output_stream_printf (stream, + "varying vec2 %s_texcoords;\n" "uniform sampler1D %s_sampler;\n" - "uniform mat3 %s_matrix;\n" - "uniform vec2 %s_segment;\n" "\n" "vec4 get_%s()\n" "{\n" - " vec2 pos = (%s_matrix * vec3 (gl_FragCoord.xy, 1.0)).xy;\n" - " float t = dot (pos, %s_segment) / dot (%s_segment, %s_segment);\n" - " return texture1D (%s_sampler, t);\n" + " return texture1D (%s_sampler, %s_texcoords.x);\n" "}\n", - namestr, namestr, namestr, namestr, namestr, - namestr, namestr, namestr, namestr); + namestr, namestr, namestr, namestr, namestr); break; case CAIRO_GL_OPERAND_RADIAL_GRADIENT: _cairo_output_stream_printf (stream,