Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-desaturate-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-desaturate-effect
27  * @short_description: A desaturation effect
28  * @see_also: #ClutterEffect, #ClutterOffscreenEffect
29  *
30  * #ClutterDesaturateEffect is a sub-class of #ClutterEffect that
31  * desaturates the color of an actor and its contents. The strenght
32  * of the desaturation effect is controllable and animatable through
33  * the #ClutterDesaturateEffect:factor property.
34  *
35  * #ClutterDesaturateEffect is available since Clutter 1.4
36  */
37
38 #define CLUTTER_DESATURATE_EFFECT_CLASS(klass)        (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DESATURATE_EFFECT, ClutterDesaturateEffectClass))
39 #define CLUTTER_IS_DESATURATE_EFFECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DESATURATE_EFFECT))
40 #define CLUTTER_DESATURATE_EFFECT_GET_CLASS(obj)      (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DESATURATE_EFFECT, ClutterDesaturateEffectClass))
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #define CLUTTER_ENABLE_EXPERIMENTAL_API
47
48 #include <math.h>
49
50 #include "clutter-desaturate-effect.h"
51
52 #include "cogl/cogl.h"
53
54 #include "clutter-debug.h"
55 #include "clutter-enum-types.h"
56 #include "clutter-offscreen-effect.h"
57 #include "clutter-private.h"
58
59 struct _ClutterDesaturateEffect
60 {
61   ClutterOffscreenEffect parent_instance;
62
63   /* the desaturation factor, also known as "strength" */
64   gdouble factor;
65
66   gint factor_uniform;
67
68   gint tex_width;
69   gint tex_height;
70
71   CoglPipeline *pipeline;
72 };
73
74 struct _ClutterDesaturateEffectClass
75 {
76   ClutterOffscreenEffectClass parent_class;
77
78   CoglPipeline *base_pipeline;
79 };
80
81 /* the magic gray vec3 has been taken from the NTSC conversion weights
82  * as defined by:
83  *
84  *   "OpenGL Superbible, 4th edition"
85  *   -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel
86  *   Addison-Wesley
87  */
88 static const gchar *desaturate_glsl_declarations =
89   "uniform float factor;\n"
90   "\n"
91   "vec3 desaturate (const vec3 color, const float desaturation)\n"
92   "{\n"
93   "  const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n"
94   "  vec3 gray = vec3 (dot (gray_conv, color));\n"
95   "  return vec3 (mix (color.rgb, gray, desaturation));\n"
96   "}\n";
97
98 static const gchar *desaturate_glsl_source =
99   "  cogl_color_out.rgb = desaturate (cogl_color_out.rgb, factor);\n";
100
101 enum
102 {
103   PROP_0,
104
105   PROP_FACTOR,
106
107   PROP_LAST
108 };
109
110 static GParamSpec *obj_props[PROP_LAST];
111
112 G_DEFINE_TYPE (ClutterDesaturateEffect,
113                clutter_desaturate_effect,
114                CLUTTER_TYPE_OFFSCREEN_EFFECT);
115
116 static gboolean
117 clutter_desaturate_effect_pre_paint (ClutterEffect *effect)
118 {
119   ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect);
120   ClutterEffectClass *parent_class;
121
122   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
123     return FALSE;
124
125   if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
126     {
127       /* if we don't have support for GLSL shaders then we
128        * forcibly disable the ActorMeta
129        */
130       g_warning ("Unable to use the ShaderEffect: the graphics hardware "
131                  "or the current GL driver does not implement support "
132                  "for the GLSL shading language.");
133       clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
134       return FALSE;
135     }
136
137   parent_class = CLUTTER_EFFECT_CLASS (clutter_desaturate_effect_parent_class);
138   if (parent_class->pre_paint (effect))
139     {
140       ClutterOffscreenEffect *offscreen_effect =
141         CLUTTER_OFFSCREEN_EFFECT (effect);
142       CoglHandle texture;
143
144       texture = clutter_offscreen_effect_get_texture (offscreen_effect);
145       self->tex_width = cogl_texture_get_width (texture);
146       self->tex_height = cogl_texture_get_height (texture);
147
148       cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
149
150       return TRUE;
151     }
152   else
153     return FALSE;
154 }
155
156 static void
157 clutter_desaturate_effect_paint_target (ClutterOffscreenEffect *effect)
158 {
159   ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect);
160   ClutterActor *actor;
161   CoglHandle texture;
162   guint8 paint_opacity;
163
164   texture = clutter_offscreen_effect_get_texture (effect);
165   cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
166
167   actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
168   paint_opacity = clutter_actor_get_paint_opacity (actor);
169
170   cogl_pipeline_set_color4ub (self->pipeline,
171                               paint_opacity,
172                               paint_opacity,
173                               paint_opacity,
174                               paint_opacity);
175   cogl_push_source (self->pipeline);
176
177   cogl_rectangle (0, 0,
178                   cogl_texture_get_width (texture),
179                   cogl_texture_get_height (texture));
180
181   cogl_pop_source ();
182 }
183
184 static void
185 clutter_desaturate_effect_dispose (GObject *gobject)
186 {
187   ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (gobject);
188
189   if (self->pipeline != NULL)
190     {
191       cogl_object_unref (self->pipeline);
192       self->pipeline = NULL;
193     }
194
195   G_OBJECT_CLASS (clutter_desaturate_effect_parent_class)->dispose (gobject);
196 }
197
198 static void
199 clutter_desaturate_effect_set_property (GObject      *gobject,
200                                         guint         prop_id,
201                                         const GValue *value,
202                                         GParamSpec   *pspec)
203 {
204   ClutterDesaturateEffect *effect = CLUTTER_DESATURATE_EFFECT (gobject);
205
206   switch (prop_id)
207     {
208     case PROP_FACTOR:
209       clutter_desaturate_effect_set_factor (effect,
210                                             g_value_get_double (value));
211       break;
212
213     default:
214       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
215       break;
216     }
217 }
218
219 static void
220 clutter_desaturate_effect_get_property (GObject    *gobject,
221                                         guint       prop_id,
222                                         GValue     *value,
223                                         GParamSpec *pspec)
224 {
225   ClutterDesaturateEffect *effect = CLUTTER_DESATURATE_EFFECT (gobject);
226
227   switch (prop_id)
228     {
229     case PROP_FACTOR:
230       g_value_set_double (value, effect->factor);
231       break;
232
233     default:
234       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
235       break;
236     }
237 }
238
239 static void
240 update_factor_uniform (ClutterDesaturateEffect *self)
241 {
242   if (self->factor_uniform > -1)
243     cogl_pipeline_set_uniform_1f (self->pipeline,
244                                   self->factor_uniform,
245                                   self->factor);
246 }
247
248 static void
249 clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass)
250 {
251   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
252   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
253   ClutterOffscreenEffectClass *offscreen_class;
254
255   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
256   offscreen_class->paint_target = clutter_desaturate_effect_paint_target;
257
258   effect_class->pre_paint = clutter_desaturate_effect_pre_paint;
259
260   /**
261    * ClutterDesaturateEffect:factor:
262    *
263    * The desaturation factor, between 0.0 (no desaturation) and 1.0 (full
264    * desaturation).
265    *
266    * Since: 1.4
267    */
268   obj_props[PROP_FACTOR] =
269     g_param_spec_double ("factor",
270                          P_("Factor"),
271                          P_("The desaturation factor"),
272                          0.0, 1.0,
273                          1.0,
274                          CLUTTER_PARAM_READWRITE);
275
276   gobject_class->dispose = clutter_desaturate_effect_dispose;
277   gobject_class->set_property = clutter_desaturate_effect_set_property;
278   gobject_class->get_property = clutter_desaturate_effect_get_property;
279
280   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
281 }
282
283 static void
284 clutter_desaturate_effect_init (ClutterDesaturateEffect *self)
285 {
286   ClutterDesaturateEffectClass *klass = CLUTTER_DESATURATE_EFFECT_GET_CLASS (self);
287
288   if (G_UNLIKELY (klass->base_pipeline == NULL))
289     {
290       CoglContext *ctx =
291         clutter_backend_get_cogl_context (clutter_get_default_backend ());
292       CoglSnippet *snippet;
293
294       klass->base_pipeline = cogl_pipeline_new (ctx);
295
296       snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
297                                   desaturate_glsl_declarations,
298                                   desaturate_glsl_source);
299       cogl_pipeline_add_snippet (klass->base_pipeline, snippet);
300       cogl_object_unref (snippet);
301
302       cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
303                                             0, /* layer number */
304                                             COGL_TEXTURE_TYPE_2D);
305     }
306
307   self->pipeline = cogl_pipeline_copy (klass->base_pipeline);
308
309   self->factor_uniform =
310     cogl_pipeline_get_uniform_location (self->pipeline, "factor");
311
312   self->factor = 1.0;
313
314   update_factor_uniform (self);
315 }
316
317 /**
318  * clutter_desaturate_effect_new:
319  * @factor: the desaturation factor, between 0.0 and 1.0
320  *
321  * Creates a new #ClutterDesaturateEffect to be used with
322  * clutter_actor_add_effect()
323  *
324  * Return value: the newly created #ClutterDesaturateEffect or %NULL
325  *
326  * Since: 1.4
327  */
328 ClutterEffect *
329 clutter_desaturate_effect_new (gdouble factor)
330 {
331   g_return_val_if_fail (factor >= 0.0 && factor <= 1.0, NULL);
332
333   return g_object_new (CLUTTER_TYPE_DESATURATE_EFFECT,
334                        "factor", factor,
335                        NULL);
336 }
337
338 /**
339  * clutter_desaturate_effect_set_factor:
340  * @effect: a #ClutterDesaturateEffect
341  * @factor: the desaturation factor, between 0.0 and 1.0
342  *
343  * Sets the desaturation factor for @effect, with 0.0 being "do not desaturate"
344  * and 1.0 being "fully desaturate"
345  *
346  * Since: 1.4
347  */
348 void
349 clutter_desaturate_effect_set_factor (ClutterDesaturateEffect *effect,
350                                       gdouble                  factor)
351 {
352   g_return_if_fail (CLUTTER_IS_DESATURATE_EFFECT (effect));
353   g_return_if_fail (factor >= 0.0 && factor <= 1.0);
354
355   if (fabsf (effect->factor - factor) >= 0.00001)
356     {
357       effect->factor = factor;
358       update_factor_uniform (effect);
359
360       clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
361
362       g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_FACTOR]);
363     }
364 }
365
366 /**
367  * clutter_desaturate_effect_get_factor:
368  * @effect: a #ClutterDesaturateEffect
369  *
370  * Retrieves the desaturation factor of @effect
371  *
372  * Return value: the desaturation factor
373  *
374  * Since: 1.4
375  */
376 gdouble
377 clutter_desaturate_effect_get_factor (ClutterDesaturateEffect *effect)
378 {
379   g_return_val_if_fail (CLUTTER_IS_DESATURATE_EFFECT (effect), 0.0);
380
381   return effect->factor;
382 }