/* * Emits the wrap function used by an operand. * * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are * only available for NPOT textures if the GL_OES_texture_npot is supported. * If GL_OES_texture_npot is not supported, we need to implement the wrapping * functionality in the shader. */ static void _cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); _cairo_output_stream_printf (stream, "vec2 %s_wrap(vec2 coords)\n" "{\n", namestr); if (! ctx->has_npot_repeat && (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) { if (extend == CAIRO_EXTEND_REPEAT) { _cairo_output_stream_printf (stream, " return fract(coords);\n"); } else { /* CAIRO_EXTEND_REFLECT */ _cairo_output_stream_printf (stream, " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); } } else { _cairo_output_stream_printf (stream, " return coords;\n"); } _cairo_output_stream_printf (stream, "}\n"); }
/* * Returns whether an operand needs a special border fade fragment shader * to simulate the GL_CLAMP_TO_BORDER wrapping method that is missing in GLES2. */ static cairo_bool_t _cairo_gl_shader_needs_border_fade (cairo_gl_operand_t *operand) { cairo_extend_t extend =_cairo_gl_operand_get_extend (operand); return extend == CAIRO_EXTEND_NONE && (operand->type == CAIRO_GL_OPERAND_TEXTURE || operand->type == CAIRO_GL_OPERAND_LINEAR_GRADIENT || operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_NONE || operand->type == CAIRO_GL_OPERAND_RADIAL_GRADIENT_A0); }
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); } }
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; }
/* * Emits the wrap function used by an operand. * * In OpenGL ES 2.0, repeat wrap modes (GL_REPEAT and GL_MIRRORED REPEAT) are * only available for NPOT textures if the GL_OES_texture_npot is supported. * If GL_OES_texture_npot is not supported, we need to implement the wrapping * functionality in the shader. */ static void _cairo_gl_shader_emit_wrap (cairo_gl_context_t *ctx, cairo_output_stream_t *stream, cairo_gl_operand_t *operand, cairo_gl_tex_t name) { const char *namestr = operand_names[name]; cairo_extend_t extend = _cairo_gl_operand_get_extend (operand); cairo_bool_t use_atlas = _cairo_gl_operand_get_use_atlas (operand); if (use_atlas) _cairo_output_stream_printf (stream, "vec2 %s_wrap (vec2 coords, vec2 start_coords, vec2 stop_coords)\n" "{\n", namestr); else _cairo_output_stream_printf (stream, "vec2 %s_wrap(vec2 coords)\n" "{\n", namestr); if (use_atlas) { if (extend == CAIRO_EXTEND_REPEAT) { _cairo_output_stream_printf (stream, " vec2 range = stop_coords - start_coords;\n" " return mod (coords - start_coords, range) + start_coords;\n"); } else if (extend == CAIRO_EXTEND_REFLECT){ _cairo_output_stream_printf (stream, " vec2 range = stop_coords - start_coords;\n" " vec2 frac = mod (coords - start_coords, range);\n" " return mix(frac + start_coords, range - frac + start_coords, mod(floor((coords - start_coords) / range), 2.0));\n"); } else if (extend == CAIRO_EXTEND_PAD) { _cairo_output_stream_printf (stream, " bvec2 compare_to_start = lessThan (coords, start_coords);\n" " bvec2 compare_to_stop = greaterThan (coords, stop_coords);\n" " if (all (compare_to_start))\n" " return start_coords;\n" " else if (all (compare_to_stop))\n" " return stop_coords;\n" " else if (compare_to_start.x && compare_to_stop.y)\n" " return vec2 (start_coords.x, stop_coords.y);\n" " else if (compare_to_start.x && ! compare_to_stop.y)\n" " return vec2 (start_coords.x, coords.y);\n" " else if (compare_to_stop.x && compare_to_start.y)\n" " return vec2 (stop_coords.x, start_coords.y);\n" " else if (compare_to_stop.x && ! compare_to_stop.y)\n" " return vec2 (stop_coords.x, coords.y);\n" " else if (compare_to_start.y && ! compare_to_start.x)\n" " return vec2 (coords.x, start_coords.y);\n" " else if (compare_to_stop.y && ! compare_to_start.x)\n" " return vec2 (coords.x, stop_coords.y);\n" " else\n" " return coords;\n"); } else { _cairo_output_stream_printf (stream, " if (any (lessThan (coords, start_coords)))\n" " return vec2 (-1.0);\n" " if (any (greaterThan (coords, stop_coords)))\n" " return vec2 (-1.0);\n" " else\n" " return coords;\n"); } } else { if (! ctx->has_npot_repeat && (extend == CAIRO_EXTEND_REPEAT || extend == CAIRO_EXTEND_REFLECT)) { if (extend == CAIRO_EXTEND_REPEAT) { _cairo_output_stream_printf (stream, " return fract(coords);\n"); } else { /* CAIRO_EXTEND_REFLECT */ _cairo_output_stream_printf (stream, " return mix(fract(coords), 1.0 - fract(coords), floor(mod(coords, 2.0)));\n"); } } else { _cairo_output_stream_printf (stream, " return coords;\n"); } } _cairo_output_stream_printf (stream, "}\n"); }