Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-blur-effect.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010  Intel Corporation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Author:
22  *   Emmanuele Bassi <ebassi@linux.intel.com>
23  */
24
25 /**
26  * SECTION:clutter-blur-effect
27  * @short_description: A blur effect
28  * @see_also: #ClutterEffect, #ClutterOffscreenEffect
29  *
30  * #ClutterBlurEffect is a sub-class of #ClutterEffect that allows blurring a
31  * actor and its contents.
32  *
33  * #ClutterBlurEffect is available since Clutter 1.4
34  */
35
36 #define CLUTTER_BLUR_EFFECT_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass))
37 #define CLUTTER_IS_BLUR_EFFECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BLUR_EFFECT))
38 #define CLUTTER_BLUR_EFFECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass))
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #define CLUTTER_ENABLE_EXPERIMENTAL_API
45
46 #include "clutter-blur-effect.h"
47
48 #include "cogl/cogl.h"
49
50 #include "clutter-debug.h"
51 #include "clutter-offscreen-effect.h"
52 #include "clutter-private.h"
53
54 #define BLUR_PADDING    2
55
56 /* FIXME - lame shader; we should really have a decoupled
57  * horizontal/vertical two pass shader for the gaussian blur
58  */
59 static const gchar *box_blur_glsl_declarations =
60 "uniform vec2 pixel_step;\n";
61 /* FIXME: Is this shader right? It is doing 10 samples (ie, sampling
62    the middle texel twice) and then only dividing by 9 */
63 #define SAMPLE(offx, offy) \
64   "cogl_texel += texture2D (cogl_sampler, cogl_tex_coord.st + pixel_step * " \
65   "vec2 (" G_STRINGIFY (offx) ", " G_STRINGIFY (offy) ") * 2.0);\n"
66 static const gchar *box_blur_glsl_shader =
67 "  cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n"
68   SAMPLE (-1.0, -1.0)
69   SAMPLE ( 0.0, -1.0)
70   SAMPLE (+1.0, -1.0)
71   SAMPLE (-1.0,  0.0)
72   SAMPLE ( 0.0,  0.0)
73   SAMPLE (+1.0,  0.0)
74   SAMPLE (-1.0, +1.0)
75   SAMPLE ( 0.0, +1.0)
76   SAMPLE (+1.0, +1.0)
77 "  cogl_texel /= 9.0;\n";
78 #undef SAMPLE
79
80 struct _ClutterBlurEffect
81 {
82   ClutterOffscreenEffect parent_instance;
83
84   /* a back pointer to our actor, so that we can query it */
85   ClutterActor *actor;
86
87   gint pixel_step_uniform;
88
89   gint tex_width;
90   gint tex_height;
91
92   CoglPipeline *pipeline;
93 };
94
95 struct _ClutterBlurEffectClass
96 {
97   ClutterOffscreenEffectClass parent_class;
98
99   CoglPipeline *base_pipeline;
100 };
101
102 G_DEFINE_TYPE (ClutterBlurEffect,
103                clutter_blur_effect,
104                CLUTTER_TYPE_OFFSCREEN_EFFECT);
105
106 static gboolean
107 clutter_blur_effect_pre_paint (ClutterEffect *effect)
108 {
109   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
110   ClutterEffectClass *parent_class;
111
112   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
113     return FALSE;
114
115   self->actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
116   if (self->actor == NULL)
117     return FALSE;
118
119   if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
120     {
121       /* if we don't have support for GLSL shaders then we
122        * forcibly disable the ActorMeta
123        */
124       g_warning ("Unable to use the ShaderEffect: the graphics hardware "
125                  "or the current GL driver does not implement support "
126                  "for the GLSL shading language.");
127       clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
128       return FALSE;
129     }
130
131   parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class);
132   if (parent_class->pre_paint (effect))
133     {
134       ClutterOffscreenEffect *offscreen_effect =
135         CLUTTER_OFFSCREEN_EFFECT (effect);
136       CoglHandle texture;
137
138       texture = clutter_offscreen_effect_get_texture (offscreen_effect);
139       self->tex_width = cogl_texture_get_width (texture);
140       self->tex_height = cogl_texture_get_height (texture);
141
142       if (self->pixel_step_uniform > -1)
143         {
144           gfloat pixel_step[2];
145
146           pixel_step[0] = 1.0f / self->tex_width;
147           pixel_step[1] = 1.0f / self->tex_height;
148
149           cogl_pipeline_set_uniform_float (self->pipeline,
150                                            self->pixel_step_uniform,
151                                            2, /* n_components */
152                                            1, /* count */
153                                            pixel_step);
154         }
155
156       cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
157
158       return TRUE;
159     }
160   else
161     return FALSE;
162 }
163
164 static void
165 clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect)
166 {
167   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect);
168   guint8 paint_opacity;
169
170   paint_opacity = clutter_actor_get_paint_opacity (self->actor);
171
172   cogl_pipeline_set_color4ub (self->pipeline,
173                               paint_opacity,
174                               paint_opacity,
175                               paint_opacity,
176                               paint_opacity);
177   cogl_push_source (self->pipeline);
178
179   cogl_rectangle (0, 0, self->tex_width, self->tex_height);
180
181   cogl_pop_source ();
182 }
183
184 static gboolean
185 clutter_blur_effect_get_paint_volume (ClutterEffect      *effect,
186                                       ClutterPaintVolume *volume)
187 {
188   gfloat cur_width, cur_height;
189   ClutterVertex origin;
190
191   clutter_paint_volume_get_origin (volume, &origin);
192   cur_width = clutter_paint_volume_get_width (volume);
193   cur_height = clutter_paint_volume_get_height (volume);
194
195   origin.x -= BLUR_PADDING;
196   origin.y -= BLUR_PADDING;
197   cur_width += 2 * BLUR_PADDING;
198   cur_height += 2 * BLUR_PADDING;
199   clutter_paint_volume_set_origin (volume, &origin);
200   clutter_paint_volume_set_width (volume, cur_width);
201   clutter_paint_volume_set_height (volume, cur_height);
202
203   return TRUE;
204 }
205
206 static void
207 clutter_blur_effect_dispose (GObject *gobject)
208 {
209   ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject);
210
211   if (self->pipeline != NULL)
212     {
213       cogl_object_unref (self->pipeline);
214       self->pipeline = NULL;
215     }
216
217   G_OBJECT_CLASS (clutter_blur_effect_parent_class)->dispose (gobject);
218 }
219
220 static void
221 clutter_blur_effect_class_init (ClutterBlurEffectClass *klass)
222 {
223   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
224   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
225   ClutterOffscreenEffectClass *offscreen_class;
226
227   gobject_class->dispose = clutter_blur_effect_dispose;
228
229   effect_class->pre_paint = clutter_blur_effect_pre_paint;
230   effect_class->get_paint_volume = clutter_blur_effect_get_paint_volume;
231
232   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
233   offscreen_class->paint_target = clutter_blur_effect_paint_target;
234 }
235
236 static void
237 clutter_blur_effect_init (ClutterBlurEffect *self)
238 {
239   ClutterBlurEffectClass *klass = CLUTTER_BLUR_EFFECT_GET_CLASS (self);
240
241   if (G_UNLIKELY (klass->base_pipeline == NULL))
242     {
243       CoglSnippet *snippet;
244       CoglContext *ctx =
245         clutter_backend_get_cogl_context (clutter_get_default_backend ());
246
247       klass->base_pipeline = cogl_pipeline_new (ctx);
248
249       snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP,
250                                   box_blur_glsl_declarations,
251                                   NULL);
252       cogl_snippet_set_replace (snippet, box_blur_glsl_shader);
253       cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet);
254       cogl_object_unref (snippet);
255
256       cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
257                                             0, /* layer number */
258                                             COGL_TEXTURE_TYPE_2D);
259     }
260
261   self->pipeline = cogl_pipeline_copy (klass->base_pipeline);
262
263   self->pixel_step_uniform =
264     cogl_pipeline_get_uniform_location (self->pipeline, "pixel_step");
265 }
266
267 /**
268  * clutter_blur_effect_new:
269  *
270  * Creates a new #ClutterBlurEffect to be used with
271  * clutter_actor_add_effect()
272  *
273  * Return value: the newly created #ClutterBlurEffect or %NULL
274  *
275  * Since: 1.4
276  */
277 ClutterEffect *
278 clutter_blur_effect_new (void)
279 {
280   return g_object_new (CLUTTER_TYPE_BLUR_EFFECT, NULL);
281 }