clutter-color: Remove the value table
[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   gchar *string = clutter_color_to_string (src->data[0].v_pointer);
630
631   g_value_take_string (dest, string);
632 }
633
634 static void
635 clutter_value_transform_string_color (const GValue *src,
636                                       GValue       *dest)
637 {
638   ClutterColor color = { 0, };
639
640   clutter_color_from_string (&color, g_value_get_string (src));
641
642   clutter_value_set_color (dest, &color);
643 }
644
645 GType
646 clutter_color_get_type (void)
647 {
648   static GType _clutter_color_type = 0;
649   
650   if (G_UNLIKELY (_clutter_color_type == 0))
651     {
652        _clutter_color_type =
653          g_boxed_type_register_static (I_("ClutterColor"),
654                                        (GBoxedCopyFunc) clutter_color_copy,
655                                        (GBoxedFreeFunc) clutter_color_free);
656
657        g_value_register_transform_func (_clutter_color_type, G_TYPE_STRING,
658                                         clutter_value_transform_color_string);
659        g_value_register_transform_func (G_TYPE_STRING, _clutter_color_type,
660                                         clutter_value_transform_string_color);
661     }
662
663   return _clutter_color_type;
664 }
665
666 /**
667  * clutter_value_set_color:
668  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
669  * @color: the color to set
670  *
671  * Sets @value to @color.
672  *
673  * Since: 0.8.4
674  */
675 void
676 clutter_value_set_color (GValue             *value,
677                          const ClutterColor *color)
678 {
679   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
680
681   value->data[0].v_pointer = clutter_color_copy (color);
682 }
683
684 /**
685  * clutter_value_get_color:
686  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
687  *
688  * Gets the #ClutterColor contained in @value.
689  *
690  * Return value: the colors inside the passed #GValue
691  *
692  * Since: 0.8.4
693  */
694 G_CONST_RETURN ClutterColor *
695 clutter_value_get_color (const GValue *value)
696 {
697   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
698
699   return value->data[0].v_pointer;
700 }
701
702 static void
703 param_color_init (GParamSpec *pspec)
704 {
705   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
706
707   cspec->default_value = NULL;
708 }
709
710 static void
711 param_color_finalize (GParamSpec *pspec)
712 {
713   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
714
715   clutter_color_free (cspec->default_value);
716 }
717
718 static void
719 param_color_set_default (GParamSpec *pspec,
720                         GValue     *value)
721 {
722   value->data[0].v_pointer = CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
723   value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
724 }
725
726 static gint
727 param_color_values_cmp (GParamSpec   *pspec,
728                         const GValue *value1,
729                         const GValue *value2)
730 {
731   guint32 color1, color2;
732
733   color1 = clutter_color_to_pixel (value1->data[0].v_pointer);
734   color2 = clutter_color_to_pixel (value2->data[0].v_pointer);
735
736   if (color1 < color2)
737     return -1;
738   else if (color1 == color2)
739     return 0;
740   else
741     return 1;
742 }
743
744 GType
745 clutter_param_color_get_type (void)
746 {
747   static GType pspec_type = 0;
748
749   if (G_UNLIKELY (pspec_type == 0))
750     {
751       const GParamSpecTypeInfo pspec_info = {
752         sizeof (ClutterParamSpecColor),
753         16,
754         param_color_init,
755         CLUTTER_TYPE_COLOR,
756         param_color_finalize,
757         param_color_set_default,
758         NULL,
759         param_color_values_cmp,
760       };
761
762       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
763                                                  &pspec_info);
764     }
765
766   return pspec_type;
767 }
768
769 /**
770  * clutter_param_spec_color:
771  * @name: name of the property
772  * @nick: short name
773  * @blurb: description (can be translatable)
774  * @default_value: default value
775  * @flags: flags for the param spec
776  *
777  * Creates a #GParamSpec for properties using #ClutterColor.
778  *
779  * Return value: the newly created #GParamSpec
780  *
781  * Since: 0.8.4
782  */
783 GParamSpec *
784 clutter_param_spec_color (const gchar        *name,
785                           const gchar        *nick,
786                           const gchar        *blurb,
787                           const ClutterColor *default_value,
788                           GParamFlags         flags)
789 {
790   ClutterParamSpecColor *cspec;
791
792   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
793                                  name, nick, blurb, flags);
794
795   cspec->default_value = clutter_color_copy (default_value);
796
797   return G_PARAM_SPEC (cspec);
798 }