diff options
Diffstat (limited to 'src/cairo-gl-filters.c')
-rwxr-xr-x | src/cairo-gl-filters.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/src/cairo-gl-filters.c b/src/cairo-gl-filters.c new file mode 100755 index 000000000..7ddc4a246 --- /dev/null +++ b/src/cairo-gl-filters.c @@ -0,0 +1,416 @@ +/* cairo - a vector graphics library with display and print output + * + * Copyright © 2009 Eric Anholt + * Copyright © 2009 Chris Wilson + * Copyright © 2005,2010 Red Hat, Inc + * Copyright © 2011 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is Red Hat, Inc. + * + * Contributor(s): + * Benjamin Otte <otte@gnome.org> + * Carl Worth <cworth@cworth.org> + * Chris Wilson <chris@chris-wilson.co.uk> + * Eric Anholt <eric@anholt.net> + */ + +#include "cairoint.h" + +#include "cairo-gl-private.h" +#include "cairo-filters-private.h" + +static cairo_int_status_t +_draw_rect (cairo_gl_context_t *ctx, + cairo_gl_composite_t *setup, + cairo_rectangle_int_t *rect) +{ + int quad[8]; + + quad[0] = quad[2] = rect->x; + quad[1] = quad[7] = rect->y; + quad[3] = quad[5] = rect->y + rect->height; + quad[4] = quad[6] = rect->x + rect->width; + + return _cairo_gl_composite_emit_int_quad_as_tristrip (ctx, setup, quad); +} + +/* stage 0 - shrink image */ +static cairo_int_status_t +gaussian_filter_stage_0 (cairo_surface_pattern_t *pattern, + cairo_gl_surface_t *src, + cairo_gl_surface_t *dst, + int src_width, int src_height, + int dst_width, int dst_height) +{ + cairo_rectangle_int_t rect; + cairo_clip_t *clip; + cairo_int_status_t status; + + src->blur_stage = CAIRO_GL_BLUR_STAGE_0; + _cairo_pattern_init_for_surface (pattern, &src->base); + + cairo_matrix_init_scale (&pattern->base.matrix, + (double) src_width / (double) dst_width, + (double) src_height / (double) dst_height); + + rect.x = 0; + rect.y = 0; + rect.width = dst_width + 1; + rect.height = dst_height + 1; + clip = _cairo_clip_intersect_rectangle (NULL, &rect); + + status = _cairo_surface_paint (&dst->base, + CAIRO_OPERATOR_SOURCE, + &pattern->base, clip); + + _cairo_clip_destroy (clip); + status = _cairo_gl_surface_resolve_multisampling (dst); + + return status; +} + +/* x-axis pass to scratches[1] */ +static cairo_int_status_t +gaussian_filter_stage_1 (cairo_bool_t x_axis, + const cairo_surface_pattern_t *original_pattern, + cairo_surface_pattern_t *pattern, + cairo_gl_surface_t *src, + cairo_gl_surface_t *dst, + int dst_width, int dst_height, + cairo_bool_t is_opaque, + cairo_gl_context_t **ctx) +{ + int row, col; + cairo_gl_composite_t setup; + cairo_gl_context_t *ctx_out = NULL; + cairo_rectangle_int_t rect; + cairo_int_status_t status; + + src->image_content_scale_x = (double) dst_width / (double) src->width; + src->image_content_scale_y = (double) dst_height / (double) src->height; + row = original_pattern->base.y_radius * 2 + 1; + col = original_pattern->base.x_radius * 2 + 1; + + src->blur_stage = CAIRO_GL_BLUR_STAGE_1; + _cairo_pattern_init_for_surface (pattern, &src->base); + pattern->base.filter = CAIRO_FILTER_GOOD; + + if (x_axis) { + src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN; + src->operand.pass = 1; + + memset (&src->operand.texture.coef[0], 0, sizeof (float) * col); + compute_x_coef_to_float (original_pattern->base.convolution_matrix, + row, col, &src->operand.texture.coef[0]); + + src->operand.texture.x_radius = original_pattern->base.x_radius; + src->operand.texture.y_radius = 1; + } + else { + src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN; + src->operand.pass = 2; + + memset (&src->operand.texture.coef[0], 0, sizeof (float) * row); + compute_y_coef_to_float (original_pattern->base.convolution_matrix, + row, col, &src->operand.texture.coef[0]); + + src->operand.texture.y_radius = original_pattern->base.y_radius; + src->operand.texture.x_radius = 1; + } + + *ctx = ctx_out; + status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE, + dst, FALSE); + if (unlikely (status)) + return status; + + pattern->base.extend = CAIRO_EXTEND_NONE; + status = _cairo_gl_composite_set_source (&setup, &pattern->base, + NULL, NULL, FALSE, FALSE); + if (unlikely (status)) { + _cairo_gl_composite_fini (&setup); + return status; + } + + status = _cairo_gl_composite_begin (&setup, &ctx_out); + if (unlikely (status)) { + _cairo_gl_composite_fini (&setup); + return status; + } + + if (is_opaque) + _cairo_gl_shader_bind_float (ctx_out, + _cairo_gl_shader_uniform_for_texunit ( + CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE), + 1.0); + else + _cairo_gl_shader_bind_float (ctx_out, + _cairo_gl_shader_uniform_for_texunit ( + CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE), + 0.0); + + rect.x = 0; + rect.y = 0; + rect.width = dst_width + 1; + rect.height = dst_height + 1; + status = _draw_rect (ctx_out, &setup, &rect); + _cairo_gl_composite_fini (&setup); + if (unlikely (status)) { + status = _cairo_gl_context_release (ctx_out, status); + return status; + } + + *ctx = ctx_out; + _cairo_pattern_fini (&pattern->base); + return status; +} + +/* y-axis pass to scratches[1] */ +static cairo_int_status_t +gaussian_filter_stage_2 (cairo_bool_t y_axis, + const cairo_surface_pattern_t *original_pattern, + cairo_gl_surface_t *stage_1_src, + cairo_gl_surface_t *stage_2_src, + int dst_width, int dst_height) +{ + cairo_int_status_t status; + int row, col; + + stage_2_src->image_content_scale_x = (double) dst_width / (double) stage_1_src->width; + stage_2_src->image_content_scale_y = (double) dst_height / (double) stage_1_src->height; + + if (y_axis) { + stage_2_src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN; + stage_2_src->operand.pass = 2; + + row = original_pattern->base.y_radius * 2 + 1; + col = original_pattern->base.x_radius * 2 + 1; + + memset (&stage_2_src->operand.texture.coef[0], 0, sizeof (float) * row); + compute_y_coef_to_float (original_pattern->base.convolution_matrix, + row, col, &stage_2_src->operand.texture.coef[2]); + stage_2_src->operand.texture.y_radius = original_pattern->base.y_radius; + stage_2_src->operand.texture.x_radius = 1; + } + else + stage_2_src->operand.type = CAIRO_GL_OPERAND_TEXTURE; + + stage_2_src->blur_stage = CAIRO_GL_BLUR_STAGE_2; + + status = _cairo_gl_surface_resolve_multisampling (stage_2_src); + + return CAIRO_INT_STATUS_SUCCESS; +} + +/* out_extents should be populated by caller, and modified by the + * function + */ +cairo_gl_surface_t * +_cairo_gl_gaussian_filter (cairo_gl_surface_t *dst, + const cairo_surface_pattern_t *pattern, + cairo_gl_surface_t *src, + cairo_rectangle_int_t *extents_out) +{ + cairo_gl_surface_t *scratches[2]; + int src_width, src_height; + int width, height; + int scratch_width, scratch_height; + + cairo_gl_context_t *ctx, *ctx_out; + cairo_status_t status; + cairo_bool_t skip_stage_0 = FALSE; + cairo_gl_operand_type_t saved_type = src->operand.type; + + cairo_surface_pattern_t temp_pattern; + cairo_bool_t is_source; + + int n; + cairo_bool_t is_opaque = FALSE; + + cairo_content_t content = cairo_surface_get_content (&src->base); + + if (src->operand.type == CAIRO_GL_OPERAND_GAUSSIAN) { + extents_out->x = extents_out->y = 0; + extents_out->width = cairo_gl_surface_get_width (&src->base) * src->image_content_scale_x; + extents_out->height = cairo_gl_surface_get_height (&src->base) * src->image_content_scale_y; + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + } + + if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN || + ! pattern->base.convolution_matrix || + ! _cairo_gl_surface_is_texture (src)) + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + + if (content == CAIRO_CONTENT_COLOR) + is_opaque = TRUE; + + src_width = cairo_gl_surface_get_width (&src->base); + src_height = cairo_gl_surface_get_height (&src->base); + + width = src_width / pattern->base.shrink_factor_x; + height = src_height / pattern->base.shrink_factor_y; + + status = _cairo_gl_context_acquire (dst->base.device, &ctx); + if (unlikely (status)) + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + + for (n = 0; n < 2; n++) { + if (ctx->source_scratch_in_use) { + scratches[n] = ctx->mask_scratch_surfaces[n]; + is_source = FALSE; + } + else { + scratches[n] = ctx->source_scratch_surfaces[n]; + is_source = TRUE; + } + + if (scratches[n]) { + scratch_width = cairo_gl_surface_get_width (&scratches[n]->base); + scratch_height = cairo_gl_surface_get_height (&scratches[n]->base); + + if ((scratch_width < width && + scratch_width < MAX_SCRATCH_SIZE) || + (scratch_height < height && + scratch_height < MAX_SCRATCH_SIZE)) { + cairo_surface_destroy (&scratches[n]->base); + scratches[n] = NULL; + } + else if (scratch_width > 4 * width && + scratch_height > 4 * height) { + cairo_surface_destroy (&scratches[n]->base); + scratches[n] = NULL; + } + } + + if (! scratches[n]) { + scratch_width = scratch_height = MIN_SCRATCH_SIZE; + while (scratch_width < width) { + scratch_width *= 2; + if (scratch_width == MAX_SCRATCH_SIZE) + break; + else if (scratch_width > MAX_SCRATCH_SIZE) { + scratch_width *= 0.5; + break; + } + } + + while (scratch_height < height) { + scratch_height *= 2; + if (scratch_height == MAX_SCRATCH_SIZE) + break; + else if (scratch_height > MAX_SCRATCH_SIZE) { + scratch_height *= 0.5; + break; + } + } + + scratches[n] = + (cairo_gl_surface_t *)_cairo_gl_surface_create_scratch (ctx, + CAIRO_CONTENT_COLOR_ALPHA, + scratch_width, + scratch_height); + _cairo_surface_release_device_reference (&scratches[n]->base); + } + + if (ctx->source_scratch_in_use) + ctx->mask_scratch_surfaces[n] = scratches[n]; + else + ctx->source_scratch_surfaces[n] = scratches[n]; + + scratches[n]->needs_to_cache = FALSE; + scratches[n]->force_no_cache = TRUE; + } + + if (is_source) + ctx->source_scratch_in_use = TRUE; + + /* we have created two scratch surfaces */ + /* shrink surface to scratches[0] */ + width = src_width / pattern->base.shrink_factor_x; + height = src_height / pattern->base.shrink_factor_y; + if (pattern->base.shrink_factor_x == 1.0 && + pattern->base.shrink_factor_y == 1.0) { + skip_stage_0 = TRUE; + width = src_width; + height = src_height; + } + else if (width > scratches[0]->width || + height > scratches[0]->height) { + width = scratches[0]->width; + height = scratches[0]->height; + } + + if (! skip_stage_0) { + status = gaussian_filter_stage_0 (&temp_pattern, src, + scratches[0], + src_width, src_height, + width, height); + _cairo_pattern_fini (&temp_pattern.base); + if (unlikely (status)) + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + } + + /* x-axis pass to scratches[1] */ + if (! skip_stage_0) + status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern, + scratches[0], scratches[1], + width, height, is_opaque, &ctx_out); + else { + status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern, + src, scratches[1], + width, height, is_opaque, &ctx_out); + src->operand.type = saved_type; + } + + if (ctx_out) + status = _cairo_gl_context_release (ctx_out, status); + if (unlikely (status)) + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + + /* y-axis pass */ + status = gaussian_filter_stage_1 (FALSE, pattern, &temp_pattern, + scratches[1], scratches[0], + width, height, is_opaque, &ctx_out); + if (ctx_out) + status = _cairo_gl_context_release (ctx_out, status); + if (unlikely (status)) + return (cairo_gl_surface_t *)cairo_surface_reference (&src->base); + + status = gaussian_filter_stage_2 (FALSE, pattern, + scratches[1], scratches[0], + width, height); + + extents_out->x = 0; + extents_out->y = 0; + extents_out->width = width; + extents_out->height = height; + + status = _cairo_gl_context_release (ctx, status); + + return (cairo_gl_surface_t *) cairo_surface_reference (&scratches[0]->base); +} |