* clutter/clutter-color.c (clutter_color_new): Remove CLAMP macros
[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, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 /**
27  * SECTION:clutter-color
28  * @short_description: Color management and manipulation.
29  *
30  * #ClutterColor is a simple type for representing colors.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <pango/pango-attributes.h>
38 #include <gobject/gvaluecollector.h>
39
40 #include "clutter-main.h"
41 #include "clutter-color.h"
42 #include "clutter-private.h"
43 #include "clutter-debug.h"
44
45 /**
46  * clutter_color_add:
47  * @src1: a #ClutterColor
48  * @src2: a #ClutterColor
49  * @dest: return location for the result
50  *
51  * Adds @src2 to @src1 and saves the resulting color
52  * inside @dest.
53  *
54  * The alpha channel of @dest is as the maximum value
55  * between the alpha channels of @src1 and @src2.
56  */
57 void
58 clutter_color_add (const ClutterColor *src1,
59                    const ClutterColor *src2,
60                    ClutterColor       *dest)
61 {
62   g_return_if_fail (src1 != NULL);
63   g_return_if_fail (src2 != NULL);
64   g_return_if_fail (dest != NULL);
65
66   dest->red   = CLAMP (src1->red   + src2->red,   0, 255);
67   dest->green = CLAMP (src1->green + src2->green, 0, 255);
68   dest->blue  = CLAMP (src1->blue  + src2->blue,  0, 255);
69
70   dest->alpha = MAX (src1->alpha, src2->alpha);
71 }
72
73 /**
74  * clutter_color_subtract:
75  * @src1: a #ClutterColor
76  * @src2: a #ClutterColor
77  * @dest: return location for the result
78  *
79  * Subtracts @src2 from @src1 and saves the resulting
80  * color inside @dest. This function assumes that the components
81  * of @src1 are greater than the components of @src2; the result is,
82  * otherwise, undefined.
83  *
84  * The alpha channel of @dest is set as the minimum value
85  * between the alpha channels of @src1 and @src2.
86  */
87 void
88 clutter_color_subtract (const ClutterColor *src1,
89                         const ClutterColor *src2,
90                         ClutterColor       *dest)
91 {
92   g_return_if_fail (src1 != NULL);
93   g_return_if_fail (src2 != NULL);
94   g_return_if_fail (dest != NULL);
95
96   dest->red   = CLAMP (src1->red   - src2->red,   0, 255);
97   dest->green = CLAMP (src1->green - src2->green, 0, 255);
98   dest->blue  = CLAMP (src1->blue  - src2->blue,  0, 255);
99
100   dest->alpha = MIN (src1->alpha, src2->alpha);
101 }
102
103 /**
104  * clutter_color_lighten:
105  * @src: a #ClutterColor
106  * @dest: return location for the lighter color
107  *
108  * Lightens @src by a fixed amount, and saves the changed
109  * color in @dest.
110  */
111 void
112 clutter_color_lighten (const ClutterColor *src,
113                        ClutterColor       *dest)
114 {
115   /* 0x14ccd is ClutterFixed for 1.3 */
116   clutter_color_shadex (src, dest, 0x14ccd);
117 }
118
119 /**
120  * clutter_color_darken:
121  * @src: a #ClutterColor
122  * @dest: return location for the darker color
123  *
124  * Darkens @src by a fixed amount, and saves the changed color
125  * in @dest.
126  */
127 void
128 clutter_color_darken (const ClutterColor *src,
129                       ClutterColor       *dest)
130 {
131   /* 0xb333 is ClutterFixed for 0.7 */
132   clutter_color_shadex (src, dest, 0xb333);
133 }
134
135 /**
136  * clutter_color_to_hlsx:
137  * @src: a #ClutterColor
138  * @hue: return location for the hue value or %NULL
139  * @luminance: return location for the luminance value or %NULL
140  * @saturation: return location for the saturation value or %NULL
141  *
142  * Converts @src to the HLS format. Returned hue is in degrees (0 .. 360),
143  * luminance and saturation from interval <0 .. 1>.
144  */
145 void
146 clutter_color_to_hlsx (const ClutterColor *src,
147                        ClutterFixed       *hue,
148                        ClutterFixed       *luminance,
149                        ClutterFixed       *saturation)
150 {
151   ClutterFixed red, green, blue;
152   ClutterFixed min, max, delta;
153   ClutterFixed h, l, s;
154   
155   g_return_if_fail (src != NULL);
156
157   red   = COGL_FIXED_FROM_INT (src->red)   / 255;
158   green = COGL_FIXED_FROM_INT (src->green) / 255;
159   blue  = COGL_FIXED_FROM_INT (src->blue)  / 255;
160
161   if (red > green)
162     {
163       if (red > blue)
164         max = red;
165       else
166         max = blue;
167
168       if (green < blue)
169         min = green;
170       else
171         min = blue;
172     }
173   else
174     {
175       if (green > blue)
176         max = green;
177       else
178         max = blue;
179
180       if (red < blue)
181         min = red;
182       else
183         min = blue;
184     }
185
186   l = (max + min) / 2;
187   s = 0;
188   h = 0;
189
190   if (max != min)
191     {
192       if (l <= COGL_FIXED_0_5)
193         s = COGL_FIXED_FAST_DIV ((max - min), (max + min));
194       else
195         s = COGL_FIXED_FAST_DIV ((max - min),
196                                  (COGL_FIXED_FROM_INT (2) - max - min));
197
198       delta = max - min;
199
200       if (red == max)
201         h = COGL_FIXED_FAST_DIV ((green - blue), delta);
202       else if (green == max)
203         {
204           h = COGL_FIXED_FROM_INT (2)
205             + COGL_FIXED_FAST_DIV ((blue - red), delta);
206         }
207       else if (blue == max)
208         {
209           h = COGL_FIXED_FROM_INT (4)
210             + COGL_FIXED_FAST_DIV ((red - green), delta);
211         }
212
213       h *= 60;
214
215       if (h < 0)
216         h += COGL_FIXED_360;
217     }
218
219   if (hue)
220     *hue = h;
221
222   if (luminance)
223     *luminance = l;
224
225   if (saturation)
226     *saturation = s;
227 }
228
229 /**
230  * clutter_color_from_hlsx:
231  * @dest: return location for a #ClutterColor
232  * @hue: hue value (0 .. 360)
233  * @luminance: luminance value (0 .. 1)
234  * @saturation: saturation value (0 .. 1)
235  *
236  * Converts a color expressed in HLS (hue, luminance and saturation)
237  * values into a #ClutterColor.
238  */
239
240 void
241 clutter_color_from_hlsx (ClutterColor *dest,
242                          ClutterFixed   hue,
243                          ClutterFixed   luminance,
244                          ClutterFixed   saturation)
245 {
246   ClutterFixed h, l, s;
247   ClutterFixed m1, m2;
248   
249   g_return_if_fail (dest != NULL);
250
251   l = luminance;
252   s = saturation;
253
254   if (l <= COGL_FIXED_0_5)
255     m2 = COGL_FIXED_FAST_MUL (l, (COGL_FIXED_1 + s));
256   else
257     m2 = l + s - COGL_FIXED_FAST_MUL (l, s);
258
259   m1 = 2 * l - m2;
260
261   if (s == 0)
262     {
263       dest->red   = (guint8) COGL_FIXED_TO_INT (l * 255);
264       dest->green = (guint8) COGL_FIXED_TO_INT (l * 255);
265       dest->blue  = (guint8) COGL_FIXED_TO_INT (l * 255);
266     }
267   else
268     {
269       h = hue + COGL_FIXED_120;
270
271       while (h > COGL_FIXED_360)
272         h -= COGL_FIXED_360;
273
274       while (h < 0)
275         h += COGL_FIXED_360;
276
277       if (h < COGL_FIXED_60)
278         {
279           CoglFixed tmp;
280
281           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1), h) / 60);
282           dest->red = (guint8) COGL_FIXED_TO_INT (tmp * 255);
283         }
284       else if (h < COGL_FIXED_180)
285         dest->red = (guint8) COGL_FIXED_TO_INT (m2 * 255);
286       else if (h < COGL_FIXED_240)
287         {
288           CoglFixed tmp;
289
290           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1), (COGL_FIXED_240 - h)))
291               / 60;
292           dest->red = (guint8) COGL_FIXED_TO_INT (tmp * 255);
293         }
294       else
295         dest->red = (guint8) COGL_FIXED_TO_INT (m1 * 255);
296
297       h = hue;
298       while (h > COGL_FIXED_360)
299         h -= COGL_FIXED_360;
300       while (h < 0)
301         h += COGL_FIXED_360;
302
303       if (h < COGL_FIXED_60)
304         {
305           CoglFixed tmp;
306
307           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1), h) / 60);
308           dest->green = (guint8) COGL_FIXED_TO_INT (tmp * 255);
309         }
310       else if (h < COGL_FIXED_180)
311         dest->green = (guint8) COGL_FIXED_TO_INT (m2 * 255);
312       else if (h < COGL_FIXED_240)
313         {
314           CoglFixed tmp;
315
316           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1) , (COGL_FIXED_240 - h)))
317               / 60;
318           dest->green = (guint8) COGL_FIXED_TO_INT (tmp * 255);
319         }
320       else
321         dest->green = (guint8) COGL_FIXED_TO_INT (m1 * 255);
322
323       h = hue - COGL_FIXED_120;
324
325       while (h > COGL_FIXED_360)
326         h -= COGL_FIXED_360;
327
328       while (h < 0)
329         h += COGL_FIXED_360;
330
331       if (h < COGL_FIXED_60)
332         {
333           CoglFixed tmp;
334
335           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1), h) / 60);
336           dest->blue = (guint8) COGL_FIXED_TO_INT (tmp * 255);
337         }
338       else if (h < COGL_FIXED_180)
339         dest->blue = (guint8) COGL_FIXED_TO_INT (m2 * 255);
340       else if (h < COGL_FIXED_240)
341         {
342           CoglFixed tmp;
343
344           tmp = (m1 + COGL_FIXED_FAST_MUL ((m2 - m1), (COGL_FIXED_240 - h)))
345               / 60;
346           dest->blue = (guint8) COGL_FIXED_TO_INT (tmp * 255);
347         }
348       else
349         dest->blue = (guint8) COGL_FIXED_TO_INT (m1 * 255);
350     }
351 }
352
353 /**
354  * clutter_color_to_hls:
355  * @src: a #ClutterColor
356  * @hue: return location for the hue value or %NULL
357  * @luminance: return location for the luminance value or %NULL
358  * @saturation: return location for the saturation value or %NULL
359  *
360  * Converts @src to the HLS format. Returned HLS values are from interval
361  * 0 .. 255.
362  */
363 void
364 clutter_color_to_hls (const ClutterColor *src,
365                       guint8             *hue,
366                       guint8             *luminance,
367                       guint8             *saturation)
368 {
369   ClutterFixed h, l, s;
370   
371   clutter_color_to_hlsx (src, &h, &l, &s);
372   
373   if (hue)
374     *hue = (guint8) COGL_FIXED_TO_INT (h * 255) / 360;
375
376   if (luminance)
377     *luminance = (guint8) COGL_FIXED_TO_INT (l * 255);
378
379   if (saturation)
380     *saturation = (guint8) COGL_FIXED_TO_INT (s * 255);
381 }
382
383 /**
384  * clutter_color_from_hls:
385  * @dest: return location for a #ClutterColor
386  * @hue: hue value (0 .. 255)
387  * @luminance: luminance value (0 .. 255)
388  * @saturation: saturation value (0 .. 255)
389  *
390  * Converts a color expressed in HLS (hue, luminance and saturation)
391  * values into a #ClutterColor.
392  */
393
394 void
395 clutter_color_from_hls (ClutterColor *dest,
396                         guint8        hue,
397                         guint8        luminance,
398                         guint8        saturation)
399 {
400   ClutterFixed h, l, s;
401
402   h = COGL_FIXED_FROM_INT (hue * 360)  / 255;
403   l = COGL_FIXED_FROM_INT (luminance)  / 255;
404   s = COGL_FIXED_FROM_INT (saturation) / 255;
405
406   clutter_color_from_hlsx (dest, h, l, s);
407 }
408
409 /**
410  * clutter_color_shade:
411  * @src: a #ClutterColor
412  * @dest: return location for the shaded color
413  * @shade: the shade factor to apply
414  * 
415  * Shades @src by the factor of @shade and saves the modified
416  * color into @dest.
417  */
418 void
419 clutter_color_shade (const ClutterColor *src,
420                      ClutterColor       *dest,
421                      gdouble             shade)
422 {
423   clutter_color_shadex (src, dest, COGL_FIXED_FROM_FLOAT (shade));
424 }
425
426 /**
427  * clutter_color_shadex:
428  * @src: a #ClutterColor
429  * @dest: return location for the shaded color
430  * @shade: #ClutterFixed the shade factor to apply
431  * 
432  * Fixed point version of clutter_color_shade().
433  *
434  * Shades @src by the factor of @shade and saves the modified
435  * color into @dest.
436  *
437  * Since: 0.2
438  */
439 void
440 clutter_color_shadex (const ClutterColor *src,
441                       ClutterColor       *dest,
442                       ClutterFixed        shade)
443 {
444   ClutterFixed h, l, s;
445
446   g_return_if_fail (src != NULL);
447   g_return_if_fail (dest != NULL);
448   
449   clutter_color_to_hlsx (src, &h, &l, &s);
450
451   l = COGL_FIXED_FAST_MUL (l, shade);
452   if (l > COGL_FIXED_1)
453     l = COGL_FIXED_1;
454   else if (l < 0)
455     l = 0;
456
457   s = COGL_FIXED_FAST_MUL (s, shade);
458   if (s > COGL_FIXED_1)
459     s = COGL_FIXED_1;
460   else if (s < 0)
461     s = 0;
462   
463   clutter_color_from_hlsx (dest, h, l, s);
464   dest->alpha = src->alpha;
465 }
466
467 /**
468  * clutter_color_to_pixel:
469  * @src: a #ClutterColor
470  *
471  * Converts @src into a packed 32 bit integer, containing
472  * all the four 8 bit channels used by #ClutterColor.
473  *
474  * Return value: a packed color
475  */
476 guint32
477 clutter_color_to_pixel (const ClutterColor *src)
478 {
479   g_return_val_if_fail (src != NULL, 0);
480   
481   return (src->alpha | src->blue << 8 | src->green << 16  | src->red << 24);
482 }
483
484 /**
485  * clutter_color_from_pixel:
486  * @dest: return location for a #ClutterColor
487  * @pixel: a 32 bit packed integer containing a color
488  *
489  * Converts @pixel from the packed representation of a four 8 bit channel
490  * color to a #ClutterColor.
491  */
492 void
493 clutter_color_from_pixel (ClutterColor *dest,
494                           guint32       pixel)
495 {
496   g_return_if_fail (dest != NULL);
497
498   dest->red   =  pixel >> 24;
499   dest->green = (pixel >> 16) & 0xff;
500   dest->blue  = (pixel >> 8)  & 0xff;
501   dest->alpha =  pixel        & 0xff;
502 }
503
504 /**
505  * clutter_color_parse:
506  * @color: a string specifiying a color (named color or #RRGGBBAA)
507  * @dest: return location for a #ClutterColor
508  *
509  * Parses a string definition of a color, filling the
510  * <structfield>red</structfield>, <structfield>green</structfield>, 
511  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
512  * channels of @dest. If alpha is not specified it will be set full opaque.
513  * The color in @dest is not allocated.
514  *
515  * The color may be defined by any of the formats understood by
516  * <function>pango_color_parse</function>; these include literal color
517  * names, like <literal>Red</literal> or <literal>DarkSlateGray</literal>,
518  * or hexadecimal specifications like <literal>&num;3050b2</literal> or
519  * <literal>&num;333</literal>.
520  *
521  * Return value: %TRUE if parsing succeeded.
522  *
523  * Since: 0.2
524  */
525 gboolean
526 clutter_color_parse (const gchar  *color,
527                      ClutterColor *dest)
528 {
529   PangoColor pango_color;
530
531   g_return_val_if_fail (color != NULL, FALSE);
532   g_return_val_if_fail (dest != NULL, FALSE);
533
534   /* parse ourselves to get alpha */
535   if (color[0] == '#')
536     {
537       gint32 result;
538
539       if (sscanf (color + 1, "%x", &result))
540         {
541           if (strlen (color) == 9)
542             {
543               dest->red   = (result >> 24) & 0xff;
544               dest->green = (result >> 16) & 0xff;
545               dest->blue  = (result >>  8) & 0xff;
546               dest->alpha = result & 0xff;
547
548               return TRUE;
549             }
550           else if (strlen (color) == 7)
551             {
552               dest->red   = (result >> 16) & 0xff;
553               dest->green = (result >>  8) & 0xff;
554               dest->blue  = result & 0xff;
555               dest->alpha = 0xff;
556
557               return TRUE;
558             }
559         }
560     }
561   
562   /* Fall back to pango for named colors - note pango does not handle alpha */
563   if (pango_color_parse (&pango_color, color))
564     {
565       dest->red   = pango_color.red;
566       dest->green = pango_color.green;
567       dest->blue  = pango_color.blue;
568       dest->alpha = 0xff;
569
570       return TRUE;
571     }
572
573   return FALSE;
574 }
575
576 /**
577  * clutter_color_to_string:
578  * @color: a #ClutterColor
579  *
580  * Returns a textual specification of @color in the hexadecimal form
581  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
582  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
583  * hex digits representing the red, green, blue and alpha components
584  * respectively.
585  *
586  * Return value: a newly-allocated text string
587  *
588  * Since: 0.2
589  */
590 gchar *
591 clutter_color_to_string (const ClutterColor *color)
592 {
593   g_return_val_if_fail (color != NULL, NULL);
594
595   return g_strdup_printf ("#%02x%02x%02x%02x",
596                           color->red,
597                           color->green,
598                           color->blue,
599                           color->alpha);
600 }
601
602 /**
603  * clutter_color_equal:
604  * @a: a #ClutterColor
605  * @b: a #ClutterColor
606  *
607  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
608  *
609  * Return value: %TRUE if the two colors are the same.
610  *
611  * Since: 0.2
612  */
613 gboolean
614 clutter_color_equal (const ClutterColor *a,
615                      const ClutterColor *b)
616 {
617   g_return_val_if_fail (a != NULL, FALSE);
618   g_return_val_if_fail (b != NULL, FALSE);
619
620   if (a == b)
621     return TRUE;
622
623   return (a->red == b->red &&
624           a->green == b->green &&
625           a->blue == b->blue &&
626           a->alpha == b->alpha);
627 }
628
629 /**
630  * clutter_color_copy:
631  * @color: a #ClutterColor
632  *
633  * Makes a copy of the color structure.  The result must be
634  * freed using clutter_color_free().
635  *
636  * Return value: an allocated copy of @color.
637  *
638  * Since: 0.2
639  */
640 ClutterColor *
641 clutter_color_copy (const ClutterColor *color)
642 {
643   ClutterColor *result;
644   
645   g_return_val_if_fail (color != NULL, NULL);
646
647   result = g_slice_new (ClutterColor);
648   *result = *color;
649
650   return result;
651 }
652
653 /**
654  * clutter_color_free:
655  * @color: a #ClutterColor
656  *
657  * Frees a color structure created with clutter_color_copy().
658  *
659  * Since: 0.2
660  */
661 void
662 clutter_color_free (ClutterColor *color)
663 {
664   if (G_LIKELY (color))
665     g_slice_free (ClutterColor, color);
666 }
667
668 /**
669  * clutter_color_new:
670  * @red: red component of the color, between 0 and 255
671  * @green: green component of the color, between 0 and 255
672  * @blue: blue component of the color, between 0 and 255
673  * @alpha: alpha component of the color, between 0 and 255
674  *
675  * Creates a new #ClutterColor with the given values.
676  *
677  * Return value: the newly allocated color. Use clutter_color_free()
678  *   when done
679  *
680  * Since: 0.8.4
681  */
682 ClutterColor *
683 clutter_color_new (guint8 red,
684                    guint8 green,
685                    guint8 blue,
686                    guint8 alpha)
687 {
688   ClutterColor *color;
689
690   color = g_slice_new (ClutterColor);
691
692   color->red   = red;
693   color->green = green;
694   color->blue  = blue;
695   color->alpha = alpha;
696
697   return color;
698 }
699
700 static void
701 clutter_value_transform_color_string (const GValue *src,
702                                       GValue       *dest)
703 {
704   gchar *string = clutter_color_to_string (src->data[0].v_pointer);
705
706   g_value_take_string (dest, string);
707 }
708
709 static void
710 clutter_value_transform_string_color (const GValue *src,
711                                       GValue       *dest)
712 {
713   ClutterColor color = { 0, };
714
715   clutter_color_parse (g_value_get_string (src), &color);
716
717   clutter_value_set_color (dest, &color);
718 }
719
720 GType
721 clutter_color_get_type (void)
722 {
723   static GType _clutter_color_type = 0;
724   
725   if (G_UNLIKELY (_clutter_color_type == 0))
726     {
727        _clutter_color_type =
728          g_boxed_type_register_static (I_("ClutterColor"),
729                                        (GBoxedCopyFunc) clutter_color_copy,
730                                        (GBoxedFreeFunc) clutter_color_free);
731
732        g_value_register_transform_func (_clutter_color_type, G_TYPE_STRING,
733                                         clutter_value_transform_color_string);
734        g_value_register_transform_func (G_TYPE_STRING, _clutter_color_type,
735                                         clutter_value_transform_string_color);
736     }
737
738   return _clutter_color_type;
739 }
740
741 static void
742 clutter_value_init_color (GValue *value)
743 {
744   value->data[0].v_pointer = NULL;
745 }
746
747 static void
748 clutter_value_free_color (GValue *value)
749 {
750   if (!(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
751     clutter_color_free (value->data[0].v_pointer);
752 }
753
754 static void
755 clutter_value_copy_color (const GValue *src,
756                           GValue       *dest)
757 {
758   dest->data[0].v_pointer = clutter_color_copy (src->data[0].v_pointer);
759 }
760
761 static gpointer
762 clutter_value_peek_color (const GValue *value)
763 {
764   return value->data[0].v_pointer;
765 }
766
767 static gchar *
768 clutter_value_collect_color (GValue      *value,
769                              guint        n_collect_values,
770                              GTypeCValue *collect_values,
771                              guint        collect_flags)
772 {
773   if (!collect_values[0].v_pointer)
774       value->data[0].v_pointer = NULL;
775   else
776     {
777       if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
778         {
779           value->data[0].v_pointer = collect_values[0].v_pointer;
780           value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
781         }
782       else
783         {
784           value->data[0].v_pointer =
785             clutter_color_copy (collect_values[0].v_pointer);
786         }
787     }
788
789   return NULL;
790 }
791
792 static gchar *
793 clutter_value_lcopy_color (const GValue *value,
794                            guint         n_collect_values,
795                            GTypeCValue  *collect_values,
796                            guint         collect_flags)
797 {
798   ClutterColor **color_p = collect_values[0].v_pointer;
799
800   if (!color_p)
801     return g_strdup_printf ("value location for `%s' passed as NULL",
802                             G_VALUE_TYPE_NAME (value));
803
804   if (!value->data[0].v_pointer)
805     *color_p = NULL;
806   else
807     {
808       if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
809         *color_p = value->data[0].v_pointer;
810       else
811         *color_p = clutter_color_copy (value->data[0].v_pointer);
812     }
813
814   return NULL;
815 }
816
817 /**
818  * clutter_value_set_color:
819  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
820  * @color: the color to set
821  *
822  * Sets @value to @color.
823  *
824  * Since: 0.8.4
825  */
826 void
827 clutter_value_set_color (GValue             *value,
828                          const ClutterColor *color)
829 {
830   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
831
832   value->data[0].v_pointer = clutter_color_copy (color);
833 }
834
835 /**
836  * clutter_value_get_color:
837  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
838  *
839  * Gets the #ClutterColor contained in @value.
840  *
841  * Return value: the colors inside the passed #GValue
842  *
843  * Since: 0.8.4
844  */
845 G_CONST_RETURN ClutterColor *
846 clutter_value_get_color (const GValue *value)
847 {
848   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
849
850   return value->data[0].v_pointer;
851 }
852
853 static void
854 param_color_init (GParamSpec *pspec)
855 {
856   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
857
858   cspec->default_value = NULL;
859 }
860
861 static void
862 param_color_finalize (GParamSpec *pspec)
863 {
864   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
865
866   clutter_color_free (cspec->default_value);
867 }
868
869 static void
870 param_color_set_default (GParamSpec *pspec,
871                         GValue     *value)
872 {
873   value->data[0].v_pointer = CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
874   value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
875 }
876
877 static gint
878 param_color_values_cmp (GParamSpec   *pspec,
879                         const GValue *value1,
880                         const GValue *value2)
881 {
882   guint32 color1, color2;
883
884   color1 = clutter_color_to_pixel (value1->data[0].v_pointer);
885   color2 = clutter_color_to_pixel (value2->data[0].v_pointer);
886
887   if (color1 < color2)
888     return -1;
889   else if (color1 == color2)
890     return 0;
891   else
892     return 1;
893 }
894
895 static const GTypeValueTable _clutter_color_value_table = {
896   clutter_value_init_color,
897   clutter_value_free_color,
898   clutter_value_copy_color,
899   clutter_value_peek_color,
900   "p",
901   clutter_value_collect_color,
902   "p",
903   clutter_value_lcopy_color
904 };
905
906 GType
907 clutter_param_color_get_type (void)
908 {
909   static GType pspec_type = 0;
910
911   if (G_UNLIKELY (pspec_type == 0))
912     {
913       const GParamSpecTypeInfo pspec_info = {
914         sizeof (ClutterParamSpecColor),
915         16,
916         param_color_init,
917         CLUTTER_TYPE_COLOR,
918         param_color_finalize,
919         param_color_set_default,
920         NULL,
921         param_color_values_cmp,
922       };
923
924       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
925                                                  &pspec_info);
926     }
927
928   return pspec_type;
929 }
930
931 /**
932  * clutter_param_spec_color:
933  * @name: name of the property
934  * @nick: short name
935  * @blurb: description (can be translatable)
936  * @default_value: default value
937  * @flags: flags for the param spec
938  *
939  * Creates a #GParamSpec for properties using #ClutterColor.
940  *
941  * Return value: the newly created #GParamSpec
942  *
943  * Since: 0.8.4
944  */
945 GParamSpec *
946 clutter_param_spec_color (const gchar        *name,
947                           const gchar        *nick,
948                           const gchar        *blurb,
949                           const ClutterColor *default_value,
950                           GParamFlags         flags)
951 {
952   ClutterParamSpecColor *cspec;
953
954   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
955                                  name, nick, blurb, flags);
956
957   cspec->default_value = clutter_color_copy (default_value);
958
959   return G_PARAM_SPEC (cspec);
960 }