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