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