docs: Close a tag
[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  *
635  * <itemizedlist>
636  * <listitem><para>a standard name (taken from the X11 rgb.txt
637  * file");</para></listitem>
638  * <listitem><para>an hexadecimal value in the form: '#rgb', '#rrggbb',
639  * '#rgba' or '#rrggbbaa';</para></listitem>
640  * <listitem><para>a RGB color in the form 'rgb(r, g, b)';</para></listitem>
641  * <listitem><para>a RGBA color in the form 'rgba(r, g, b,
642  * a)';</para></listitem>
643  * </itemizedlist>
644  *
645  * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue and
646  * alpha color values.
647  *
648  * In the last two cases, the 'r', 'g', and 'b' values are either integers
649  * between 0 and 255, or percentage values in the range between 0% and 100%;
650  * the percentages require the '%' character. The 'a' value, if specified,
651  * can only be a floating point value between 0.0 and 1.0.
652  *
653  * Whitespace is ignored.
654  *
655  * If the alpha component is not specified then it is assumed to be set to
656  * be fully opaque.
657  *
658  * Return value: %TRUE if parsing succeeded.
659  *
660  * Since: 1.0
661  */
662 gboolean
663 clutter_color_from_string (ClutterColor *color,
664                            const gchar  *str)
665 {
666   PangoColor pango_color = { 0, };
667
668   g_return_val_if_fail (color != NULL, FALSE);
669   g_return_val_if_fail (str != NULL, FALSE);
670
671   if (strncmp (str, "rgb", 3) == 0)
672     {
673       gchar *s = (gchar *) str;
674       gboolean res;
675
676       if (strncmp (str, "rgba", 4) == 0)
677         res = parse_rgba (color, s + 4, TRUE);
678       else
679         res = parse_rgba (color, s + 3, FALSE);
680
681       return res;
682     }
683
684   if (strncmp (str, "hsl", 3) == 0)
685     {
686       gchar *s = (gchar *) str;
687       gboolean res;
688
689       if (strncmp (str, "hsla", 4) == 0)
690         res = parse_hsla (color, s + 4, TRUE);
691       else
692         res = parse_hsla (color, s + 3, FALSE);
693
694       return res;
695     }
696
697   /* if the string contains a color encoded using the hexadecimal
698    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
699    * parsing the color ourselves, as we need the alpha channel that
700    * Pango can't retrieve.
701    */
702   if (str[0] == '#')
703     {
704       gint32 result;
705
706       if (sscanf (str + 1, "%x", &result))
707         {
708           gsize length = strlen (str);
709
710           switch (length)
711             {
712             case 9: /* rrggbbaa */
713               color->red   = (result >> 24) & 0xff;
714               color->green = (result >> 16) & 0xff;
715               color->blue  = (result >>  8) & 0xff;
716
717               color->alpha = result & 0xff;
718
719               return TRUE;
720
721             case 7: /* #rrggbb */
722               color->red   = (result >> 16) & 0xff;
723               color->green = (result >>  8) & 0xff;
724               color->blue  = result & 0xff;
725
726               color->alpha = 0xff;
727
728               return TRUE;
729
730             case 5: /* #rgba */
731               color->red   = ((result >> 12) & 0xf);
732               color->green = ((result >>  8) & 0xf);
733               color->blue  = ((result >>  4) & 0xf);
734               color->alpha = result & 0xf;
735
736               color->red   = (color->red   << 4) | color->red;
737               color->green = (color->green << 4) | color->green;
738               color->blue  = (color->blue  << 4) | color->blue;
739               color->alpha = (color->alpha << 4) | color->alpha;
740
741               return TRUE;
742
743             case 4: /* #rgb */
744               color->red   = ((result >>  8) & 0xf);
745               color->green = ((result >>  4) & 0xf);
746               color->blue  = result & 0xf;
747
748               color->red   = (color->red   << 4) | color->red;
749               color->green = (color->green << 4) | color->green;
750               color->blue  = (color->blue  << 4) | color->blue;
751
752               color->alpha = 0xff;
753
754               return TRUE;
755
756             default:
757               /* pass through to Pango */
758               break;
759             }
760         }
761     }
762
763   /* Fall back to pango for named colors */
764   if (pango_color_parse (&pango_color, str))
765     {
766       color->red   = pango_color.red;
767       color->green = pango_color.green;
768       color->blue  = pango_color.blue;
769
770       color->alpha = 0xff;
771
772       return TRUE;
773     }
774
775   return FALSE;
776 }
777
778 /**
779  * clutter_color_to_string:
780  * @color: a #ClutterColor
781  *
782  * Returns a textual specification of @color in the hexadecimal form
783  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
784  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
785  * hex digits representing the red, green, blue and alpha components
786  * respectively.
787  *
788  * Return value: (transfer full): a newly-allocated text string
789  *
790  * Since: 0.2
791  */
792 gchar *
793 clutter_color_to_string (const ClutterColor *color)
794 {
795   g_return_val_if_fail (color != NULL, NULL);
796
797   return g_strdup_printf ("#%02x%02x%02x%02x",
798                           color->red,
799                           color->green,
800                           color->blue,
801                           color->alpha);
802 }
803
804 /**
805  * clutter_color_equal:
806  * @v1: a #ClutterColor
807  * @v2: a #ClutterColor
808  *
809  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
810  *
811  * This function can be passed to g_hash_table_new() as the @key_equal_func
812  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
813  *
814  * Return value: %TRUE if the two colors are the same.
815  *
816  * Since: 0.2
817  */
818 gboolean
819 clutter_color_equal (gconstpointer v1,
820                      gconstpointer v2)
821 {
822   const ClutterColor *a, *b;
823
824   g_return_val_if_fail (v1 != NULL, FALSE);
825   g_return_val_if_fail (v2 != NULL, FALSE);
826
827   if (v1 == v2)
828     return TRUE;
829
830   a = v1;
831   b = v2;
832
833   return (a->red   == b->red   &&
834           a->green == b->green &&
835           a->blue  == b->blue  &&
836           a->alpha == b->alpha);
837 }
838
839 /**
840  * clutter_color_hash:
841  * @v: a #ClutterColor
842  *
843  * Converts a #ClutterColor to a hash value.
844  *
845  * This function can be passed to g_hash_table_new() as the @hash_func
846  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
847  *
848  * Return value: a hash value corresponding to the color
849  *
850  * Since: 1.0
851  */
852 guint
853 clutter_color_hash (gconstpointer v)
854 {
855   return clutter_color_to_pixel ((const ClutterColor *) v);
856 }
857
858 /**
859  * clutter_color_interpolate:
860  * @initial: the initial #ClutterColor
861  * @final: the final #ClutterColor
862  * @progress: the interpolation progress
863  * @result: (out): return location for the interpolation
864  *
865  * Interpolates between @initial and @final #ClutterColor<!-- -->s
866  * using @progress
867  *
868  * Since: 1.6
869  */
870 void
871 clutter_color_interpolate (const ClutterColor *initial,
872                            const ClutterColor *final,
873                            gdouble             progress,
874                            ClutterColor       *result)
875 {
876   g_return_if_fail (initial != NULL);
877   g_return_if_fail (final != NULL);
878   g_return_if_fail (result != NULL);
879
880   result->red   = initial->red   + (final->red   - initial->red)   * progress;
881   result->green = initial->green + (final->green - initial->green) * progress;
882   result->blue  = initial->blue  + (final->blue  - initial->blue)  * progress;
883   result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress;
884 }
885
886 static gboolean
887 clutter_color_progress (const GValue *a,
888                         const GValue *b,
889                         gdouble       progress,
890                         GValue       *retval)
891 {
892   const ClutterColor *a_color = clutter_value_get_color (a);
893   const ClutterColor *b_color = clutter_value_get_color (b);
894   ClutterColor res = { 0, };
895
896   clutter_color_interpolate (a_color, b_color, progress, &res);
897   clutter_value_set_color (retval, &res);
898
899   return TRUE;
900 }
901
902 /**
903  * clutter_color_copy:
904  * @color: a #ClutterColor
905  *
906  * Makes a copy of the color structure.  The result must be
907  * freed using clutter_color_free().
908  *
909  * Return value: (transfer full): an allocated copy of @color.
910  *
911  * Since: 0.2
912  */
913 ClutterColor *
914 clutter_color_copy (const ClutterColor *color)
915 {
916   if (G_LIKELY (color != NULL))
917     return g_slice_dup (ClutterColor, color);
918
919   return NULL;
920 }
921
922 /**
923  * clutter_color_free:
924  * @color: a #ClutterColor
925  *
926  * Frees a color structure created with clutter_color_copy().
927  *
928  * Since: 0.2
929  */
930 void
931 clutter_color_free (ClutterColor *color)
932 {
933   if (G_LIKELY (color != NULL))
934     g_slice_free (ClutterColor, color);
935 }
936
937 /**
938  * clutter_color_new:
939  * @red: red component of the color, between 0 and 255
940  * @green: green component of the color, between 0 and 255
941  * @blue: blue component of the color, between 0 and 255
942  * @alpha: alpha component of the color, between 0 and 255
943  *
944  * Creates a new #ClutterColor with the given values.
945  *
946  * Return value: (transfer full): the newly allocated color.
947  *   Use clutter_color_free() when done
948  *
949  * Since: 0.8.4
950  */
951 ClutterColor *
952 clutter_color_new (guint8 red,
953                    guint8 green,
954                    guint8 blue,
955                    guint8 alpha)
956 {
957   ClutterColor *color;
958
959   color = g_slice_new (ClutterColor);
960
961   color->red   = red;
962   color->green = green;
963   color->blue  = blue;
964   color->alpha = alpha;
965
966   return color;
967 }
968
969 static void
970 clutter_value_transform_color_string (const GValue *src,
971                                       GValue       *dest)
972 {
973   const ClutterColor *color = g_value_get_boxed (src);
974
975   if (color)
976     {
977       gchar *string = clutter_color_to_string (color);
978
979       g_value_take_string (dest, string);
980     }
981   else
982     g_value_set_string (dest, NULL);
983 }
984
985 static void
986 clutter_value_transform_string_color (const GValue *src,
987                                       GValue       *dest)
988 {
989   const char *str = g_value_get_string (src);
990
991   if (str)
992     {
993       ClutterColor color = { 0, };
994
995       clutter_color_from_string (&color, str);
996
997       clutter_value_set_color (dest, &color);
998     }
999   else
1000     clutter_value_set_color (dest, NULL);
1001 }
1002
1003 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color,
1004                                clutter_color_copy,
1005                                clutter_color_free,
1006                                CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string)
1007                                CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color)
1008                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress));
1009
1010 /**
1011  * clutter_value_set_color:
1012  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1013  * @color: the color to set
1014  *
1015  * Sets @value to @color.
1016  *
1017  * Since: 0.8.4
1018  */
1019 void
1020 clutter_value_set_color (GValue             *value,
1021                          const ClutterColor *color)
1022 {
1023   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
1024
1025   g_value_set_boxed (value, color);
1026 }
1027
1028 /**
1029  * clutter_value_get_color:
1030  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1031  *
1032  * Gets the #ClutterColor contained in @value.
1033  *
1034  * Return value: (transfer none): the color inside the passed #GValue
1035  *
1036  * Since: 0.8.4
1037  */
1038 G_CONST_RETURN ClutterColor *
1039 clutter_value_get_color (const GValue *value)
1040 {
1041   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
1042
1043   return g_value_get_boxed (value);
1044 }
1045
1046 static void
1047 param_color_init (GParamSpec *pspec)
1048 {
1049   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1050
1051   cspec->default_value = NULL;
1052 }
1053
1054 static void
1055 param_color_finalize (GParamSpec *pspec)
1056 {
1057   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1058
1059   clutter_color_free (cspec->default_value);
1060 }
1061
1062 static void
1063 param_color_set_default (GParamSpec *pspec,
1064                          GValue     *value)
1065 {
1066   const ClutterColor *default_value =
1067     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
1068   clutter_value_set_color (value, default_value);
1069 }
1070
1071 static gint
1072 param_color_values_cmp (GParamSpec   *pspec,
1073                         const GValue *value1,
1074                         const GValue *value2)
1075 {
1076   const ClutterColor *color1 = g_value_get_boxed (value1);
1077   const ClutterColor *color2 = g_value_get_boxed (value2);
1078   int pixel1, pixel2;
1079
1080   if (color1 == NULL)
1081     return color2 == NULL ? 0 : -1;
1082
1083   pixel1 = clutter_color_to_pixel (color1);
1084   pixel2 = clutter_color_to_pixel (color2);
1085
1086   if (pixel1 < pixel2)
1087     return -1;
1088   else if (pixel1 == pixel2)
1089     return 0;
1090   else
1091     return 1;
1092 }
1093
1094 GType
1095 clutter_param_color_get_type (void)
1096 {
1097   static GType pspec_type = 0;
1098
1099   if (G_UNLIKELY (pspec_type == 0))
1100     {
1101       const GParamSpecTypeInfo pspec_info = {
1102         sizeof (ClutterParamSpecColor),
1103         16,
1104         param_color_init,
1105         CLUTTER_TYPE_COLOR,
1106         param_color_finalize,
1107         param_color_set_default,
1108         NULL,
1109         param_color_values_cmp,
1110       };
1111
1112       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
1113                                                  &pspec_info);
1114     }
1115
1116   return pspec_type;
1117 }
1118
1119 /**
1120  * clutter_param_spec_color: (skip)
1121  * @name: name of the property
1122  * @nick: short name
1123  * @blurb: description (can be translatable)
1124  * @default_value: default value
1125  * @flags: flags for the param spec
1126  *
1127  * Creates a #GParamSpec for properties using #ClutterColor.
1128  *
1129  * Return value: the newly created #GParamSpec
1130  *
1131  * Since: 0.8.4
1132  */
1133 GParamSpec *
1134 clutter_param_spec_color (const gchar        *name,
1135                           const gchar        *nick,
1136                           const gchar        *blurb,
1137                           const ClutterColor *default_value,
1138                           GParamFlags         flags)
1139 {
1140   ClutterParamSpecColor *cspec;
1141
1142   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
1143                                  name, nick, blurb, flags);
1144
1145   cspec->default_value = clutter_color_copy (default_value);
1146
1147   return G_PARAM_SPEC (cspec);
1148 }