1 /* cairo - a vector graphics library with display and print output
3 * Copyright © 2009 Eric Anholt
4 * Copyright © 2009 Chris Wilson
5 * Copyright © 2005,2010 Red Hat, Inc
6 * Copyright © 2011 Intel Corporation
8 * This library is free software; you can redistribute it and/or
9 * modify it either under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation
11 * (the "LGPL") or, at your option, under the terms of the Mozilla
12 * Public License Version 1.1 (the "MPL"). If you do not alter this
13 * notice, a recipient may use your version of this file under either
14 * the MPL or the LGPL.
16 * You should have received a copy of the LGPL along with this library
17 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
19 * You should have received a copy of the MPL along with this library
20 * in the file COPYING-MPL-1.1
22 * The contents of this file are subject to the Mozilla Public License
23 * Version 1.1 (the "License"); you may not use this file except in
24 * compliance with the License. You may obtain a copy of the License at
25 * http://www.mozilla.org/MPL/
27 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
28 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
29 * the specific language governing rights and limitations.
31 * The Original Code is the cairo graphics library.
33 * The Initial Developer of the Original Code is Red Hat, Inc.
36 * Benjamin Otte <otte@gnome.org>
37 * Carl Worth <cworth@cworth.org>
38 * Chris Wilson <chris@chris-wilson.co.uk>
39 * Eric Anholt <eric@anholt.net>
44 #include "cairo-gl-private.h"
45 #include "cairo-filters-private.h"
47 static cairo_int_status_t
48 _draw_rect (cairo_gl_context_t *ctx,
49 cairo_gl_composite_t *setup,
50 cairo_rectangle_int_t *rect)
54 quad[0] = quad[2] = rect->x;
55 quad[1] = quad[7] = rect->y;
56 quad[3] = quad[5] = rect->y + rect->height;
57 quad[4] = quad[6] = rect->x + rect->width;
59 return _cairo_gl_composite_emit_int_quad_as_tristrip (ctx, setup, quad);
62 /* stage 0 - shrink image */
63 static cairo_int_status_t
64 gaussian_filter_stage_0 (cairo_surface_pattern_t *pattern,
65 cairo_gl_surface_t *src,
66 cairo_gl_surface_t *dst,
67 int src_width, int src_height,
68 int dst_width, int dst_height)
70 cairo_rectangle_int_t rect;
72 cairo_int_status_t status;
74 src->blur_stage = CAIRO_GL_BLUR_STAGE_0;
75 _cairo_pattern_init_for_surface (pattern, &src->base);
77 cairo_matrix_init_scale (&pattern->base.matrix,
78 (double) src_width / (double) dst_width,
79 (double) src_height / (double) dst_height);
83 rect.width = dst_width + 1;
84 rect.height = dst_height + 1;
85 clip = _cairo_clip_intersect_rectangle (NULL, &rect);
87 status = _cairo_surface_paint (&dst->base,
88 CAIRO_OPERATOR_SOURCE,
89 &pattern->base, clip);
91 _cairo_clip_destroy (clip);
92 status = _cairo_gl_surface_resolve_multisampling (dst);
97 /* x-axis pass to scratches[1] */
98 static cairo_int_status_t
99 gaussian_filter_stage_1 (cairo_bool_t x_axis,
100 const cairo_surface_pattern_t *original_pattern,
101 cairo_surface_pattern_t *pattern,
102 cairo_gl_surface_t *src,
103 cairo_gl_surface_t *dst,
104 int dst_width, int dst_height,
105 cairo_bool_t is_opaque,
106 cairo_gl_context_t **ctx)
109 cairo_gl_composite_t setup;
110 cairo_gl_context_t *ctx_out = NULL;
111 cairo_rectangle_int_t rect;
112 cairo_int_status_t status;
114 src->image_content_scale_x = (double) dst_width / (double) src->width;
115 src->image_content_scale_y = (double) dst_height / (double) src->height;
116 row = original_pattern->base.y_radius * 2 + 1;
117 col = original_pattern->base.x_radius * 2 + 1;
119 src->blur_stage = CAIRO_GL_BLUR_STAGE_1;
120 _cairo_pattern_init_for_surface (pattern, &src->base);
121 pattern->base.filter = CAIRO_FILTER_GOOD;
124 src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
125 src->operand.pass = 1;
127 memset (&src->operand.texture.coef[0], 0, sizeof (float) * col);
128 compute_x_coef_to_float (original_pattern->base.convolution_matrix,
129 row, col, &src->operand.texture.coef[0]);
131 src->operand.texture.x_radius = original_pattern->base.x_radius;
132 src->operand.texture.y_radius = 1;
135 src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
136 src->operand.pass = 2;
138 memset (&src->operand.texture.coef[0], 0, sizeof (float) * row);
139 compute_y_coef_to_float (original_pattern->base.convolution_matrix,
140 row, col, &src->operand.texture.coef[0]);
142 src->operand.texture.y_radius = original_pattern->base.y_radius;
143 src->operand.texture.x_radius = 1;
147 status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
149 if (unlikely (status))
152 pattern->base.extend = CAIRO_EXTEND_NONE;
153 status = _cairo_gl_composite_set_source (&setup, &pattern->base,
154 NULL, NULL, FALSE, FALSE);
155 if (unlikely (status)) {
156 _cairo_gl_composite_fini (&setup);
160 status = _cairo_gl_composite_begin (&setup, &ctx_out);
161 if (unlikely (status)) {
162 _cairo_gl_composite_fini (&setup);
167 _cairo_gl_shader_bind_float (ctx_out,
168 _cairo_gl_shader_uniform_for_texunit (
169 CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
172 _cairo_gl_shader_bind_float (ctx_out,
173 _cairo_gl_shader_uniform_for_texunit (
174 CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
179 rect.width = dst_width + 1;
180 rect.height = dst_height + 1;
181 status = _draw_rect (ctx_out, &setup, &rect);
182 _cairo_gl_composite_fini (&setup);
183 if (unlikely (status)) {
184 status = _cairo_gl_context_release (ctx_out, status);
189 _cairo_pattern_fini (&pattern->base);
193 /* y-axis pass to scratches[1] */
194 static cairo_int_status_t
195 gaussian_filter_stage_2 (cairo_bool_t y_axis,
196 const cairo_surface_pattern_t *original_pattern,
197 cairo_gl_surface_t *stage_1_src,
198 cairo_gl_surface_t *stage_2_src,
199 int dst_width, int dst_height)
201 cairo_int_status_t status;
204 stage_2_src->image_content_scale_x = (double) dst_width / (double) stage_1_src->width;
205 stage_2_src->image_content_scale_y = (double) dst_height / (double) stage_1_src->height;
208 stage_2_src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
209 stage_2_src->operand.pass = 2;
211 row = original_pattern->base.y_radius * 2 + 1;
212 col = original_pattern->base.x_radius * 2 + 1;
214 memset (&stage_2_src->operand.texture.coef[0], 0, sizeof (float) * row);
215 compute_y_coef_to_float (original_pattern->base.convolution_matrix,
216 row, col, &stage_2_src->operand.texture.coef[2]);
217 stage_2_src->operand.texture.y_radius = original_pattern->base.y_radius;
218 stage_2_src->operand.texture.x_radius = 1;
221 stage_2_src->operand.type = CAIRO_GL_OPERAND_TEXTURE;
223 stage_2_src->blur_stage = CAIRO_GL_BLUR_STAGE_2;
225 status = _cairo_gl_surface_resolve_multisampling (stage_2_src);
227 return CAIRO_INT_STATUS_SUCCESS;
230 /* out_extents should be populated by caller, and modified by the
234 _cairo_gl_gaussian_filter (cairo_gl_surface_t *dst,
235 const cairo_surface_pattern_t *pattern,
236 cairo_gl_surface_t *src,
237 cairo_rectangle_int_t *extents_out)
239 cairo_gl_surface_t *scratches[2];
240 int src_width, src_height;
242 int scratch_width, scratch_height;
244 cairo_gl_context_t *ctx, *ctx_out;
245 cairo_status_t status;
246 cairo_bool_t skip_stage_0 = FALSE;
247 cairo_gl_operand_type_t saved_type = src->operand.type;
249 cairo_surface_pattern_t temp_pattern;
250 cairo_bool_t is_source;
253 cairo_bool_t is_opaque = FALSE;
255 cairo_content_t content = cairo_surface_get_content (&src->base);
257 if (src->operand.type == CAIRO_GL_OPERAND_GAUSSIAN) {
258 extents_out->x = extents_out->y = 0;
259 extents_out->width = cairo_gl_surface_get_width (&src->base) * src->image_content_scale_x;
260 extents_out->height = cairo_gl_surface_get_height (&src->base) * src->image_content_scale_y;
261 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
264 if (pattern->base.filter != CAIRO_FILTER_GAUSSIAN ||
265 ! pattern->base.convolution_matrix ||
266 ! _cairo_gl_surface_is_texture (src))
267 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
269 if (content == CAIRO_CONTENT_COLOR)
272 src_width = cairo_gl_surface_get_width (&src->base);
273 src_height = cairo_gl_surface_get_height (&src->base);
275 width = src_width / pattern->base.shrink_factor_x;
276 height = src_height / pattern->base.shrink_factor_y;
278 status = _cairo_gl_context_acquire (dst->base.device, &ctx);
279 if (unlikely (status))
280 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
282 for (n = 0; n < 2; n++) {
283 if (ctx->source_scratch_in_use) {
284 scratches[n] = ctx->mask_scratch_surfaces[n];
288 scratches[n] = ctx->source_scratch_surfaces[n];
293 scratch_width = cairo_gl_surface_get_width (&scratches[n]->base);
294 scratch_height = cairo_gl_surface_get_height (&scratches[n]->base);
296 if ((scratch_width < width &&
297 scratch_width < MAX_SCRATCH_SIZE) ||
298 (scratch_height < height &&
299 scratch_height < MAX_SCRATCH_SIZE)) {
300 cairo_surface_destroy (&scratches[n]->base);
303 else if (scratch_width > 4 * width &&
304 scratch_height > 4 * height) {
305 cairo_surface_destroy (&scratches[n]->base);
310 if (! scratches[n]) {
311 scratch_width = scratch_height = MIN_SCRATCH_SIZE;
312 while (scratch_width < width) {
314 if (scratch_width == MAX_SCRATCH_SIZE)
316 else if (scratch_width > MAX_SCRATCH_SIZE) {
317 scratch_width *= 0.5;
322 while (scratch_height < height) {
324 if (scratch_height == MAX_SCRATCH_SIZE)
326 else if (scratch_height > MAX_SCRATCH_SIZE) {
327 scratch_height *= 0.5;
333 (cairo_gl_surface_t *)_cairo_gl_surface_create_scratch (ctx,
334 CAIRO_CONTENT_COLOR_ALPHA,
337 _cairo_surface_release_device_reference (&scratches[n]->base);
340 if (ctx->source_scratch_in_use)
341 ctx->mask_scratch_surfaces[n] = scratches[n];
343 ctx->source_scratch_surfaces[n] = scratches[n];
345 scratches[n]->needs_to_cache = FALSE;
346 scratches[n]->force_no_cache = TRUE;
350 ctx->source_scratch_in_use = TRUE;
352 /* we have created two scratch surfaces */
353 /* shrink surface to scratches[0] */
354 width = src_width / pattern->base.shrink_factor_x;
355 height = src_height / pattern->base.shrink_factor_y;
356 if (pattern->base.shrink_factor_x == 1.0 &&
357 pattern->base.shrink_factor_y == 1.0) {
362 else if (width > scratches[0]->width ||
363 height > scratches[0]->height) {
364 width = scratches[0]->width;
365 height = scratches[0]->height;
368 if (! skip_stage_0) {
369 status = gaussian_filter_stage_0 (&temp_pattern, src,
371 src_width, src_height,
373 _cairo_pattern_fini (&temp_pattern.base);
374 if (unlikely (status))
375 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
378 /* x-axis pass to scratches[1] */
380 status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
381 scratches[0], scratches[1],
382 width, height, is_opaque, &ctx_out);
384 status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
386 width, height, is_opaque, &ctx_out);
387 src->operand.type = saved_type;
391 status = _cairo_gl_context_release (ctx_out, status);
392 if (unlikely (status))
393 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
396 status = gaussian_filter_stage_1 (FALSE, pattern, &temp_pattern,
397 scratches[1], scratches[0],
398 width, height, is_opaque, &ctx_out);
400 status = _cairo_gl_context_release (ctx_out, status);
401 if (unlikely (status))
402 return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
404 status = gaussian_filter_stage_2 (FALSE, pattern,
405 scratches[1], scratches[0],
410 extents_out->width = width;
411 extents_out->height = height;
413 status = _cairo_gl_context_release (ctx, status);
415 return (cairo_gl_surface_t *) cairo_surface_reference (&scratches[0]->base);