tizen 2.3.1 release
[framework/graphics/cairo.git] / src / cairo-gl-filters.c
1 /* cairo - a vector graphics library with display and print output
2  *
3  * Copyright © 2009 Eric Anholt
4  * Copyright © 2009 Chris Wilson
5  * Copyright © 2005,2010 Red Hat, Inc
6  * Copyright © 2011 Intel Corporation
7  *
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.
15  *
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
21  *
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/
26  *
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.
30  *
31  * The Original Code is the cairo graphics library.
32  *
33  * The Initial Developer of the Original Code is Red Hat, Inc.
34  *
35  * Contributor(s):
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>
40  */
41
42 #include "cairoint.h"
43
44 #include "cairo-gl-private.h"
45 #include "cairo-filters-private.h"
46
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)
51 {
52     int quad[8];
53
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;
58
59     return _cairo_gl_composite_emit_int_quad_as_tristrip (ctx, setup, quad);
60 }
61
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)
69 {
70     cairo_rectangle_int_t rect;
71     cairo_clip_t *clip;
72     cairo_int_status_t status;
73
74     src->blur_stage = CAIRO_GL_BLUR_STAGE_0;
75     _cairo_pattern_init_for_surface (pattern, &src->base);
76
77     cairo_matrix_init_scale (&pattern->base.matrix,
78                         (double) src_width / (double) dst_width,
79                         (double) src_height / (double) dst_height);
80
81     rect.x = 0;
82     rect.y = 0;
83     rect.width = dst_width + 1;
84     rect.height = dst_height + 1;
85     clip = _cairo_clip_intersect_rectangle (NULL, &rect);
86
87     status = _cairo_surface_paint (&dst->base,
88                                    CAIRO_OPERATOR_SOURCE,
89                                    &pattern->base, clip);
90
91     _cairo_clip_destroy (clip);
92     status = _cairo_gl_surface_resolve_multisampling (dst);
93
94     return status;
95 }
96
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)
107 {
108     int row, col;
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;
113
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;
118
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;
122
123     if (x_axis) {
124         src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
125         src->operand.pass = 1;
126
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]);
130
131         src->operand.texture.x_radius = original_pattern->base.x_radius;
132         src->operand.texture.y_radius = 1;
133     }
134     else {
135         src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
136         src->operand.pass = 2;
137
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]);
141
142         src->operand.texture.y_radius = original_pattern->base.y_radius;
143         src->operand.texture.x_radius = 1;
144     }
145
146     *ctx = ctx_out;
147     status = _cairo_gl_composite_init (&setup, CAIRO_OPERATOR_SOURCE,
148                                       dst, FALSE);
149     if (unlikely (status))
150         return status;
151
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);
157         return status;
158     }
159
160     status = _cairo_gl_composite_begin (&setup, &ctx_out);
161     if (unlikely (status)) {
162         _cairo_gl_composite_fini (&setup);
163         return status;
164     }
165
166     if (is_opaque)
167         _cairo_gl_shader_bind_float (ctx_out,
168                                      _cairo_gl_shader_uniform_for_texunit (
169                                         CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
170                                         1.0);
171     else
172         _cairo_gl_shader_bind_float (ctx_out,
173                                      _cairo_gl_shader_uniform_for_texunit (
174                                         CAIRO_GL_UNIFORM_ALPHA, CAIRO_GL_TEX_SOURCE),
175                                         0.0);
176
177     rect.x = 0;
178     rect.y = 0;
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);
185         return status;
186     }
187
188     *ctx = ctx_out;
189     _cairo_pattern_fini (&pattern->base);
190     return status;
191 }
192
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)
200 {
201     cairo_int_status_t status;
202     int row, col;
203
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;
206
207     if (y_axis) {
208         stage_2_src->operand.type = CAIRO_GL_OPERAND_GAUSSIAN;
209         stage_2_src->operand.pass = 2;
210
211         row = original_pattern->base.y_radius * 2 + 1;
212         col = original_pattern->base.x_radius * 2 + 1;
213
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;
219     }
220     else
221         stage_2_src->operand.type = CAIRO_GL_OPERAND_TEXTURE;
222
223     stage_2_src->blur_stage = CAIRO_GL_BLUR_STAGE_2;
224
225     status = _cairo_gl_surface_resolve_multisampling (stage_2_src);
226
227     return CAIRO_INT_STATUS_SUCCESS;
228 }
229
230 /* out_extents should be populated by caller, and modified by the
231  * function
232  */
233 cairo_gl_surface_t *
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)
238 {
239     cairo_gl_surface_t *scratches[2];
240     int src_width, src_height;
241     int width, height;
242     int scratch_width, scratch_height;
243
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;
248
249     cairo_surface_pattern_t temp_pattern;
250     cairo_bool_t is_source;
251
252     int n;
253     cairo_bool_t is_opaque = FALSE;
254
255     cairo_content_t content = cairo_surface_get_content (&src->base);
256
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);
262     }
263
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);
268
269     if (content == CAIRO_CONTENT_COLOR)
270         is_opaque = TRUE;
271
272     src_width = cairo_gl_surface_get_width (&src->base);
273     src_height = cairo_gl_surface_get_height (&src->base);
274
275     width = src_width / pattern->base.shrink_factor_x;
276     height = src_height / pattern->base.shrink_factor_y;
277
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);
281
282     for (n = 0; n < 2; n++) {
283         if (ctx->source_scratch_in_use) {
284             scratches[n] = ctx->mask_scratch_surfaces[n];
285             is_source = FALSE;
286         }
287         else {
288             scratches[n] = ctx->source_scratch_surfaces[n];
289             is_source = TRUE;
290         }
291
292         if (scratches[n]) {
293             scratch_width = cairo_gl_surface_get_width (&scratches[n]->base);
294             scratch_height = cairo_gl_surface_get_height (&scratches[n]->base);
295
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);
301                 scratches[n] = NULL;
302             }
303             else if (scratch_width > 4 * width &&
304                      scratch_height > 4 * height) {
305                 cairo_surface_destroy (&scratches[n]->base);
306                 scratches[n] = NULL;
307             }
308         }
309
310         if (! scratches[n]) {
311             scratch_width = scratch_height = MIN_SCRATCH_SIZE;
312             while (scratch_width < width) {
313                 scratch_width *= 2;
314                 if (scratch_width == MAX_SCRATCH_SIZE)
315                     break;
316                 else if (scratch_width > MAX_SCRATCH_SIZE) {
317                     scratch_width *= 0.5;
318                     break;
319                 }
320             }
321
322             while (scratch_height < height) {
323                 scratch_height *= 2;
324                 if (scratch_height == MAX_SCRATCH_SIZE)
325                     break;
326                 else if (scratch_height > MAX_SCRATCH_SIZE) {
327                     scratch_height *= 0.5;
328                     break;
329                 }
330             }
331
332             scratches[n] =
333                 (cairo_gl_surface_t *)_cairo_gl_surface_create_scratch (ctx,
334                                                         CAIRO_CONTENT_COLOR_ALPHA,
335                                                         scratch_width,
336                                                         scratch_height);
337             _cairo_surface_release_device_reference (&scratches[n]->base);
338         }
339
340         if (ctx->source_scratch_in_use)
341             ctx->mask_scratch_surfaces[n] = scratches[n];
342         else
343             ctx->source_scratch_surfaces[n] = scratches[n];
344
345         scratches[n]->needs_to_cache = FALSE;
346         scratches[n]->force_no_cache = TRUE;
347     }
348
349     if (is_source)
350         ctx->source_scratch_in_use = TRUE;
351
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) {
358         skip_stage_0 = TRUE;
359         width = src_width;
360         height = src_height;
361     }
362     else if (width > scratches[0]->width ||
363              height > scratches[0]->height) {
364         width = scratches[0]->width;
365         height = scratches[0]->height;
366     }
367
368     if (! skip_stage_0) {
369         status = gaussian_filter_stage_0 (&temp_pattern, src,
370                                           scratches[0],
371                                           src_width, src_height,
372                                           width, height);
373         _cairo_pattern_fini (&temp_pattern.base);
374         if (unlikely (status))
375             return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
376     }
377
378     /* x-axis pass to scratches[1] */
379     if (! skip_stage_0)
380         status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
381                                           scratches[0], scratches[1],
382                                           width, height, is_opaque, &ctx_out);
383     else {
384         status = gaussian_filter_stage_1 (TRUE, pattern, &temp_pattern,
385                                           src, scratches[1],
386                                           width, height, is_opaque, &ctx_out);
387         src->operand.type = saved_type;
388     }
389
390     if (ctx_out)
391         status = _cairo_gl_context_release (ctx_out, status);
392     if (unlikely (status))
393         return (cairo_gl_surface_t *)cairo_surface_reference (&src->base);
394
395     /* y-axis pass */
396     status = gaussian_filter_stage_1 (FALSE, pattern, &temp_pattern,
397                                       scratches[1], scratches[0],
398                                       width, height, is_opaque, &ctx_out);
399     if (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);
403
404     status = gaussian_filter_stage_2 (FALSE, pattern,
405                                       scratches[1], scratches[0],
406                                       width, height);
407
408     extents_out->x = 0;
409     extents_out->y = 0;
410     extents_out->width = width;
411     extents_out->height = height;
412
413     status = _cairo_gl_context_release (ctx, status);
414
415     return (cairo_gl_surface_t *) cairo_surface_reference (&scratches[0]->base);
416 }