color: Do not shadow a variable
[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-interval.h"
45 #include "clutter-main.h"
46 #include "clutter-color.h"
47 #include "clutter-private.h"
48 #include "clutter-debug.h"
49
50 /* XXX - keep in sync with the ClutterStaticColor enumeration order */
51 static const ClutterColor const static_colors[] = {
52   /* CGA/EGA color palette */
53   { 0xff, 0xff, 0xff, 0xff },   /* white */
54   { 0x00, 0x00, 0x00, 0xff },   /* black */
55   { 0xff, 0x00, 0x00, 0xff },   /* red */
56   { 0x80, 0x00, 0x00, 0xff },   /* dark red */
57   { 0x00, 0xff, 0x00, 0xff },   /* green */
58   { 0x00, 0x80, 0x00, 0xff },   /* dark green */
59   { 0x00, 0x00, 0xff, 0xff },   /* blue */
60   { 0x00, 0x00, 0x80, 0xff },   /* dark blue */
61   { 0x00, 0xff, 0xff, 0xff },   /* cyan */
62   { 0x00, 0x80, 0x80, 0xff },   /* dark cyan */
63   { 0xff, 0x00, 0xff, 0xff },   /* magenta */
64   { 0x80, 0x00, 0x80, 0xff },   /* dark magenta */
65   { 0xff, 0xff, 0x00, 0xff },   /* yellow */
66   { 0x80, 0x80, 0x00, 0xff },   /* dark yellow */
67   { 0xa0, 0xa0, 0xa4, 0xff },   /* gray */
68   { 0x80, 0x80, 0x80, 0xff },   /* dark gray */
69   { 0xc0, 0xc0, 0xc0, 0xff },   /* light gray */
70
71   /* Tango Icon color palette */
72   { 0xed, 0xd4, 0x00, 0xff },   /* butter */
73   { 0xfc, 0xe9, 0x4f, 0xff },   /* butter light */
74   { 0xc4, 0xa0, 0x00, 0xff },   /* butter dark */
75   { 0xf5, 0x79, 0x00, 0xff },   /* orange */
76   { 0xfc, 0xaf, 0x3e, 0xff },   /* orange light */
77   { 0xce, 0x5c, 0x00, 0xff },   /* orange dark */
78   { 0xc1, 0x7d, 0x11, 0xff },   /* chocolate */
79   { 0xe9, 0xb9, 0x6e, 0xff },   /* chocolate light */
80   { 0x8f, 0x59, 0x02, 0xff },   /* chocolate dark */
81   { 0x73, 0xd2, 0x16, 0xff },   /* chameleon */
82   { 0x8a, 0xe2, 0x34, 0xff },   /* chameleon light */
83   { 0x4e, 0x9a, 0x06, 0xff },   /* chameleon dark */
84   { 0x34, 0x65, 0xa4, 0xff },   /* sky blue */
85   { 0x72, 0x9f, 0xcf, 0xff },   /* sky blue light */
86   { 0x20, 0x4a, 0x87, 0xff },   /* sky blue dark */
87   { 0x75, 0x50, 0x7b, 0xff },   /* plum */
88   { 0xad, 0x7f, 0xa8, 0xff },   /* plum light */
89   { 0x5c, 0x35, 0x66, 0xff },   /* plum dark */
90   { 0xcc, 0x00, 0x00, 0xff },   /* scarlet red */
91   { 0xef, 0x29, 0x29, 0xff },   /* scarlet red light */
92   { 0xa4, 0x00, 0x00, 0xff },   /* scarlet red dark */
93   { 0xee, 0xee, 0xec, 0xff },   /* aluminium 1 */
94   { 0xd3, 0xd7, 0xcf, 0xff },   /* aluminium 2 */
95   { 0xba, 0xbd, 0xb6, 0xff },   /* aluminium 3 */
96   { 0x88, 0x8a, 0x85, 0xff },   /* aluminium 4 */
97   { 0x55, 0x57, 0x53, 0xff },   /* aluminium 5 */
98   { 0x2e, 0x34, 0x36, 0xff },   /* aluminium 6 */
99
100   /* last color */
101   { 0x00, 0x00, 0x00, 0x00 }    /* transparent */
102 };
103
104 /**
105  * clutter_color_get_static:
106  * @color: the named global color
107  *
108  * Retrieves a static color for the given @color name
109  *
110  * Static colors are created by Clutter and are guaranteed to always be
111  * available and valid
112  *
113  * Return value: a pointer to a static color; the returned pointer
114  *   is owned by Clutter and it should never be modified or freed
115  *
116  * Since: 1.4
117  */
118 G_CONST_RETURN ClutterColor *
119 clutter_color_get_static (ClutterStaticColor color)
120 {
121   g_return_val_if_fail (color >= CLUTTER_COLOR_WHITE &&
122                         color <= CLUTTER_COLOR_TRANSPARENT, NULL);
123
124   return &static_colors[color];
125 }
126
127 /**
128  * clutter_color_add:
129  * @a: a #ClutterColor
130  * @b: a #ClutterColor
131  * @result: (out caller-allocates): return location for the result
132  *
133  * Adds @a to @b and saves the resulting color inside @result.
134  *
135  * The alpha channel of @result is set as as the maximum value
136  * between the alpha channels of @a and @b.
137  */
138 void
139 clutter_color_add (const ClutterColor *a,
140                    const ClutterColor *b,
141                    ClutterColor       *result)
142 {
143   g_return_if_fail (a != NULL);
144   g_return_if_fail (b != NULL);
145   g_return_if_fail (result != NULL);
146
147   result->red   = CLAMP (a->red   + b->red,   0, 255);
148   result->green = CLAMP (a->green + b->green, 0, 255);
149   result->blue  = CLAMP (a->blue  + b->blue,  0, 255);
150
151   result->alpha = MAX (a->alpha, b->alpha);
152 }
153
154 /**
155  * clutter_color_subtract:
156  * @a: a #ClutterColor
157  * @b: a #ClutterColor
158  * @result: (out caller-allocates): return location for the result
159  *
160  * Subtracts @b from @a and saves the resulting color inside @result.
161  *
162  * This function assumes that the components of @a are greater than the
163  * components of @b; the result is, otherwise, undefined.
164  *
165  * The alpha channel of @result is set as the minimum value
166  * between the alpha channels of @a and @b.
167  */
168 void
169 clutter_color_subtract (const ClutterColor *a,
170                         const ClutterColor *b,
171                         ClutterColor       *result)
172 {
173   g_return_if_fail (a != NULL);
174   g_return_if_fail (b != NULL);
175   g_return_if_fail (result != NULL);
176
177   result->red   = CLAMP (a->red   - b->red,   0, 255);
178   result->green = CLAMP (a->green - b->green, 0, 255);
179   result->blue  = CLAMP (a->blue  - b->blue,  0, 255);
180
181   result->alpha = MIN (a->alpha, b->alpha);
182 }
183
184 /**
185  * clutter_color_lighten:
186  * @color: a #ClutterColor
187  * @result: (out caller-allocates): return location for the lighter color
188  *
189  * Lightens @color by a fixed amount, and saves the changed color
190  * in @result.
191  */
192 void
193 clutter_color_lighten (const ClutterColor *color,
194                        ClutterColor       *result)
195 {
196   clutter_color_shade (color, 1.3, result);
197 }
198
199 /**
200  * clutter_color_darken:
201  * @color: a #ClutterColor
202  * @result: (out caller-allocates): return location for the darker color
203  *
204  * Darkens @color by a fixed amount, and saves the changed color
205  * in @result.
206  */
207 void
208 clutter_color_darken (const ClutterColor *color,
209                       ClutterColor       *result)
210 {
211   clutter_color_shade (color, 0.7, result);
212 }
213
214 /**
215  * clutter_color_to_hls:
216  * @color: a #ClutterColor
217  * @hue: (out): return location for the hue value or %NULL
218  * @luminance: (out): return location for the luminance value or %NULL
219  * @saturation: (out): return location for the saturation value or %NULL
220  *
221  * Converts @color to the HLS format.
222  *
223  * The @hue value is in the 0 .. 360 range. The @luminance and
224  * @saturation values are in the 0 .. 1 range.
225  */
226 void
227 clutter_color_to_hls (const ClutterColor *color,
228                       float              *hue,
229                       float              *luminance,
230                       float              *saturation)
231 {
232   float red, green, blue;
233   float min, max, delta;
234   float h, l, s;
235   
236   g_return_if_fail (color != NULL);
237
238   red   = color->red / 255.0;
239   green = color->green / 255.0;
240   blue  = color->blue / 255.0;
241
242   if (red > green)
243     {
244       if (red > blue)
245         max = red;
246       else
247         max = blue;
248
249       if (green < blue)
250         min = green;
251       else
252         min = blue;
253     }
254   else
255     {
256       if (green > blue)
257         max = green;
258       else
259         max = blue;
260
261       if (red < blue)
262         min = red;
263       else
264         min = blue;
265     }
266
267   l = (max + min) / 2;
268   s = 0;
269   h = 0;
270
271   if (max != min)
272     {
273       if (l <= 0.5)
274         s = (max - min) / (max + min);
275       else
276         s = (max - min) / (2.0 - max - min);
277
278       delta = max - min;
279
280       if (red == max)
281         h = (green - blue) / delta;
282       else if (green == max)
283         h = 2.0 + (blue - red) / delta;
284       else if (blue == max)
285         h = 4.0 + (red - green) / delta;
286
287       h *= 60;
288
289       if (h < 0)
290         h += 360.0;
291     }
292
293   if (hue)
294     *hue = h;
295
296   if (luminance)
297     *luminance = l;
298
299   if (saturation)
300     *saturation = s;
301 }
302
303 /**
304  * clutter_color_from_hls:
305  * @color: (out): return location for a #ClutterColor
306  * @hue: hue value, in the 0 .. 360 range
307  * @luminance: luminance value, in the 0 .. 1 range
308  * @saturation: saturation value, in the 0 .. 1 range
309  *
310  * Converts a color expressed in HLS (hue, luminance and saturation)
311  * values into a #ClutterColor.
312  */
313 void
314 clutter_color_from_hls (ClutterColor *color,
315                         float         hue,
316                         float         luminance,
317                         float         saturation)
318 {
319   float tmp1, tmp2;
320   float tmp3[3];
321   float clr[3];
322   int   i;
323
324   hue /= 360.0;
325
326   if (saturation == 0)
327     {
328       color->red = color->green = color->blue = (luminance * 255);
329
330       return;
331     }
332
333   if (luminance <= 0.5)
334     tmp2 = luminance * (1.0 + saturation);
335   else
336     tmp2 = luminance + saturation - (luminance * saturation);
337
338   tmp1 = 2.0 * luminance - tmp2;
339
340   tmp3[0] = hue + 1.0 / 3.0;
341   tmp3[1] = hue;
342   tmp3[2] = hue - 1.0 / 3.0;
343
344   for (i = 0; i < 3; i++)
345     {
346       if (tmp3[i] < 0)
347         tmp3[i] += 1.0;
348
349       if (tmp3[i] > 1)
350         tmp3[i] -= 1.0;
351
352       if (6.0 * tmp3[i] < 1.0)
353         clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0;
354       else if (2.0 * tmp3[i] < 1.0)
355         clr[i] = tmp2;
356       else if (3.0 * tmp3[i] < 2.0)
357         clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0);
358       else
359         clr[i] = tmp1;
360     }
361
362   color->red   = floorf (clr[0] * 255.0 + 0.5);
363   color->green = floorf (clr[1] * 255.0 + 0.5);
364   color->blue  = floorf (clr[2] * 255.0 + 0.5);
365 }
366
367 /**
368  * clutter_color_shade:
369  * @color: a #ClutterColor
370  * @factor: the shade factor to apply
371  * @result: (out caller-allocates): return location for the shaded color
372  *
373  * Shades @color by @factor and saves the modified color into @result.
374  */
375 void
376 clutter_color_shade (const ClutterColor *color,
377                      gdouble             factor,
378                      ClutterColor       *result)
379 {
380   float h, l, s;
381
382   g_return_if_fail (color != NULL);
383   g_return_if_fail (result != NULL);
384   
385   clutter_color_to_hls (color, &h, &l, &s);
386
387   l *= factor;
388   if (l > 1.0)
389     l = 1.0;
390   else if (l < 0)
391     l = 0;
392
393   s *= factor;
394   if (s > 1.0)
395     s = 1.0;
396   else if (s < 0)
397     s = 0;
398   
399   clutter_color_from_hls (result, h, l, s);
400
401   result->alpha = color->alpha;
402 }
403
404 /**
405  * clutter_color_to_pixel:
406  * @color: a #ClutterColor
407  *
408  * Converts @color into a packed 32 bit integer, containing
409  * all the four 8 bit channels used by #ClutterColor.
410  *
411  * Return value: a packed color
412  */
413 guint32
414 clutter_color_to_pixel (const ClutterColor *color)
415 {
416   g_return_val_if_fail (color != NULL, 0);
417   
418   return (color->alpha       |
419           color->blue  << 8  |
420           color->green << 16 |
421           color->red   << 24);
422 }
423
424 /**
425  * clutter_color_from_pixel:
426  * @color: (out caller-allocates): return location for a #ClutterColor
427  * @pixel: a 32 bit packed integer containing a color
428  *
429  * Converts @pixel from the packed representation of a four 8 bit channel
430  * color to a #ClutterColor.
431  */
432 void
433 clutter_color_from_pixel (ClutterColor *color,
434                           guint32       pixel)
435 {
436   g_return_if_fail (color != NULL);
437
438   color->red   =  pixel >> 24;
439   color->green = (pixel >> 16) & 0xff;
440   color->blue  = (pixel >> 8)  & 0xff;
441   color->alpha =  pixel        & 0xff;
442 }
443
444 static inline void
445 skip_whitespace (gchar **str)
446 {
447   while (g_ascii_isspace (**str))
448     *str += 1;
449 }
450
451 static inline void
452 parse_rgb_value (gchar   *str,
453                  guint8  *color,
454                  gchar  **endp)
455 {
456   gdouble number;
457   gchar *p;
458
459   skip_whitespace (&str);
460
461   number = g_ascii_strtod (str, endp);
462
463   p = *endp;
464
465   skip_whitespace (&p);
466
467   if (*p == '%')
468     {
469       *endp = (gchar *) (p + 1);
470
471       *color = CLAMP (number / 100.0, 0.0, 1.0) * 255;
472     }
473   else
474     *color = CLAMP (number, 0, 255);
475 }
476
477 static gboolean
478 parse_rgba (ClutterColor *color,
479             gchar        *str,
480             gboolean      has_alpha)
481 {
482   skip_whitespace (&str);
483
484   if (*str != '(')
485     return FALSE;
486
487   str += 1;
488
489   /* red */
490   parse_rgb_value (str, &color->red, &str);
491   skip_whitespace (&str);
492   if (*str != ',')
493     return FALSE;
494
495   str += 1;
496
497   /* green */
498   parse_rgb_value (str, &color->green, &str);
499   skip_whitespace (&str);
500   if (*str != ',')
501     return FALSE;
502
503   str += 1;
504
505   /* blue */
506   parse_rgb_value (str, &color->blue, &str);
507   skip_whitespace (&str);
508
509   /* alpha (optional); since the alpha channel value can only
510    * be between 0 and 1 we don't use the parse_rgb_value()
511    * function
512    */
513   if (has_alpha)
514     {
515       gdouble number;
516
517       if (*str != ',')
518         return FALSE;
519
520       str += 1;
521
522       skip_whitespace (&str);
523       number = g_ascii_strtod (str, &str);
524
525       color->alpha = CLAMP (number * 255.0, 0, 255);
526     }
527   else
528     color->alpha = 255;
529
530   skip_whitespace (&str);
531   if (*str != ')')
532     return FALSE;
533
534   return TRUE;
535 }
536
537 static gboolean
538 parse_hsla (ClutterColor *color,
539             gchar        *str,
540             gboolean      has_alpha)
541 {
542   gdouble number;
543   gdouble h, l, s;
544
545   skip_whitespace (&str);
546
547   if (*str != '(')
548     return FALSE;
549
550   str += 1;
551
552   /* hue */
553   skip_whitespace (&str);
554   /* we don't do any angle normalization here because
555    * clutter_color_from_hls() will do it for us
556    */
557   number = g_ascii_strtod (str, &str);
558   skip_whitespace (&str);
559   if (*str != ',')
560     return FALSE;
561
562   h = number;
563
564   str += 1;
565
566   /* saturation */
567   skip_whitespace (&str);
568   number = g_ascii_strtod (str, &str);
569   skip_whitespace (&str);
570   if (*str != '%')
571     return FALSE;
572
573   str += 1;
574
575   s = CLAMP (number / 100.0, 0.0, 1.0);
576   skip_whitespace (&str);
577   if (*str != ',')
578     return FALSE;
579
580   str += 1;
581
582   /* luminance */
583   skip_whitespace (&str);
584   number = g_ascii_strtod (str, &str);
585   skip_whitespace (&str);
586   if (*str != '%')
587     return FALSE;
588
589   str += 1;
590
591   l = CLAMP (number / 100.0, 0.0, 1.0);
592
593   /* alpha (optional); since the alpha channel value can only
594    * be between 0 and 1 we don't use the parse_rgb_value()
595    * function
596    */
597   if (has_alpha)
598     {
599       if (*str != ',')
600         return FALSE;
601
602       str += 1;
603
604       skip_whitespace (&str);
605       number = g_ascii_strtod (str, &str);
606
607       color->alpha = CLAMP (number * 255.0, 0, 255);
608     }
609   else
610     color->alpha = 255;
611
612   skip_whitespace (&str);
613   if (*str != ')')
614     return FALSE;
615
616   clutter_color_from_hls (color, h, l, s);
617
618   return TRUE;
619 }
620
621 /**
622  * clutter_color_from_string:
623  * @color: (out caller-allocates): return location for a #ClutterColor
624  * @str: a string specifiying a color (named color or #RRGGBBAA)
625  *
626  * Parses a string definition of a color, filling the
627  * <structfield>red</structfield>, <structfield>green</structfield>, 
628  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
629  * channels of @color. If alpha is not specified it will be set full opaque.
630  *
631  * The @color is not allocated.
632  *
633  * The format of @str can be either one of:
634  * <itemizedlist>
635  * <listitem><para>a standard name (taken from the X11 rgb.txt
636  * file");</para></listitem>
637  * <listitem><para>an hexadecimal value in the form: '#rgb', '#rrggbb',
638  * '#rgba' or '#rrggbbaa';</para></listitem>
639  * <listitem><para>a RGB color in the form 'rgb(r, g, b)';</para></listitem>
640  * <listitem><para>a RGBA color in the form 'rgba(r, g, b,
641  * a)';</para></listitem>
642  *
643  * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue and
644  * alpha color values.
645  *
646  * In the last two cases, the 'r', 'g', and 'b' values are either integers
647  * between 0 and 255, or percentage values in the range between 0% and 100%;
648  * the percentages require the '%' character. The 'a' value, if specified,
649  * can only be a floating point value between 0.0 and 1.0.
650  *
651  * Whitespace is ignored.
652  *
653  * If the alpha component is not specified then it is assumed to be set to
654  * be fully opaque.
655  *
656  * Return value: %TRUE if parsing succeeded.
657  *
658  * Since: 1.0
659  */
660 gboolean
661 clutter_color_from_string (ClutterColor *color,
662                            const gchar  *str)
663 {
664   PangoColor pango_color = { 0, };
665
666   g_return_val_if_fail (color != NULL, FALSE);
667   g_return_val_if_fail (str != NULL, FALSE);
668
669   if (strncmp (str, "rgb", 3) == 0)
670     {
671       gchar *s = (gchar *) str;
672       gboolean res;
673
674       if (strncmp (str, "rgba", 4) == 0)
675         res = parse_rgba (color, s + 4, TRUE);
676       else
677         res = parse_rgba (color, s + 3, FALSE);
678
679       return res;
680     }
681
682   if (strncmp (str, "hsl", 3) == 0)
683     {
684       gchar *s = (gchar *) str;
685       gboolean res;
686
687       if (strncmp (str, "hsla", 4) == 0)
688         res = parse_hsla (color, s + 4, TRUE);
689       else
690         res = parse_hsla (color, s + 3, FALSE);
691
692       return res;
693     }
694
695   /* if the string contains a color encoded using the hexadecimal
696    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
697    * parsing the color ourselves, as we need the alpha channel that
698    * Pango can't retrieve.
699    */
700   if (str[0] == '#')
701     {
702       gint32 result;
703
704       if (sscanf (str + 1, "%x", &result))
705         {
706           gsize length = strlen (str);
707
708           switch (length)
709             {
710             case 9: /* rrggbbaa */
711               color->red   = (result >> 24) & 0xff;
712               color->green = (result >> 16) & 0xff;
713               color->blue  = (result >>  8) & 0xff;
714
715               color->alpha = result & 0xff;
716
717               return TRUE;
718
719             case 7: /* #rrggbb */
720               color->red   = (result >> 16) & 0xff;
721               color->green = (result >>  8) & 0xff;
722               color->blue  = result & 0xff;
723
724               color->alpha = 0xff;
725
726               return TRUE;
727
728             case 5: /* #rgba */
729               color->red   = ((result >> 12) & 0xf);
730               color->green = ((result >>  8) & 0xf);
731               color->blue  = ((result >>  4) & 0xf);
732               color->alpha = result & 0xf;
733
734               color->red   = (color->red   << 4) | color->red;
735               color->green = (color->green << 4) | color->green;
736               color->blue  = (color->blue  << 4) | color->blue;
737               color->alpha = (color->alpha << 4) | color->alpha;
738
739               return TRUE;
740
741             case 4: /* #rgb */
742               color->red   = ((result >>  8) & 0xf);
743               color->green = ((result >>  4) & 0xf);
744               color->blue  = result & 0xf;
745
746               color->red   = (color->red   << 4) | color->red;
747               color->green = (color->green << 4) | color->green;
748               color->blue  = (color->blue  << 4) | color->blue;
749
750               color->alpha = 0xff;
751
752               return TRUE;
753
754             default:
755               /* pass through to Pango */
756               break;
757             }
758         }
759     }
760
761   /* Fall back to pango for named colors */
762   if (pango_color_parse (&pango_color, str))
763     {
764       color->red   = pango_color.red;
765       color->green = pango_color.green;
766       color->blue  = pango_color.blue;
767
768       color->alpha = 0xff;
769
770       return TRUE;
771     }
772
773   return FALSE;
774 }
775
776 /**
777  * clutter_color_to_string:
778  * @color: a #ClutterColor
779  *
780  * Returns a textual specification of @color in the hexadecimal form
781  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
782  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
783  * hex digits representing the red, green, blue and alpha components
784  * respectively.
785  *
786  * Return value: (transfer full): a newly-allocated text string
787  *
788  * Since: 0.2
789  */
790 gchar *
791 clutter_color_to_string (const ClutterColor *color)
792 {
793   g_return_val_if_fail (color != NULL, NULL);
794
795   return g_strdup_printf ("#%02x%02x%02x%02x",
796                           color->red,
797                           color->green,
798                           color->blue,
799                           color->alpha);
800 }
801
802 /**
803  * clutter_color_equal:
804  * @v1: a #ClutterColor
805  * @v2: a #ClutterColor
806  *
807  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
808  *
809  * This function can be passed to g_hash_table_new() as the @key_equal_func
810  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
811  *
812  * Return value: %TRUE if the two colors are the same.
813  *
814  * Since: 0.2
815  */
816 gboolean
817 clutter_color_equal (gconstpointer v1,
818                      gconstpointer v2)
819 {
820   const ClutterColor *a, *b;
821
822   g_return_val_if_fail (v1 != NULL, FALSE);
823   g_return_val_if_fail (v2 != NULL, FALSE);
824
825   if (v1 == v2)
826     return TRUE;
827
828   a = v1;
829   b = v2;
830
831   return (a->red   == b->red   &&
832           a->green == b->green &&
833           a->blue  == b->blue  &&
834           a->alpha == b->alpha);
835 }
836
837 /**
838  * clutter_color_hash:
839  * @v: a #ClutterColor
840  *
841  * Converts a #ClutterColor to a hash value.
842  *
843  * This function can be passed to g_hash_table_new() as the @hash_func
844  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
845  *
846  * Return value: a hash value corresponding to the color
847  *
848  * Since: 1.0
849  */
850 guint
851 clutter_color_hash (gconstpointer v)
852 {
853   return clutter_color_to_pixel ((const ClutterColor *) v);
854 }
855
856 /**
857  * clutter_color_interpolate:
858  * @initial: the initial #ClutterColor
859  * @final: the final #ClutterColor
860  * @progress: the interpolation progress
861  * @result: (out): return location for the interpolation
862  *
863  * Interpolates between @initial and @final #ClutterColor<!-- -->s
864  * using @progress
865  *
866  * Since: 1.6
867  */
868 void
869 clutter_color_interpolate (const ClutterColor *initial,
870                            const ClutterColor *final,
871                            gdouble             progress,
872                            ClutterColor       *result)
873 {
874   g_return_if_fail (initial != NULL);
875   g_return_if_fail (final != NULL);
876   g_return_if_fail (result != NULL);
877
878   result->red   = initial->red   + (final->red   - initial->red)   * progress;
879   result->green = initial->green + (final->green - initial->green) * progress;
880   result->blue  = initial->blue  + (final->blue  - initial->blue)  * progress;
881   result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress;
882 }
883
884 static gboolean
885 clutter_color_progress (const GValue *a,
886                         const GValue *b,
887                         gdouble       progress,
888                         GValue       *retval)
889 {
890   const ClutterColor *a_color = clutter_value_get_color (a);
891   const ClutterColor *b_color = clutter_value_get_color (b);
892   ClutterColor res = { 0, };
893
894   clutter_color_interpolate (a_color, b_color, progress, &res);
895   clutter_value_set_color (retval, &res);
896
897   return TRUE;
898 }
899
900 /**
901  * clutter_color_copy:
902  * @color: a #ClutterColor
903  *
904  * Makes a copy of the color structure.  The result must be
905  * freed using clutter_color_free().
906  *
907  * Return value: (transfer full): an allocated copy of @color.
908  *
909  * Since: 0.2
910  */
911 ClutterColor *
912 clutter_color_copy (const ClutterColor *color)
913 {
914   if (G_LIKELY (color != NULL))
915     return g_slice_dup (ClutterColor, color);
916
917   return NULL;
918 }
919
920 /**
921  * clutter_color_free:
922  * @color: a #ClutterColor
923  *
924  * Frees a color structure created with clutter_color_copy().
925  *
926  * Since: 0.2
927  */
928 void
929 clutter_color_free (ClutterColor *color)
930 {
931   if (G_LIKELY (color != NULL))
932     g_slice_free (ClutterColor, color);
933 }
934
935 /**
936  * clutter_color_new:
937  * @red: red component of the color, between 0 and 255
938  * @green: green component of the color, between 0 and 255
939  * @blue: blue component of the color, between 0 and 255
940  * @alpha: alpha component of the color, between 0 and 255
941  *
942  * Creates a new #ClutterColor with the given values.
943  *
944  * Return value: (transfer full): the newly allocated color.
945  *   Use clutter_color_free() when done
946  *
947  * Since: 0.8.4
948  */
949 ClutterColor *
950 clutter_color_new (guint8 red,
951                    guint8 green,
952                    guint8 blue,
953                    guint8 alpha)
954 {
955   ClutterColor *color;
956
957   color = g_slice_new (ClutterColor);
958
959   color->red   = red;
960   color->green = green;
961   color->blue  = blue;
962   color->alpha = alpha;
963
964   return color;
965 }
966
967 static void
968 clutter_value_transform_color_string (const GValue *src,
969                                       GValue       *dest)
970 {
971   const ClutterColor *color = g_value_get_boxed (src);
972
973   if (color)
974     {
975       gchar *string = clutter_color_to_string (color);
976
977       g_value_take_string (dest, string);
978     }
979   else
980     g_value_set_string (dest, NULL);
981 }
982
983 static void
984 clutter_value_transform_string_color (const GValue *src,
985                                       GValue       *dest)
986 {
987   const char *str = g_value_get_string (src);
988
989   if (str)
990     {
991       ClutterColor color = { 0, };
992
993       clutter_color_from_string (&color, str);
994
995       clutter_value_set_color (dest, &color);
996     }
997   else
998     clutter_value_set_color (dest, NULL);
999 }
1000
1001 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color,
1002                                clutter_color_copy,
1003                                clutter_color_free,
1004                                CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string)
1005                                CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color)
1006                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress));
1007
1008 /**
1009  * clutter_value_set_color:
1010  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1011  * @color: the color to set
1012  *
1013  * Sets @value to @color.
1014  *
1015  * Since: 0.8.4
1016  */
1017 void
1018 clutter_value_set_color (GValue             *value,
1019                          const ClutterColor *color)
1020 {
1021   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
1022
1023   g_value_set_boxed (value, color);
1024 }
1025
1026 /**
1027  * clutter_value_get_color:
1028  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1029  *
1030  * Gets the #ClutterColor contained in @value.
1031  *
1032  * Return value: (transfer none): the color inside the passed #GValue
1033  *
1034  * Since: 0.8.4
1035  */
1036 G_CONST_RETURN ClutterColor *
1037 clutter_value_get_color (const GValue *value)
1038 {
1039   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
1040
1041   return g_value_get_boxed (value);
1042 }
1043
1044 static void
1045 param_color_init (GParamSpec *pspec)
1046 {
1047   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1048
1049   cspec->default_value = NULL;
1050 }
1051
1052 static void
1053 param_color_finalize (GParamSpec *pspec)
1054 {
1055   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1056
1057   clutter_color_free (cspec->default_value);
1058 }
1059
1060 static void
1061 param_color_set_default (GParamSpec *pspec,
1062                          GValue     *value)
1063 {
1064   const ClutterColor *default_value =
1065     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
1066   clutter_value_set_color (value, default_value);
1067 }
1068
1069 static gint
1070 param_color_values_cmp (GParamSpec   *pspec,
1071                         const GValue *value1,
1072                         const GValue *value2)
1073 {
1074   const ClutterColor *color1 = g_value_get_boxed (value1);
1075   const ClutterColor *color2 = g_value_get_boxed (value2);
1076   int pixel1, pixel2;
1077
1078   if (color1 == NULL)
1079     return color2 == NULL ? 0 : -1;
1080
1081   pixel1 = clutter_color_to_pixel (color1);
1082   pixel2 = clutter_color_to_pixel (color2);
1083
1084   if (pixel1 < pixel2)
1085     return -1;
1086   else if (pixel1 == pixel2)
1087     return 0;
1088   else
1089     return 1;
1090 }
1091
1092 GType
1093 clutter_param_color_get_type (void)
1094 {
1095   static GType pspec_type = 0;
1096
1097   if (G_UNLIKELY (pspec_type == 0))
1098     {
1099       const GParamSpecTypeInfo pspec_info = {
1100         sizeof (ClutterParamSpecColor),
1101         16,
1102         param_color_init,
1103         CLUTTER_TYPE_COLOR,
1104         param_color_finalize,
1105         param_color_set_default,
1106         NULL,
1107         param_color_values_cmp,
1108       };
1109
1110       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
1111                                                  &pspec_info);
1112     }
1113
1114   return pspec_type;
1115 }
1116
1117 /**
1118  * clutter_param_spec_color: (skip)
1119  * @name: name of the property
1120  * @nick: short name
1121  * @blurb: description (can be translatable)
1122  * @default_value: default value
1123  * @flags: flags for the param spec
1124  *
1125  * Creates a #GParamSpec for properties using #ClutterColor.
1126  *
1127  * Return value: the newly created #GParamSpec
1128  *
1129  * Since: 0.8.4
1130  */
1131 GParamSpec *
1132 clutter_param_spec_color (const gchar        *name,
1133                           const gchar        *nick,
1134                           const gchar        *blurb,
1135                           const ClutterColor *default_value,
1136                           GParamFlags         flags)
1137 {
1138   ClutterParamSpecColor *cspec;
1139
1140   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
1141                                  name, nick, blurb, flags);
1142
1143   cspec->default_value = clutter_color_copy (default_value);
1144
1145   return G_PARAM_SPEC (cspec);
1146 }