summaryrefslogtreecommitdiff
path: root/src/cairo-gl-filters.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-gl-filters.c')
-rwxr-xr-xsrc/cairo-gl-filters.c416
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);
+}