Release Clutter 1.11.4 (snapshot)
[profile/ivi/clutter.git] / clutter / clutter-brightness-contrast-effect.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010-2012 Inclusive Design Research Centre, OCAD University.
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  *   Joseph Scheuhammer <clown@alum.mit.edu>
23  */
24
25 /**
26  * SECTION:clutter-brightness-contrast-effect
27  * @short_description: Increase/decrease brightness and/or contrast of actor.
28  * @see_also: #ClutterEffect, #ClutterOffscreenEffect
29  *
30  * #ClutterBrightnessContrastEffect is a sub-class of #ClutterEffect that
31  * changes the overall brightness of a #ClutterActor.
32  *
33  * #ClutterBrightnessContrastEffect is available since Clutter 1.10
34  */
35
36 #define CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, ClutterBrightnessContrastEffectClass))
37 #define CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT))
38 #define CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, ClutterBrightnessContrastEffectClass))
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <math.h>
45
46 #define CLUTTER_ENABLE_EXPERIMENTAL_API
47
48 #include "clutter-brightness-contrast-effect.h"
49
50 #include <cogl/cogl.h>
51
52 #include "clutter-debug.h"
53 #include "clutter-enum-types.h"
54 #include "clutter-offscreen-effect.h"
55 #include "clutter-private.h"
56
57 struct _ClutterBrightnessContrastEffect
58 {
59   ClutterOffscreenEffect parent_instance;
60
61   /* Brightness and contrast changes. */
62   gfloat brightness_red;
63   gfloat brightness_green;
64   gfloat brightness_blue;
65
66   gfloat contrast_red;
67   gfloat contrast_green;
68   gfloat contrast_blue;
69
70   gint brightness_multiplier_uniform;
71   gint brightness_offset_uniform;
72   gint contrast_uniform;
73
74   gint tex_width;
75   gint tex_height;
76
77   CoglPipeline *pipeline;
78 };
79
80 struct _ClutterBrightnessContrastEffectClass
81 {
82   ClutterOffscreenEffectClass parent_class;
83
84   CoglPipeline *base_pipeline;
85 };
86
87 /* Brightness effects in GLSL.
88  */
89 static const gchar *brightness_contrast_decls =
90   "uniform vec3 brightness_multiplier;\n"
91   "uniform vec3 brightness_offset;\n"
92   "uniform vec3 contrast;\n";
93
94 static const gchar *brightness_contrast_source =
95   /* Apply the brightness. The brightness_offset is multiplied by the
96      alpha to keep the color pre-multiplied */
97   "cogl_color_out.rgb = (cogl_color_out.rgb * brightness_multiplier +\n"
98   "                      brightness_offset * cogl_color_out.a);\n"
99   /* Apply the contrast */
100   "cogl_color_out.rgb = ((cogl_color_out.rgb - 0.5 * cogl_color_out.a) *\n"
101   "                      contrast + 0.5 * cogl_color_out.a);\n";
102
103 static const ClutterColor no_brightness_change = { 0x7f, 0x7f, 0x7f, 0xff };
104 static const ClutterColor no_contrast_change = { 0x7f, 0x7f, 0x7f, 0xff };
105 static const gfloat no_change = 0.0f;
106
107 enum
108 {
109   PROP_0,
110
111   PROP_BRIGHTNESS,
112   PROP_CONTRAST,
113
114   PROP_LAST
115 };
116
117 static GParamSpec *obj_props[PROP_LAST];
118
119 G_DEFINE_TYPE (ClutterBrightnessContrastEffect,
120                clutter_brightness_contrast_effect,
121                CLUTTER_TYPE_OFFSCREEN_EFFECT);
122
123 static gboolean
124 clutter_brightness_contrast_effect_pre_paint (ClutterEffect *effect)
125 {
126   ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (effect);
127   ClutterEffectClass *parent_class;
128
129   if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
130     return FALSE;
131
132   if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
133     {
134       /* if we don't have support for GLSL shaders then we
135        * forcibly disable the ActorMeta
136        */
137       g_warning ("Unable to use the ClutterBrightnessContrastEffect: the "
138                  "graphics hardware or the current GL driver does not "
139                  "implement support for the GLSL shading language. The "
140                  "effect will be disabled.");
141       clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
142       return FALSE;
143     }
144
145   parent_class =
146     CLUTTER_EFFECT_CLASS (clutter_brightness_contrast_effect_parent_class);
147   if (parent_class->pre_paint (effect))
148     {
149       ClutterOffscreenEffect *offscreen_effect =
150         CLUTTER_OFFSCREEN_EFFECT (effect);
151       CoglHandle texture;
152
153       texture = clutter_offscreen_effect_get_texture (offscreen_effect);
154       self->tex_width = cogl_texture_get_width (texture);
155       self->tex_height = cogl_texture_get_height (texture);
156
157       cogl_pipeline_set_layer_texture (self->pipeline, 0, texture);
158
159       return TRUE;
160     }
161   else
162     return FALSE;
163 }
164
165 static void
166 clutter_brightness_contrast_effect_paint_target (ClutterOffscreenEffect *effect)
167 {
168   ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (effect);
169   ClutterActor *actor;
170   guint8 paint_opacity;
171
172   actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
173   paint_opacity = clutter_actor_get_paint_opacity (actor);
174
175   cogl_pipeline_set_color4ub (self->pipeline,
176                               paint_opacity,
177                               paint_opacity,
178                               paint_opacity,
179                               paint_opacity);
180   cogl_push_source (self->pipeline);
181
182   cogl_rectangle (0, 0, self->tex_width, self->tex_height);
183
184   cogl_pop_source ();
185 }
186
187 static void
188 clutter_brightness_contrast_effect_dispose (GObject *gobject)
189 {
190   ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject);
191
192   if (self->pipeline != NULL)
193     {
194       cogl_object_unref (self->pipeline);
195       self->pipeline = NULL;
196     }
197
198   G_OBJECT_CLASS (clutter_brightness_contrast_effect_parent_class)->dispose (gobject);
199 }
200
201 static void
202 clutter_brightness_contrast_effect_set_property (GObject      *gobject,
203                                                  guint        prop_id,
204                                                  const GValue *value,
205                                                  GParamSpec   *pspec)
206 {
207   ClutterBrightnessContrastEffect *effect = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject);
208
209   switch (prop_id)
210     {
211     case PROP_BRIGHTNESS:
212       {
213         const ClutterColor *color = clutter_value_get_color (value);
214         clutter_brightness_contrast_effect_set_brightness_full (effect,
215                                                                 color->red / 127.0f - 1.0f,
216                                                                 color->green / 127.0f - 1.0f,
217                                                                 color->blue / 127.0f - 1.0f);
218       }
219       break;
220
221     case PROP_CONTRAST:
222       {
223         const ClutterColor *color = clutter_value_get_color (value);
224         clutter_brightness_contrast_effect_set_contrast_full (effect,
225                                                               color->red / 127.0f - 1.0f,
226                                                               color->green / 127.0f - 1.0f,
227                                                               color->blue / 127.0f - 1.0f);
228       }
229       break;
230
231     default:
232       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
233       break;
234     }
235 }
236
237 static void
238 clutter_brightness_contrast_effect_get_property (GObject    *gobject,
239                                                  guint      prop_id,
240                                                  GValue     *value,
241                                                  GParamSpec *pspec)
242 {
243   ClutterBrightnessContrastEffect *effect = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject);
244   ClutterColor color;
245
246   switch (prop_id)
247     {
248     case PROP_BRIGHTNESS:
249       {
250         color.red = (effect->brightness_red + 1.0f) * 127.0f;
251         color.green = (effect->brightness_green + 1.0f) * 127.0f;
252         color.blue = (effect->brightness_blue + 1.0f) * 127.0f;
253         color.alpha = 0xff;
254
255         clutter_value_set_color (value, &color);
256       }
257       break;
258
259     case PROP_CONTRAST:
260       {
261         color.red = (effect->contrast_red + 1.0f) * 127.0f;
262         color.green = (effect->contrast_green + 1.0f) * 127.0f;
263         color.blue = (effect->contrast_blue + 1.0f) * 127.0f;
264         color.alpha = 0xff;
265
266         clutter_value_set_color (value, &color);
267       }
268       break;
269
270     default:
271       G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
272       break;
273     }
274 }
275
276 static void
277 clutter_brightness_contrast_effect_class_init (ClutterBrightnessContrastEffectClass *klass)
278 {
279   ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
280   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
281   ClutterOffscreenEffectClass *offscreen_class;
282
283   offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
284   offscreen_class->paint_target = clutter_brightness_contrast_effect_paint_target;
285
286   effect_class->pre_paint = clutter_brightness_contrast_effect_pre_paint;
287
288   gobject_class->set_property = clutter_brightness_contrast_effect_set_property;
289   gobject_class->get_property = clutter_brightness_contrast_effect_get_property;
290   gobject_class->dispose = clutter_brightness_contrast_effect_dispose;
291
292   /**
293    * ClutterBrightnessContrastEffect:brightness:
294    *
295    * The brightness change to apply to the effect.
296    *
297    * This property uses a #ClutterColor to represent the changes to each
298    * color channel. The range is [ 0, 255 ], with 127 as the value used
299    * to indicate no change; values smaller than 127 indicate a decrease
300    * in brightness, and values larger than 127 indicate an increase in
301    * brightness.
302    *
303    * Since: 1.10
304    */
305   obj_props[PROP_BRIGHTNESS] =
306     clutter_param_spec_color ("brightness",
307                               P_("Brightness"),
308                               P_("The brightness change to apply"),
309                               &no_brightness_change,
310                               CLUTTER_PARAM_READWRITE);
311
312   /**
313    * ClutterBrightnessContrastEffect:contrast:
314    *
315    * The contrast change to apply to the effect.
316    *
317    * This property uses a #ClutterColor to represent the changes to each
318    * color channel. The range is [ 0, 255 ], with 127 as the value used
319    * to indicate no change; values smaller than 127 indicate a decrease
320    * in contrast, and values larger than 127 indicate an increase in
321    * contrast.
322    *
323    * Since: 1.10
324    */
325   obj_props[PROP_CONTRAST] =
326     clutter_param_spec_color ("contrast",
327                               P_("Contrast"),
328                               P_("The contrast change to apply"),
329                               &no_contrast_change,
330                               CLUTTER_PARAM_READWRITE);
331
332   g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
333 }
334
335 static void
336 get_brightness_values (gfloat  value,
337                        gfloat *multiplier,
338                        gfloat *offset)
339 {
340   if (value < 0.0f)
341     {
342       *multiplier = 1.0f + value;
343       *offset = 0.0f;
344     }
345   else
346     {
347       *multiplier = 1.0f - value;
348       *offset = value;
349     }
350 }
351
352 static inline void
353 update_uniforms (ClutterBrightnessContrastEffect *self)
354 {
355   if (self->brightness_multiplier_uniform > -1 &&
356       self->brightness_offset_uniform > -1)
357     {
358       float brightness_multiplier[3];
359       float brightness_offset[3];
360
361       get_brightness_values (self->brightness_red,
362                              brightness_multiplier + 0,
363                              brightness_offset + 0);
364       get_brightness_values (self->brightness_green,
365                              brightness_multiplier + 1,
366                              brightness_offset + 1);
367       get_brightness_values (self->brightness_blue,
368                              brightness_multiplier + 2,
369                              brightness_offset + 2);
370
371       cogl_pipeline_set_uniform_float (self->pipeline,
372                                        self->brightness_multiplier_uniform,
373                                        3, /* n_components */
374                                        1, /* count */
375                                        brightness_multiplier);
376       cogl_pipeline_set_uniform_float (self->pipeline,
377                                        self->brightness_offset_uniform,
378                                        3, /* n_components */
379                                        1, /* count */
380                                        brightness_offset);
381     }
382
383   if (self->contrast_uniform > -1)
384     {
385       float contrast[3] = {
386         tan ((self->contrast_red + 1) * G_PI_4),
387         tan ((self->contrast_green + 1) * G_PI_4),
388         tan ((self->contrast_blue + 1) * G_PI_4)
389       };
390
391       cogl_pipeline_set_uniform_float (self->pipeline,
392                                        self->contrast_uniform,
393                                        3, /* n_components */
394                                        1, /* count */
395                                        contrast);
396     }
397 }
398
399 static void
400 clutter_brightness_contrast_effect_init (ClutterBrightnessContrastEffect *self)
401 {
402   ClutterBrightnessContrastEffectClass *klass;
403
404   self->brightness_red = no_change;
405   self->brightness_green = no_change;
406   self->brightness_blue = no_change;
407
408   self->contrast_red = no_change;
409   self->contrast_green = no_change;
410   self->contrast_blue = no_change;
411
412   klass = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_GET_CLASS (self);
413
414   if (G_UNLIKELY (klass->base_pipeline == NULL))
415     {
416       CoglSnippet *snippet;
417       CoglContext *ctx =
418         clutter_backend_get_cogl_context (clutter_get_default_backend ());
419
420       klass->base_pipeline = cogl_pipeline_new (ctx);
421
422       snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT,
423                                   brightness_contrast_decls,
424                                   brightness_contrast_source);
425       cogl_pipeline_add_snippet (klass->base_pipeline, snippet);
426       cogl_object_unref (snippet);
427
428       cogl_pipeline_set_layer_null_texture (klass->base_pipeline,
429                                             0, /* layer number */
430                                             COGL_TEXTURE_TYPE_2D);
431     }
432
433   self->pipeline = cogl_pipeline_copy (klass->base_pipeline);
434
435   self->brightness_multiplier_uniform =
436     cogl_pipeline_get_uniform_location (self->pipeline,
437                                         "brightness_multiplier");
438   self->brightness_offset_uniform =
439     cogl_pipeline_get_uniform_location (self->pipeline,
440                                         "brightness_offset");
441   self->contrast_uniform =
442     cogl_pipeline_get_uniform_location (self->pipeline, "contrast");
443
444   update_uniforms (self);
445 }
446
447 /**
448  * clutter_brightness_contrast_effect_new:
449  *
450  * Creates a new #ClutterBrightnessContrastEffect to be used with
451  * clutter_actor_add_effect()
452  *
453  * Return value: (transfer full): the newly created
454  *   #ClutterBrightnessContrastEffect or %NULL.  Use g_object_unref() when
455  *   done.
456  *
457  * Since: 1.10
458  */
459 ClutterEffect *
460 clutter_brightness_contrast_effect_new (void)
461 {
462   return g_object_new (CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, NULL);
463 }
464
465 /**
466  * clutter_brightness_contrast_effect_set_brightness_full:
467  * @effect: a #ClutterBrightnessContrastEffect
468  * @red: red component of the change in brightness
469  * @green: green component of the change in brightness
470  * @blue: blue component of the change in brightness
471  *
472  * The range for each component is [-1.0, 1.0] where 0.0 designates no change,
473  * values below 0.0 mean a decrease in brightness, and values above indicate
474  * an increase.
475  *
476  * Since: 1.10
477  */
478 void
479 clutter_brightness_contrast_effect_set_brightness_full (ClutterBrightnessContrastEffect *effect,
480                                                         gfloat                           red,
481                                                         gfloat                           green,
482                                                         gfloat                           blue)
483 {
484   g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect));
485
486   if (red == effect->brightness_red &&
487       green == effect->brightness_green &&
488       blue == effect->brightness_blue)
489     return;
490
491   effect->brightness_red = red;
492   effect->brightness_green = green;
493   effect->brightness_blue = blue;
494
495   update_uniforms (effect);
496
497   clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
498
499   g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_BRIGHTNESS]);
500 }
501
502 /**
503  * clutter_brightness_contrast_effect_get_brightness:
504  * @effect: a #ClutterBrightnessContrastEffect
505  * @red: (out) (allow-none): return location for red component of the
506  *    change in brightness
507  * @green: (out) (allow-none): return location for green component of the
508  *    change in brightness
509  * @blue: (out) (allow-none): return location for blue component of the
510  *    change in brightness
511  *
512  * Retrieves the change in brightness used by @effect.
513  *
514  * Since: 1.10
515  */
516 void
517 clutter_brightness_contrast_effect_get_brightness (ClutterBrightnessContrastEffect *effect,
518                                                    gfloat                          *red,
519                                                    gfloat                          *green,
520                                                    gfloat                          *blue)
521 {
522   g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect));
523
524   if (red != NULL)
525     *red = effect->brightness_red;
526
527   if (green != NULL)
528     *green = effect->brightness_green;
529
530   if (blue != NULL)
531     *blue = effect->brightness_blue;
532 }
533
534 /**
535  * clutter_brightness_contrast_effect_set_brightness:
536  * @effect: a #ClutterBrightnessContrastEffect
537  * @brightness:  the brightness change for all three components (r, g, b)
538  *
539  * The range of @brightness is [-1.0, 1.0], where 0.0 designates no change;
540  * a value below 0.0 indicates a decrease in brightness; and a value
541  * above 0.0 indicates an increase of brightness.
542  *
543  * Since: 1.10
544  */
545 void
546 clutter_brightness_contrast_effect_set_brightness (ClutterBrightnessContrastEffect *effect,
547                                                    gfloat                           brightness)
548 {
549   clutter_brightness_contrast_effect_set_brightness_full (effect,
550                                                           brightness,
551                                                           brightness,
552                                                           brightness);
553 }
554
555 /**
556  * clutter_brightness_contrast_effect_set_contrast_full:
557  * @effect: a #ClutterBrightnessContrastEffect
558  * @red: red component of the change in contrast
559  * @green: green component of the change in contrast
560  * @blue: blue component of the change in contrast
561  *
562  * The range for each component is [-1.0, 1.0] where 0.0 designates no change,
563  * values below 0.0 mean a decrease in contrast, and values above indicate
564  * an increase.
565  *
566  * Since: 1.10
567  */
568 void
569 clutter_brightness_contrast_effect_set_contrast_full (ClutterBrightnessContrastEffect *effect,
570                                                       gfloat                          red,
571                                                       gfloat                          green,
572                                                       gfloat                          blue)
573 {
574   g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect));
575
576   if (red == effect->contrast_red &&
577       green == effect->contrast_green &&
578       blue == effect->contrast_blue)
579     return;
580
581   effect->contrast_red = red;
582   effect->contrast_green = green;
583   effect->contrast_blue = blue;
584
585   update_uniforms (effect);
586
587   clutter_effect_queue_repaint (CLUTTER_EFFECT (effect));
588
589   g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_CONTRAST]);
590 }
591
592 /**
593  * clutter_brightness_contrast_effect_get_contrast:
594  * @effect: a #ClutterBrightnessContrastEffect
595  * @red: (out) (allow-none): return location for red component of the
596  *    change in contrast
597  * @green: (out) (allow-none): return location for green component of the
598  *    change in contrast
599  * @blue: (out) (allow-none): return location for blue component of the
600  *    change in contrast
601  *
602  * Retrieves the contrast value used by @effect.
603  *
604  * Since: 1.10
605  */
606 void
607 clutter_brightness_contrast_effect_get_contrast (ClutterBrightnessContrastEffect *effect,
608                                                  gfloat                          *red,
609                                                  gfloat                          *green,
610                                                  gfloat                          *blue)
611 {
612   g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect));
613
614   if (red != NULL)
615     *red = effect->contrast_red;
616
617   if (green != NULL)
618     *green = effect->contrast_green;
619
620   if (blue != NULL)
621     *blue = effect->contrast_blue;
622 }
623
624 /**
625  * clutter_brightness_contrast_effect_set_contrast:
626  * @effect: a #ClutterBrightnessContrastEffect
627  * @contrast: contrast change for all three channels
628  *
629  * The range for @contrast is [-1.0, 1.0], where 0.0 designates no change;
630  * a value below 0.0 indicates a decrease in contrast; and a value above
631  * 0.0 indicates an increase.
632  *
633  * Since: 1.10
634  */
635 void
636 clutter_brightness_contrast_effect_set_contrast (ClutterBrightnessContrastEffect *effect,
637                                                  gfloat                           contrast)
638 {
639   clutter_brightness_contrast_effect_set_contrast_full (effect,
640                                                         contrast,
641                                                         contrast,
642                                                         contrast);
643 }