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