Merge branch 'wip/framebuffer-bits'
[profile/ivi/clutter.git] / clutter / clutter-color.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /**
25  * SECTION:clutter-color
26  * @short_description: Color management and manipulation.
27  *
28  * #ClutterColor is a simple type for representing colors in Clutter.
29  *
30  * A #ClutterColor is expressed as a 4-tuple of values ranging from
31  * zero to 255, one for each color channel plus one for the alpha.
32  *
33  * The alpha channel is fully opaque at 255 and fully transparent at 0.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <math.h>
41
42 #include <pango/pango-attributes.h>
43
44 #include "clutter-main.h"
45 #include "clutter-color.h"
46 #include "clutter-private.h"
47 #include "clutter-debug.h"
48
49 /**
50  * clutter_color_add:
51  * @a: a #ClutterColor
52  * @b: a #ClutterColor
53  * @result: (out): return location for the result
54  *
55  * Adds @a to @b and saves the resulting color inside @result.
56  *
57  * The alpha channel of @result is set as as the maximum value
58  * between the alpha channels of @a and @b.
59  */
60 void
61 clutter_color_add (const ClutterColor *a,
62                    const ClutterColor *b,
63                    ClutterColor       *result)
64 {
65   g_return_if_fail (a != NULL);
66   g_return_if_fail (b != NULL);
67   g_return_if_fail (result != NULL);
68
69   result->red   = CLAMP (a->red   + b->red,   0, 255);
70   result->green = CLAMP (a->green + b->green, 0, 255);
71   result->blue  = CLAMP (a->blue  + b->blue,  0, 255);
72
73   result->alpha = MAX (a->alpha, b->alpha);
74 }
75
76 /**
77  * clutter_color_subtract:
78  * @a: a #ClutterColor
79  * @b: a #ClutterColor
80  * @result: (out): return location for the result
81  *
82  * Subtracts @b from @a and saves the resulting color inside @result.
83  *
84  * This function assumes that the components of @a are greater than the
85  * components of @b; the result is, otherwise, undefined.
86  *
87  * The alpha channel of @result is set as the minimum value
88  * between the alpha channels of @a and @b.
89  */
90 void
91 clutter_color_subtract (const ClutterColor *a,
92                         const ClutterColor *b,
93                         ClutterColor       *result)
94 {
95   g_return_if_fail (a != NULL);
96   g_return_if_fail (b != NULL);
97   g_return_if_fail (result != NULL);
98
99   result->red   = CLAMP (a->red   - b->red,   0, 255);
100   result->green = CLAMP (a->green - b->green, 0, 255);
101   result->blue  = CLAMP (a->blue  - b->blue,  0, 255);
102
103   result->alpha = MIN (a->alpha, b->alpha);
104 }
105
106 /**
107  * clutter_color_lighten:
108  * @color: a #ClutterColor
109  * @result: (out): return location for the lighter color
110  *
111  * Lightens @color by a fixed amount, and saves the changed color
112  * in @result.
113  */
114 void
115 clutter_color_lighten (const ClutterColor *color,
116                        ClutterColor       *result)
117 {
118   clutter_color_shade (color, 1.3, result);
119 }
120
121 /**
122  * clutter_color_darken:
123  * @color: a #ClutterColor
124  * @result: (out): return location for the darker color
125  *
126  * Darkens @color by a fixed amount, and saves the changed color
127  * in @result.
128  */
129 void
130 clutter_color_darken (const ClutterColor *color,
131                       ClutterColor       *result)
132 {
133   clutter_color_shade (color, 0.7, result);
134 }
135
136 /**
137  * clutter_color_to_hls:
138  * @color: a #ClutterColor
139  * @hue: return location for the hue value or %NULL
140  * @luminance: return location for the luminance value or %NULL
141  * @saturation: return location for the saturation value or %NULL
142  *
143  * Converts @color to the HLS format.
144  *
145  * The @hue value is in the 0 .. 360 range. The @luminance and
146  * @saturation values are in the 0 .. 1 range.
147  */
148 void
149 clutter_color_to_hls (const ClutterColor *color,
150                       float              *hue,
151                       float              *luminance,
152                       float              *saturation)
153 {
154   float red, green, blue;
155   float min, max, delta;
156   float h, l, s;
157   
158   g_return_if_fail (color != NULL);
159
160   red   = color->red / 255.0;
161   green = color->green / 255.0;
162   blue  = color->blue / 255.0;
163
164   if (red > green)
165     {
166       if (red > blue)
167         max = red;
168       else
169         max = blue;
170
171       if (green < blue)
172         min = green;
173       else
174         min = blue;
175     }
176   else
177     {
178       if (green > blue)
179         max = green;
180       else
181         max = blue;
182
183       if (red < blue)
184         min = red;
185       else
186         min = blue;
187     }
188
189   l = (max + min) / 2;
190   s = 0;
191   h = 0;
192
193   if (max != min)
194     {
195       if (l <= 0.5)
196         s = (max - min) / (max + min);
197       else
198         s = (max - min) / (2.0 - max - min);
199
200       delta = max - min;
201
202       if (red == max)
203         h = (green - blue) / delta;
204       else if (green == max)
205         h = 2.0 + (blue - red) / delta;
206       else if (blue == max)
207         h = 4.0 + (red - green) / delta;
208
209       h *= 60;
210
211       if (h < 0)
212         h += 360.0;
213     }
214
215   if (hue)
216     *hue = h;
217
218   if (luminance)
219     *luminance = l;
220
221   if (saturation)
222     *saturation = s;
223 }
224
225 /**
226  * clutter_color_from_hls:
227  * @color: (out): return location for a #ClutterColor
228  * @hue: hue value, in the 0 .. 360 range
229  * @luminance: luminance value, in the 0 .. 1 range
230  * @saturation: saturation value, in the 0 .. 1 range
231  *
232  * Converts a color expressed in HLS (hue, luminance and saturation)
233  * values into a #ClutterColor.
234  */
235 void
236 clutter_color_from_hls (ClutterColor *color,
237                         float         hue,
238                         float         luminance,
239                         float         saturation)
240 {
241   float tmp1, tmp2;
242   float tmp3[3];
243   float clr[3];
244   int   i;
245
246   hue /= 360.0;
247
248   if (saturation == 0)
249     {
250       color->red = color->green = color->blue = (luminance * 255);
251
252       return;
253     }
254
255   if (luminance <= 0.5)
256     tmp2 = luminance * (1.0 + saturation);
257   else
258     tmp2 = luminance + saturation - (luminance * saturation);
259
260   tmp1 = 2.0 * luminance - tmp2;
261
262   tmp3[0] = hue + 1.0 / 3.0;
263   tmp3[1] = hue;
264   tmp3[2] = hue - 1.0 / 3.0;
265
266   for (i = 0; i < 3; i++)
267     {
268       if (tmp3[i] < 0)
269         tmp3[i] += 1.0;
270
271       if (tmp3[i] > 1)
272         tmp3[i] -= 1.0;
273
274       if (6.0 * tmp3[i] < 1.0)
275         clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0;
276       else if (2.0 * tmp3[i] < 1.0)
277         clr[i] = tmp2;
278       else if (3.0 * tmp3[i] < 2.0)
279         clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0);
280       else
281         clr[i] = tmp1;
282     }
283
284   color->red   = floorf (clr[0] * 255.0 + 0.5);
285   color->green = floorf (clr[1] * 255.0 + 0.5);
286   color->blue  = floorf (clr[2] * 255.0 + 0.5);
287 }
288
289 /**
290  * clutter_color_shade:
291  * @color: a #ClutterColor
292  * @factor: the shade factor to apply
293  * @result: (out): return location for the shaded color
294  *
295  * Shades @color by @factor and saves the modified color into @result.
296  */
297 void
298 clutter_color_shade (const ClutterColor *color,
299                      gdouble             factor,
300                      ClutterColor       *result)
301 {
302   float h, l, s;
303
304   g_return_if_fail (color != NULL);
305   g_return_if_fail (result != NULL);
306   
307   clutter_color_to_hls (color, &h, &l, &s);
308
309   l *= factor;
310   if (l > 1.0)
311     l = 1.0;
312   else if (l < 0)
313     l = 0;
314
315   s *= factor;
316   if (s > 1.0)
317     s = 1.0;
318   else if (s < 0)
319     s = 0;
320   
321   clutter_color_from_hls (result, h, l, s);
322
323   result->alpha = color->alpha;
324 }
325
326 /**
327  * clutter_color_to_pixel:
328  * @color: a #ClutterColor
329  *
330  * Converts @color into a packed 32 bit integer, containing
331  * all the four 8 bit channels used by #ClutterColor.
332  *
333  * Return value: a packed color
334  */
335 guint32
336 clutter_color_to_pixel (const ClutterColor *color)
337 {
338   g_return_val_if_fail (color != NULL, 0);
339   
340   return (color->alpha       |
341           color->blue  << 8  |
342           color->green << 16 |
343           color->red   << 24);
344 }
345
346 /**
347  * clutter_color_from_pixel:
348  * @color: (out): return location for a #ClutterColor
349  * @pixel: a 32 bit packed integer containing a color
350  *
351  * Converts @pixel from the packed representation of a four 8 bit channel
352  * color to a #ClutterColor.
353  */
354 void
355 clutter_color_from_pixel (ClutterColor *color,
356                           guint32       pixel)
357 {
358   g_return_if_fail (color != NULL);
359
360   color->red   =  pixel >> 24;
361   color->green = (pixel >> 16) & 0xff;
362   color->blue  = (pixel >> 8)  & 0xff;
363   color->alpha =  pixel        & 0xff;
364 }
365
366 /**
367  * clutter_color_from_string:
368  * @color: (out): return location for a #ClutterColor
369  * @str: a string specifiying a color (named color or #RRGGBBAA)
370  *
371  * Parses a string definition of a color, filling the
372  * <structfield>red</structfield>, <structfield>green</structfield>, 
373  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
374  * channels of @color. If alpha is not specified it will be set full opaque.
375  *
376  * The @color is not allocated.
377  *
378  * The color may be defined by any of the formats understood by
379  * pango_color_from_string(); these include literal color names, like
380  * <literal>Red</literal> or <literal>DarkSlateGray</literal>, or
381  * hexadecimal specifications like <literal>&num;3050b2</literal> or
382  * <literal>&num;333</literal>.
383  *
384  * Return value: %TRUE if parsing succeeded.
385  *
386  * Since: 1.0
387  */
388 gboolean
389 clutter_color_from_string (ClutterColor *color,
390                            const gchar  *str)
391 {
392   PangoColor pango_color = { 0, };
393
394   g_return_val_if_fail (color != NULL, FALSE);
395   g_return_val_if_fail (str != NULL, FALSE);
396
397   /* if the string contains a color encoded using the hexadecimal
398    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
399    * parsing the color ourselves, as we need the alpha channel that
400    * Pango can't retrieve.
401    */
402   if (str[0] == '#')
403     {
404       gint32 result;
405
406       if (sscanf (str + 1, "%x", &result))
407         {
408           gsize length = strlen (str);
409
410           switch (length)
411             {
412             case 9: /* rrggbbaa */
413               color->red   = (result >> 24) & 0xff;
414               color->green = (result >> 16) & 0xff;
415               color->blue  = (result >>  8) & 0xff;
416
417               color->alpha = result & 0xff;
418
419               return TRUE;
420
421             case 7: /* #rrggbb */
422               color->red   = (result >> 16) & 0xff;
423               color->green = (result >>  8) & 0xff;
424               color->blue  = result & 0xff;
425
426               color->alpha = 0xff;
427
428               return TRUE;
429
430             case 5: /* #rgba */
431               color->red   = ((result >> 12) & 0xf);
432               color->green = ((result >>  8) & 0xf);
433               color->blue  = ((result >>  4) & 0xf);
434               color->alpha = result & 0xf;
435
436               color->red   = (color->red   << 4) | color->red;
437               color->green = (color->green << 4) | color->green;
438               color->blue  = (color->blue  << 4) | color->blue;
439               color->alpha = (color->alpha << 4) | color->alpha;
440
441               return TRUE;
442
443             case 4: /* #rgb */
444               color->red   = ((result >>  8) & 0xf);
445               color->green = ((result >>  4) & 0xf);
446               color->blue  = result & 0xf;
447
448               color->red   = (color->red   << 4) | color->red;
449               color->green = (color->green << 4) | color->green;
450               color->blue  = (color->blue  << 4) | color->blue;
451
452               color->alpha = 0xff;
453
454               return TRUE;
455
456             default:
457               /* pass through to Pango */
458               break;
459             }
460         }
461     }
462   
463   /* Fall back to pango for named colors */
464   if (pango_color_parse (&pango_color, str))
465     {
466       color->red   = pango_color.red;
467       color->green = pango_color.green;
468       color->blue  = pango_color.blue;
469
470       color->alpha = 0xff;
471
472       return TRUE;
473     }
474
475   return FALSE;
476 }
477
478 /**
479  * clutter_color_to_string:
480  * @color: a #ClutterColor
481  *
482  * Returns a textual specification of @color in the hexadecimal form
483  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
484  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
485  * hex digits representing the red, green, blue and alpha components
486  * respectively.
487  *
488  * Return value: a newly-allocated text string
489  *
490  * Since: 0.2
491  */
492 gchar *
493 clutter_color_to_string (const ClutterColor *color)
494 {
495   g_return_val_if_fail (color != NULL, NULL);
496
497   return g_strdup_printf ("#%02x%02x%02x%02x",
498                           color->red,
499                           color->green,
500                           color->blue,
501                           color->alpha);
502 }
503
504 /**
505  * clutter_color_equal:
506  * @v1: a #ClutterColor
507  * @v2: a #ClutterColor
508  *
509  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
510  *
511  * This function can be passed to g_hash_table_new() as the @key_equal_func
512  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
513  *
514  * Return value: %TRUE if the two colors are the same.
515  *
516  * Since: 0.2
517  */
518 gboolean
519 clutter_color_equal (gconstpointer v1,
520                      gconstpointer v2)
521 {
522   const ClutterColor *a, *b;
523
524   g_return_val_if_fail (v1 != NULL, FALSE);
525   g_return_val_if_fail (v2 != NULL, FALSE);
526
527   if (v1 == v2)
528     return TRUE;
529
530   a = v1;
531   b = v2;
532
533   return (a->red   == b->red   &&
534           a->green == b->green &&
535           a->blue  == b->blue  &&
536           a->alpha == b->alpha);
537 }
538
539 /**
540  * clutter_color_hash:
541  * @v: a #ClutterColor
542  *
543  * Converts a #ClutterColor to a hash value.
544  *
545  * This function can be passed to g_hash_table_new() as the @hash_func
546  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
547  *
548  * Return value: a hash value corresponding to the color
549  *
550  * Since: 1.0
551  */
552 guint
553 clutter_color_hash (gconstpointer v)
554 {
555   return clutter_color_to_pixel ((const ClutterColor *) v);
556 }
557
558 /**
559  * clutter_color_copy:
560  * @color: a #ClutterColor
561  *
562  * Makes a copy of the color structure.  The result must be
563  * freed using clutter_color_free().
564  *
565  * Return value: an allocated copy of @color.
566  *
567  * Since: 0.2
568  */
569 ClutterColor *
570 clutter_color_copy (const ClutterColor *color)
571 {
572   if (G_LIKELY (color != NULL))
573     return g_slice_dup (ClutterColor, color);
574
575   return NULL;
576 }
577
578 /**
579  * clutter_color_free:
580  * @color: a #ClutterColor
581  *
582  * Frees a color structure created with clutter_color_copy().
583  *
584  * Since: 0.2
585  */
586 void
587 clutter_color_free (ClutterColor *color)
588 {
589   if (G_LIKELY (color != NULL))
590     g_slice_free (ClutterColor, color);
591 }
592
593 /**
594  * clutter_color_new:
595  * @red: red component of the color, between 0 and 255
596  * @green: green component of the color, between 0 and 255
597  * @blue: blue component of the color, between 0 and 255
598  * @alpha: alpha component of the color, between 0 and 255
599  *
600  * Creates a new #ClutterColor with the given values.
601  *
602  * Return value: the newly allocated color. Use clutter_color_free()
603  *   when done
604  *
605  * Since: 0.8.4
606  */
607 ClutterColor *
608 clutter_color_new (guint8 red,
609                    guint8 green,
610                    guint8 blue,
611                    guint8 alpha)
612 {
613   ClutterColor *color;
614
615   color = g_slice_new (ClutterColor);
616
617   color->red   = red;
618   color->green = green;
619   color->blue  = blue;
620   color->alpha = alpha;
621
622   return color;
623 }
624
625 static void
626 clutter_value_transform_color_string (const GValue *src,
627                                       GValue       *dest)
628 {
629   const ClutterColor *color = g_value_get_boxed (src);
630
631   if (color)
632     {
633       gchar *string = clutter_color_to_string (color);
634
635       g_value_take_string (dest, string);
636     }
637   else
638     g_value_set_string (dest, NULL);
639 }
640
641 static void
642 clutter_value_transform_string_color (const GValue *src,
643                                       GValue       *dest)
644 {
645   const char *str = g_value_get_string (src);
646
647   if (str)
648     {
649       ClutterColor color = { 0, };
650
651       clutter_color_from_string (&color, str);
652
653       clutter_value_set_color (dest, &color);
654     }
655   else
656     clutter_value_set_color (dest, NULL);
657 }
658
659 GType
660 clutter_color_get_type (void)
661 {
662   static GType _clutter_color_type = 0;
663   
664   if (G_UNLIKELY (_clutter_color_type == 0))
665     {
666        _clutter_color_type =
667          g_boxed_type_register_static (I_("ClutterColor"),
668                                        (GBoxedCopyFunc) clutter_color_copy,
669                                        (GBoxedFreeFunc) clutter_color_free);
670
671        g_value_register_transform_func (_clutter_color_type, G_TYPE_STRING,
672                                         clutter_value_transform_color_string);
673        g_value_register_transform_func (G_TYPE_STRING, _clutter_color_type,
674                                         clutter_value_transform_string_color);
675     }
676
677   return _clutter_color_type;
678 }
679
680 /**
681  * clutter_value_set_color:
682  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
683  * @color: the color to set
684  *
685  * Sets @value to @color.
686  *
687  * Since: 0.8.4
688  */
689 void
690 clutter_value_set_color (GValue             *value,
691                          const ClutterColor *color)
692 {
693   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
694
695   g_value_set_boxed (value, color);
696 }
697
698 /**
699  * clutter_value_get_color:
700  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
701  *
702  * Gets the #ClutterColor contained in @value.
703  *
704  * Return value: the colors inside the passed #GValue
705  *
706  * Since: 0.8.4
707  */
708 G_CONST_RETURN ClutterColor *
709 clutter_value_get_color (const GValue *value)
710 {
711   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
712
713   return g_value_get_boxed (value);
714 }
715
716 static void
717 param_color_init (GParamSpec *pspec)
718 {
719   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
720
721   cspec->default_value = NULL;
722 }
723
724 static void
725 param_color_finalize (GParamSpec *pspec)
726 {
727   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
728
729   clutter_color_free (cspec->default_value);
730 }
731
732 static void
733 param_color_set_default (GParamSpec *pspec,
734                          GValue     *value)
735 {
736   const ClutterColor *default_value =
737     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
738   clutter_value_set_color (value, default_value);
739 }
740
741 static gint
742 param_color_values_cmp (GParamSpec   *pspec,
743                         const GValue *value1,
744                         const GValue *value2)
745 {
746   const ClutterColor *color1 = g_value_get_boxed (value1);
747   const ClutterColor *color2 = g_value_get_boxed (value2);
748   int pixel1, pixel2;
749
750   if (color1 == NULL)
751     return color2 == NULL ? 0 : -1;
752
753   pixel1 = clutter_color_to_pixel (color1);
754   pixel2 = clutter_color_to_pixel (color2);
755
756   if (pixel1 < pixel2)
757     return -1;
758   else if (pixel1 == pixel2)
759     return 0;
760   else
761     return 1;
762 }
763
764 GType
765 clutter_param_color_get_type (void)
766 {
767   static GType pspec_type = 0;
768
769   if (G_UNLIKELY (pspec_type == 0))
770     {
771       const GParamSpecTypeInfo pspec_info = {
772         sizeof (ClutterParamSpecColor),
773         16,
774         param_color_init,
775         CLUTTER_TYPE_COLOR,
776         param_color_finalize,
777         param_color_set_default,
778         NULL,
779         param_color_values_cmp,
780       };
781
782       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
783                                                  &pspec_info);
784     }
785
786   return pspec_type;
787 }
788
789 /**
790  * clutter_param_spec_color:
791  * @name: name of the property
792  * @nick: short name
793  * @blurb: description (can be translatable)
794  * @default_value: default value
795  * @flags: flags for the param spec
796  *
797  * Creates a #GParamSpec for properties using #ClutterColor.
798  *
799  * Return value: the newly created #GParamSpec
800  *
801  * Since: 0.8.4
802  */
803 GParamSpec *
804 clutter_param_spec_color (const gchar        *name,
805                           const gchar        *nick,
806                           const gchar        *blurb,
807                           const ClutterColor *default_value,
808                           GParamFlags         flags)
809 {
810   ClutterParamSpecColor *cspec;
811
812   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
813                                  name, nick, blurb, flags);
814
815   cspec->default_value = clutter_color_copy (default_value);
816
817   return G_PARAM_SPEC (cspec);
818 }