Merge branch 'wip/static-colors'
[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 /**
445  * clutter_color_from_string:
446  * @color: (out caller-allocates): return location for a #ClutterColor
447  * @str: a string specifiying a color (named color or #RRGGBBAA)
448  *
449  * Parses a string definition of a color, filling the
450  * <structfield>red</structfield>, <structfield>green</structfield>, 
451  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
452  * channels of @color. If alpha is not specified it will be set full opaque.
453  *
454  * The @color is not allocated.
455  *
456  * The color may be defined by any of the formats understood by
457  * pango_color_from_string(); these include literal color names, like
458  * <literal>Red</literal> or <literal>DarkSlateGray</literal>, or
459  * hexadecimal specifications like <literal>&num;3050b2</literal> or
460  * <literal>&num;333</literal>.
461  *
462  * Return value: %TRUE if parsing succeeded.
463  *
464  * Since: 1.0
465  */
466 gboolean
467 clutter_color_from_string (ClutterColor *color,
468                            const gchar  *str)
469 {
470   PangoColor pango_color = { 0, };
471
472   g_return_val_if_fail (color != NULL, FALSE);
473   g_return_val_if_fail (str != NULL, FALSE);
474
475   /* if the string contains a color encoded using the hexadecimal
476    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
477    * parsing the color ourselves, as we need the alpha channel that
478    * Pango can't retrieve.
479    */
480   if (str[0] == '#')
481     {
482       gint32 result;
483
484       if (sscanf (str + 1, "%x", &result))
485         {
486           gsize length = strlen (str);
487
488           switch (length)
489             {
490             case 9: /* rrggbbaa */
491               color->red   = (result >> 24) & 0xff;
492               color->green = (result >> 16) & 0xff;
493               color->blue  = (result >>  8) & 0xff;
494
495               color->alpha = result & 0xff;
496
497               return TRUE;
498
499             case 7: /* #rrggbb */
500               color->red   = (result >> 16) & 0xff;
501               color->green = (result >>  8) & 0xff;
502               color->blue  = result & 0xff;
503
504               color->alpha = 0xff;
505
506               return TRUE;
507
508             case 5: /* #rgba */
509               color->red   = ((result >> 12) & 0xf);
510               color->green = ((result >>  8) & 0xf);
511               color->blue  = ((result >>  4) & 0xf);
512               color->alpha = result & 0xf;
513
514               color->red   = (color->red   << 4) | color->red;
515               color->green = (color->green << 4) | color->green;
516               color->blue  = (color->blue  << 4) | color->blue;
517               color->alpha = (color->alpha << 4) | color->alpha;
518
519               return TRUE;
520
521             case 4: /* #rgb */
522               color->red   = ((result >>  8) & 0xf);
523               color->green = ((result >>  4) & 0xf);
524               color->blue  = result & 0xf;
525
526               color->red   = (color->red   << 4) | color->red;
527               color->green = (color->green << 4) | color->green;
528               color->blue  = (color->blue  << 4) | color->blue;
529
530               color->alpha = 0xff;
531
532               return TRUE;
533
534             default:
535               /* pass through to Pango */
536               break;
537             }
538         }
539     }
540   
541   /* Fall back to pango for named colors */
542   if (pango_color_parse (&pango_color, str))
543     {
544       color->red   = pango_color.red;
545       color->green = pango_color.green;
546       color->blue  = pango_color.blue;
547
548       color->alpha = 0xff;
549
550       return TRUE;
551     }
552
553   return FALSE;
554 }
555
556 /**
557  * clutter_color_to_string:
558  * @color: a #ClutterColor
559  *
560  * Returns a textual specification of @color in the hexadecimal form
561  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
562  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
563  * hex digits representing the red, green, blue and alpha components
564  * respectively.
565  *
566  * Return value: (transfer full): a newly-allocated text string
567  *
568  * Since: 0.2
569  */
570 gchar *
571 clutter_color_to_string (const ClutterColor *color)
572 {
573   g_return_val_if_fail (color != NULL, NULL);
574
575   return g_strdup_printf ("#%02x%02x%02x%02x",
576                           color->red,
577                           color->green,
578                           color->blue,
579                           color->alpha);
580 }
581
582 /**
583  * clutter_color_equal:
584  * @v1: a #ClutterColor
585  * @v2: a #ClutterColor
586  *
587  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
588  *
589  * This function can be passed to g_hash_table_new() as the @key_equal_func
590  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
591  *
592  * Return value: %TRUE if the two colors are the same.
593  *
594  * Since: 0.2
595  */
596 gboolean
597 clutter_color_equal (gconstpointer v1,
598                      gconstpointer v2)
599 {
600   const ClutterColor *a, *b;
601
602   g_return_val_if_fail (v1 != NULL, FALSE);
603   g_return_val_if_fail (v2 != NULL, FALSE);
604
605   if (v1 == v2)
606     return TRUE;
607
608   a = v1;
609   b = v2;
610
611   return (a->red   == b->red   &&
612           a->green == b->green &&
613           a->blue  == b->blue  &&
614           a->alpha == b->alpha);
615 }
616
617 /**
618  * clutter_color_hash:
619  * @v: a #ClutterColor
620  *
621  * Converts a #ClutterColor to a hash value.
622  *
623  * This function can be passed to g_hash_table_new() as the @hash_func
624  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
625  *
626  * Return value: a hash value corresponding to the color
627  *
628  * Since: 1.0
629  */
630 guint
631 clutter_color_hash (gconstpointer v)
632 {
633   return clutter_color_to_pixel ((const ClutterColor *) v);
634 }
635
636 /**
637  * clutter_color_interpolate:
638  * @initial: the initial #ClutterColor
639  * @final: the final #ClutterColor
640  * @progress: the interpolation progress
641  * @result: (out): return location for the interpolation
642  *
643  * Interpolates between @initial and @final #ClutterColor<!-- -->s
644  * using @progress
645  *
646  * Since: 1.6
647  */
648 void
649 clutter_color_interpolate (const ClutterColor *initial,
650                            const ClutterColor *final,
651                            gdouble             progress,
652                            ClutterColor       *result)
653 {
654   g_return_if_fail (initial != NULL);
655   g_return_if_fail (final != NULL);
656   g_return_if_fail (result != NULL);
657
658   result->red   = initial->red   + (final->red   - initial->red)   * progress;
659   result->green = initial->green + (final->green - initial->green) * progress;
660   result->blue  = initial->blue  + (final->blue  - initial->blue)  * progress;
661   result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress;
662 }
663
664 static gboolean
665 clutter_color_progress (const GValue *a,
666                         const GValue *b,
667                         gdouble       progress,
668                         GValue       *retval)
669 {
670   const ClutterColor *a_color = clutter_value_get_color (a);
671   const ClutterColor *b_color = clutter_value_get_color (b);
672   ClutterColor res = { 0, };
673
674   clutter_color_interpolate (a_color, b_color, progress, &res);
675   clutter_value_set_color (retval, &res);
676
677   return TRUE;
678 }
679
680 /**
681  * clutter_color_copy:
682  * @color: a #ClutterColor
683  *
684  * Makes a copy of the color structure.  The result must be
685  * freed using clutter_color_free().
686  *
687  * Return value: (transfer full): an allocated copy of @color.
688  *
689  * Since: 0.2
690  */
691 ClutterColor *
692 clutter_color_copy (const ClutterColor *color)
693 {
694   if (G_LIKELY (color != NULL))
695     return g_slice_dup (ClutterColor, color);
696
697   return NULL;
698 }
699
700 /**
701  * clutter_color_free:
702  * @color: a #ClutterColor
703  *
704  * Frees a color structure created with clutter_color_copy().
705  *
706  * Since: 0.2
707  */
708 void
709 clutter_color_free (ClutterColor *color)
710 {
711   if (G_LIKELY (color != NULL))
712     g_slice_free (ClutterColor, color);
713 }
714
715 /**
716  * clutter_color_new:
717  * @red: red component of the color, between 0 and 255
718  * @green: green component of the color, between 0 and 255
719  * @blue: blue component of the color, between 0 and 255
720  * @alpha: alpha component of the color, between 0 and 255
721  *
722  * Creates a new #ClutterColor with the given values.
723  *
724  * Return value: (transfer full): the newly allocated color.
725  *   Use clutter_color_free() when done
726  *
727  * Since: 0.8.4
728  */
729 ClutterColor *
730 clutter_color_new (guint8 red,
731                    guint8 green,
732                    guint8 blue,
733                    guint8 alpha)
734 {
735   ClutterColor *color;
736
737   color = g_slice_new (ClutterColor);
738
739   color->red   = red;
740   color->green = green;
741   color->blue  = blue;
742   color->alpha = alpha;
743
744   return color;
745 }
746
747 static void
748 clutter_value_transform_color_string (const GValue *src,
749                                       GValue       *dest)
750 {
751   const ClutterColor *color = g_value_get_boxed (src);
752
753   if (color)
754     {
755       gchar *string = clutter_color_to_string (color);
756
757       g_value_take_string (dest, string);
758     }
759   else
760     g_value_set_string (dest, NULL);
761 }
762
763 static void
764 clutter_value_transform_string_color (const GValue *src,
765                                       GValue       *dest)
766 {
767   const char *str = g_value_get_string (src);
768
769   if (str)
770     {
771       ClutterColor color = { 0, };
772
773       clutter_color_from_string (&color, str);
774
775       clutter_value_set_color (dest, &color);
776     }
777   else
778     clutter_value_set_color (dest, NULL);
779 }
780
781 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color,
782                                clutter_color_copy,
783                                clutter_color_free,
784                                CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string)
785                                CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color)
786                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress));
787
788 /**
789  * clutter_value_set_color:
790  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
791  * @color: the color to set
792  *
793  * Sets @value to @color.
794  *
795  * Since: 0.8.4
796  */
797 void
798 clutter_value_set_color (GValue             *value,
799                          const ClutterColor *color)
800 {
801   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
802
803   g_value_set_boxed (value, color);
804 }
805
806 /**
807  * clutter_value_get_color:
808  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
809  *
810  * Gets the #ClutterColor contained in @value.
811  *
812  * Return value: (transfer none): the color inside the passed #GValue
813  *
814  * Since: 0.8.4
815  */
816 G_CONST_RETURN ClutterColor *
817 clutter_value_get_color (const GValue *value)
818 {
819   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
820
821   return g_value_get_boxed (value);
822 }
823
824 static void
825 param_color_init (GParamSpec *pspec)
826 {
827   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
828
829   cspec->default_value = NULL;
830 }
831
832 static void
833 param_color_finalize (GParamSpec *pspec)
834 {
835   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
836
837   clutter_color_free (cspec->default_value);
838 }
839
840 static void
841 param_color_set_default (GParamSpec *pspec,
842                          GValue     *value)
843 {
844   const ClutterColor *default_value =
845     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
846   clutter_value_set_color (value, default_value);
847 }
848
849 static gint
850 param_color_values_cmp (GParamSpec   *pspec,
851                         const GValue *value1,
852                         const GValue *value2)
853 {
854   const ClutterColor *color1 = g_value_get_boxed (value1);
855   const ClutterColor *color2 = g_value_get_boxed (value2);
856   int pixel1, pixel2;
857
858   if (color1 == NULL)
859     return color2 == NULL ? 0 : -1;
860
861   pixel1 = clutter_color_to_pixel (color1);
862   pixel2 = clutter_color_to_pixel (color2);
863
864   if (pixel1 < pixel2)
865     return -1;
866   else if (pixel1 == pixel2)
867     return 0;
868   else
869     return 1;
870 }
871
872 GType
873 clutter_param_color_get_type (void)
874 {
875   static GType pspec_type = 0;
876
877   if (G_UNLIKELY (pspec_type == 0))
878     {
879       const GParamSpecTypeInfo pspec_info = {
880         sizeof (ClutterParamSpecColor),
881         16,
882         param_color_init,
883         CLUTTER_TYPE_COLOR,
884         param_color_finalize,
885         param_color_set_default,
886         NULL,
887         param_color_values_cmp,
888       };
889
890       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
891                                                  &pspec_info);
892     }
893
894   return pspec_type;
895 }
896
897 /**
898  * clutter_param_spec_color: (skip)
899  * @name: name of the property
900  * @nick: short name
901  * @blurb: description (can be translatable)
902  * @default_value: default value
903  * @flags: flags for the param spec
904  *
905  * Creates a #GParamSpec for properties using #ClutterColor.
906  *
907  * Return value: the newly created #GParamSpec
908  *
909  * Since: 0.8.4
910  */
911 GParamSpec *
912 clutter_param_spec_color (const gchar        *name,
913                           const gchar        *nick,
914                           const gchar        *blurb,
915                           const ClutterColor *default_value,
916                           GParamFlags         flags)
917 {
918   ClutterParamSpecColor *cspec;
919
920   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
921                                  name, nick, blurb, flags);
922
923   cspec->default_value = clutter_color_copy (default_value);
924
925   return G_PARAM_SPEC (cspec);
926 }