/* * Emits the border fade functions used by an operand. * * If bilinear filtering is used, the emitted function performs a linear * fade to transparency effect in the intervals [-1/2n, 1/2n] and * [1 - 1/2n, 1 + 1/2n] (n: texture size). * * If nearest filtering is used, the emitted function just returns * 0.0 for all values outside [0, 1). */ static void _cairo_gl_shader_emit_border_fade (cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; GLint gl_filter = _cairo_gl_operand_get_gl_filter (operand); /* 2D version */ _cairo_output_stream_printf (stream, "vec2 %s_border_fade (vec2 coords, vec2 dims)\n" "{\n", namestr); if (gl_filter == GL_LINEAR) _cairo_output_stream_printf (stream, " return clamp(-abs(dims * (coords - 0.5)) + (dims + vec2(1.0)) * 0.5, 0.0, 1.0);\n"); else _cairo_output_stream_printf (stream, " bvec2 in_tex1 = greaterThanEqual (coords, vec2 (0.0));\n" " bvec2 in_tex2 = lessThan (coords, vec2 (1.0));\n" " return vec2 (float (all (in_tex1) && all (in_tex2)));\n"); _cairo_output_stream_printf (stream, "}\n"); /* 1D version */ _cairo_output_stream_printf (stream, "float %s_border_fade (float x, float dim)\n" "{\n", namestr); if (gl_filter == GL_LINEAR) _cairo_output_stream_printf (stream, " return clamp(-abs(dim * (x - 0.5)) + (dim + 1.0) * 0.5, 0.0, 1.0);\n"); else _cairo_output_stream_printf (stream, " bool in_tex = x >= 0.0 && x < 1.0;\n" " return float (in_tex);\n"); _cairo_output_stream_printf (stream, "}\n"); }
cairo_status_t _cairo_gl_get_shader_by_type (cairo_gl_context_t *ctx, cairo_gl_operand_t *source, cairo_gl_operand_t *mask, cairo_bool_t use_coverage, cairo_gl_shader_in_t in, cairo_gl_shader_t **shader) { cairo_shader_cache_entry_t lookup, *entry; char *fs_source; cairo_status_t status; lookup.ctx = ctx; lookup.src = source->type; lookup.mask = mask->type; lookup.dest = CAIRO_GL_OPERAND_NONE; lookup.use_coverage = use_coverage; lookup.in = in; lookup.src_gl_filter = _cairo_gl_operand_get_gl_filter (source); lookup.src_border_fade = _cairo_gl_shader_needs_border_fade (source); lookup.src_extend = _cairo_gl_operand_get_extend (source); lookup.mask_gl_filter = _cairo_gl_operand_get_gl_filter (mask); lookup.mask_border_fade = _cairo_gl_shader_needs_border_fade (mask); lookup.mask_extend = _cairo_gl_operand_get_extend (mask); lookup.base.hash = _cairo_gl_shader_cache_hash (&lookup); lookup.base.size = 1; entry = _cairo_cache_lookup (&ctx->shaders, &lookup.base); if (entry) { assert (entry->shader.program); *shader = &entry->shader; return CAIRO_STATUS_SUCCESS; } status = cairo_gl_shader_get_fragment_source (ctx, in, source, mask, use_coverage, CAIRO_GL_OPERAND_NONE, &fs_source); if (unlikely (status)) return status; entry = malloc (sizeof (cairo_shader_cache_entry_t)); if (unlikely (entry == NULL)) { free (fs_source); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memcpy (entry, &lookup, sizeof (cairo_shader_cache_entry_t)); entry->ctx = ctx; _cairo_gl_shader_init (&entry->shader); status = _cairo_gl_shader_compile (ctx, &entry->shader, cairo_gl_operand_get_var_type (source->type), cairo_gl_operand_get_var_type (mask->type), use_coverage, fs_source); free (fs_source); if (unlikely (status)) { free (entry); return status; } _cairo_gl_shader_set_samplers (ctx, &entry->shader); status = _cairo_cache_insert (&ctx->shaders, &entry->base); if (unlikely (status)) { _cairo_gl_shader_fini (ctx, &entry->shader); free (entry); return status; } *shader = &entry->shader; return CAIRO_STATUS_SUCCESS; }
void _cairo_gl_operand_bind_to_shader (cairo_gl_context_t *ctx, cairo_gl_operand_t *operand, cairo_gl_tex_t tex_unit) { const cairo_matrix_t *texgen = NULL; switch (operand->type) { default: case CAIRO_GL_OPERAND_COUNT: ASSERT_NOT_REACHED; case CAIRO_GL_OPERAND_NONE: return; case CAIRO_GL_OPERAND_CONSTANT: _cairo_gl_shader_bind_vec4 (ctx, ctx->current_shader->constant_location[tex_unit], operand->constant.color[0], operand->constant.color[1], operand->constant.color[2], operand->constant.color[3]); return; case CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE: case CAIRO_GL_OPERAND_RADIAL_GRADIENT_EXT: _cairo_gl_shader_bind_float (ctx, ctx->current_shader->a_location[tex_unit], operand->gradient.a); /* fall through */ case CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0: _cairo_gl_shader_bind_vec3 (ctx, ctx->current_shader->circle_d_location[tex_unit], operand->gradient.circle_d.center.x, operand->gradient.circle_d.center.y, operand->gradient.circle_d.radius); _cairo_gl_shader_bind_float (ctx, ctx->current_shader->radius_0_location[tex_unit], operand->gradient.radius_0); /* fall through */ case CAIRO_GL_OPERAND_LINEAR_GRADIENT: case CAIRO_GL_OPERAND_TEXTURE: /* * For GLES2 we use shaders to implement GL_CLAMP_TO_BORDER (used * with CAIRO_EXTEND_NONE). When bilinear filtering is enabled, * these shaders need the texture dimensions for their calculations. */ if (ctx->gl_flavor == CAIRO_GL_FLAVOR_ES && _cairo_gl_operand_get_extend (operand) == CAIRO_EXTEND_NONE && _cairo_gl_operand_get_gl_filter (operand) == GL_LINEAR) { float width, height; if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { width = operand->texture.surface->width; height = operand->texture.surface->height; } else { width = operand->gradient.gradient->cache_entry.size, height = 1; } _cairo_gl_shader_bind_vec2 (ctx, ctx->current_shader->texdims_location[tex_unit], width, height); } break; } if (operand->type == CAIRO_GL_OPERAND_TEXTURE) { if (operand->texture.texgen) texgen = &operand->texture.attributes.matrix; } else { if (operand->gradient.texgen) texgen = &operand->gradient.m; } if (texgen) { _cairo_gl_shader_bind_matrix(ctx, ctx->current_shader->texgen_location[tex_unit], texgen); } }