Eliminate G_CONST_RETURN
[profile/ivi/clutter.git] / clutter / clutter-color.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /**
25  * SECTION:clutter-color
26  * @short_description: Color management and manipulation.
27  *
28  * #ClutterColor is a simple type for representing colors in Clutter.
29  *
30  * A #ClutterColor is expressed as a 4-tuple of values ranging from
31  * zero to 255, one for each color channel plus one for the alpha.
32  *
33  * The alpha channel is fully opaque at 255 and fully transparent at 0.
34  */
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <math.h>
41
42 #include <pango/pango-attributes.h>
43
44 #include "clutter-interval.h"
45 #include "clutter-main.h"
46 #include "clutter-color.h"
47 #include "clutter-private.h"
48 #include "clutter-debug.h"
49
50 /* XXX - keep in sync with the ClutterStaticColor enumeration order */
51 static const ClutterColor const static_colors[] = {
52   /* CGA/EGA color palette */
53   { 0xff, 0xff, 0xff, 0xff },   /* white */
54   { 0x00, 0x00, 0x00, 0xff },   /* black */
55   { 0xff, 0x00, 0x00, 0xff },   /* red */
56   { 0x80, 0x00, 0x00, 0xff },   /* dark red */
57   { 0x00, 0xff, 0x00, 0xff },   /* green */
58   { 0x00, 0x80, 0x00, 0xff },   /* dark green */
59   { 0x00, 0x00, 0xff, 0xff },   /* blue */
60   { 0x00, 0x00, 0x80, 0xff },   /* dark blue */
61   { 0x00, 0xff, 0xff, 0xff },   /* cyan */
62   { 0x00, 0x80, 0x80, 0xff },   /* dark cyan */
63   { 0xff, 0x00, 0xff, 0xff },   /* magenta */
64   { 0x80, 0x00, 0x80, 0xff },   /* dark magenta */
65   { 0xff, 0xff, 0x00, 0xff },   /* yellow */
66   { 0x80, 0x80, 0x00, 0xff },   /* dark yellow */
67   { 0xa0, 0xa0, 0xa4, 0xff },   /* gray */
68   { 0x80, 0x80, 0x80, 0xff },   /* dark gray */
69   { 0xc0, 0xc0, 0xc0, 0xff },   /* light gray */
70
71   /* Tango Icon color palette */
72   { 0xed, 0xd4, 0x00, 0xff },   /* butter */
73   { 0xfc, 0xe9, 0x4f, 0xff },   /* butter light */
74   { 0xc4, 0xa0, 0x00, 0xff },   /* butter dark */
75   { 0xf5, 0x79, 0x00, 0xff },   /* orange */
76   { 0xfc, 0xaf, 0x3e, 0xff },   /* orange light */
77   { 0xce, 0x5c, 0x00, 0xff },   /* orange dark */
78   { 0xc1, 0x7d, 0x11, 0xff },   /* chocolate */
79   { 0xe9, 0xb9, 0x6e, 0xff },   /* chocolate light */
80   { 0x8f, 0x59, 0x02, 0xff },   /* chocolate dark */
81   { 0x73, 0xd2, 0x16, 0xff },   /* chameleon */
82   { 0x8a, 0xe2, 0x34, 0xff },   /* chameleon light */
83   { 0x4e, 0x9a, 0x06, 0xff },   /* chameleon dark */
84   { 0x34, 0x65, 0xa4, 0xff },   /* sky blue */
85   { 0x72, 0x9f, 0xcf, 0xff },   /* sky blue light */
86   { 0x20, 0x4a, 0x87, 0xff },   /* sky blue dark */
87   { 0x75, 0x50, 0x7b, 0xff },   /* plum */
88   { 0xad, 0x7f, 0xa8, 0xff },   /* plum light */
89   { 0x5c, 0x35, 0x66, 0xff },   /* plum dark */
90   { 0xcc, 0x00, 0x00, 0xff },   /* scarlet red */
91   { 0xef, 0x29, 0x29, 0xff },   /* scarlet red light */
92   { 0xa4, 0x00, 0x00, 0xff },   /* scarlet red dark */
93   { 0xee, 0xee, 0xec, 0xff },   /* aluminium 1 */
94   { 0xd3, 0xd7, 0xcf, 0xff },   /* aluminium 2 */
95   { 0xba, 0xbd, 0xb6, 0xff },   /* aluminium 3 */
96   { 0x88, 0x8a, 0x85, 0xff },   /* aluminium 4 */
97   { 0x55, 0x57, 0x53, 0xff },   /* aluminium 5 */
98   { 0x2e, 0x34, 0x36, 0xff },   /* aluminium 6 */
99
100   /* last color */
101   { 0x00, 0x00, 0x00, 0x00 }    /* transparent */
102 };
103
104 /**
105  * clutter_color_get_static:
106  * @color: the named global color
107  *
108  * Retrieves a static color for the given @color name
109  *
110  * Static colors are created by Clutter and are guaranteed to always be
111  * available and valid
112  *
113  * Return value: a pointer to a static color; the returned pointer
114  *   is owned by Clutter and it should never be modified or freed
115  *
116  * Since: 1.6
117  */
118 const ClutterColor *
119 clutter_color_get_static (ClutterStaticColor color)
120 {
121   g_return_val_if_fail (color >= CLUTTER_COLOR_WHITE &&
122                         color <= CLUTTER_COLOR_TRANSPARENT, NULL);
123
124   return &static_colors[color];
125 }
126
127 /**
128  * clutter_color_add:
129  * @a: a #ClutterColor
130  * @b: a #ClutterColor
131  * @result: (out caller-allocates): return location for the result
132  *
133  * Adds @a to @b and saves the resulting color inside @result.
134  *
135  * The alpha channel of @result is set as as the maximum value
136  * between the alpha channels of @a and @b.
137  */
138 void
139 clutter_color_add (const ClutterColor *a,
140                    const ClutterColor *b,
141                    ClutterColor       *result)
142 {
143   g_return_if_fail (a != NULL);
144   g_return_if_fail (b != NULL);
145   g_return_if_fail (result != NULL);
146
147   result->red   = CLAMP (a->red   + b->red,   0, 255);
148   result->green = CLAMP (a->green + b->green, 0, 255);
149   result->blue  = CLAMP (a->blue  + b->blue,  0, 255);
150
151   result->alpha = MAX (a->alpha, b->alpha);
152 }
153
154 /**
155  * clutter_color_subtract:
156  * @a: a #ClutterColor
157  * @b: a #ClutterColor
158  * @result: (out caller-allocates): return location for the result
159  *
160  * Subtracts @b from @a and saves the resulting color inside @result.
161  *
162  * This function assumes that the components of @a are greater than the
163  * components of @b; the result is, otherwise, undefined.
164  *
165  * The alpha channel of @result is set as the minimum value
166  * between the alpha channels of @a and @b.
167  */
168 void
169 clutter_color_subtract (const ClutterColor *a,
170                         const ClutterColor *b,
171                         ClutterColor       *result)
172 {
173   g_return_if_fail (a != NULL);
174   g_return_if_fail (b != NULL);
175   g_return_if_fail (result != NULL);
176
177   result->red   = CLAMP (a->red   - b->red,   0, 255);
178   result->green = CLAMP (a->green - b->green, 0, 255);
179   result->blue  = CLAMP (a->blue  - b->blue,  0, 255);
180
181   result->alpha = MIN (a->alpha, b->alpha);
182 }
183
184 /**
185  * clutter_color_lighten:
186  * @color: a #ClutterColor
187  * @result: (out caller-allocates): return location for the lighter color
188  *
189  * Lightens @color by a fixed amount, and saves the changed color
190  * in @result.
191  */
192 void
193 clutter_color_lighten (const ClutterColor *color,
194                        ClutterColor       *result)
195 {
196   clutter_color_shade (color, 1.3, result);
197 }
198
199 /**
200  * clutter_color_darken:
201  * @color: a #ClutterColor
202  * @result: (out caller-allocates): return location for the darker color
203  *
204  * Darkens @color by a fixed amount, and saves the changed color
205  * in @result.
206  */
207 void
208 clutter_color_darken (const ClutterColor *color,
209                       ClutterColor       *result)
210 {
211   clutter_color_shade (color, 0.7, result);
212 }
213
214 /**
215  * clutter_color_to_hls:
216  * @color: a #ClutterColor
217  * @hue: (out): return location for the hue value or %NULL
218  * @luminance: (out): return location for the luminance value or %NULL
219  * @saturation: (out): return location for the saturation value or %NULL
220  *
221  * Converts @color to the HLS format.
222  *
223  * The @hue value is in the 0 .. 360 range. The @luminance and
224  * @saturation values are in the 0 .. 1 range.
225  */
226 void
227 clutter_color_to_hls (const ClutterColor *color,
228                       float              *hue,
229                       float              *luminance,
230                       float              *saturation)
231 {
232   float red, green, blue;
233   float min, max, delta;
234   float h, l, s;
235   
236   g_return_if_fail (color != NULL);
237
238   red   = color->red / 255.0;
239   green = color->green / 255.0;
240   blue  = color->blue / 255.0;
241
242   if (red > green)
243     {
244       if (red > blue)
245         max = red;
246       else
247         max = blue;
248
249       if (green < blue)
250         min = green;
251       else
252         min = blue;
253     }
254   else
255     {
256       if (green > blue)
257         max = green;
258       else
259         max = blue;
260
261       if (red < blue)
262         min = red;
263       else
264         min = blue;
265     }
266
267   l = (max + min) / 2;
268   s = 0;
269   h = 0;
270
271   if (max != min)
272     {
273       if (l <= 0.5)
274         s = (max - min) / (max + min);
275       else
276         s = (max - min) / (2.0 - max - min);
277
278       delta = max - min;
279
280       if (red == max)
281         h = (green - blue) / delta;
282       else if (green == max)
283         h = 2.0 + (blue - red) / delta;
284       else if (blue == max)
285         h = 4.0 + (red - green) / delta;
286
287       h *= 60;
288
289       if (h < 0)
290         h += 360.0;
291     }
292
293   if (hue)
294     *hue = h;
295
296   if (luminance)
297     *luminance = l;
298
299   if (saturation)
300     *saturation = s;
301 }
302
303 /**
304  * clutter_color_from_hls:
305  * @color: (out): return location for a #ClutterColor
306  * @hue: hue value, in the 0 .. 360 range
307  * @luminance: luminance value, in the 0 .. 1 range
308  * @saturation: saturation value, in the 0 .. 1 range
309  *
310  * Converts a color expressed in HLS (hue, luminance and saturation)
311  * values into a #ClutterColor.
312  */
313 void
314 clutter_color_from_hls (ClutterColor *color,
315                         float         hue,
316                         float         luminance,
317                         float         saturation)
318 {
319   float tmp1, tmp2;
320   float tmp3[3];
321   float clr[3];
322   int   i;
323
324   hue /= 360.0;
325
326   if (saturation == 0)
327     {
328       color->red = color->green = color->blue = (luminance * 255);
329
330       return;
331     }
332
333   if (luminance <= 0.5)
334     tmp2 = luminance * (1.0 + saturation);
335   else
336     tmp2 = luminance + saturation - (luminance * saturation);
337
338   tmp1 = 2.0 * luminance - tmp2;
339
340   tmp3[0] = hue + 1.0 / 3.0;
341   tmp3[1] = hue;
342   tmp3[2] = hue - 1.0 / 3.0;
343
344   for (i = 0; i < 3; i++)
345     {
346       if (tmp3[i] < 0)
347         tmp3[i] += 1.0;
348
349       if (tmp3[i] > 1)
350         tmp3[i] -= 1.0;
351
352       if (6.0 * tmp3[i] < 1.0)
353         clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0;
354       else if (2.0 * tmp3[i] < 1.0)
355         clr[i] = tmp2;
356       else if (3.0 * tmp3[i] < 2.0)
357         clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0);
358       else
359         clr[i] = tmp1;
360     }
361
362   color->red   = floorf (clr[0] * 255.0 + 0.5);
363   color->green = floorf (clr[1] * 255.0 + 0.5);
364   color->blue  = floorf (clr[2] * 255.0 + 0.5);
365 }
366
367 /**
368  * clutter_color_shade:
369  * @color: a #ClutterColor
370  * @factor: the shade factor to apply
371  * @result: (out caller-allocates): return location for the shaded color
372  *
373  * Shades @color by @factor and saves the modified color into @result.
374  */
375 void
376 clutter_color_shade (const ClutterColor *color,
377                      gdouble             factor,
378                      ClutterColor       *result)
379 {
380   float h, l, s;
381
382   g_return_if_fail (color != NULL);
383   g_return_if_fail (result != NULL);
384   
385   clutter_color_to_hls (color, &h, &l, &s);
386
387   l *= factor;
388   if (l > 1.0)
389     l = 1.0;
390   else if (l < 0)
391     l = 0;
392
393   s *= factor;
394   if (s > 1.0)
395     s = 1.0;
396   else if (s < 0)
397     s = 0;
398   
399   clutter_color_from_hls (result, h, l, s);
400
401   result->alpha = color->alpha;
402 }
403
404 /**
405  * clutter_color_to_pixel:
406  * @color: a #ClutterColor
407  *
408  * Converts @color into a packed 32 bit integer, containing
409  * all the four 8 bit channels used by #ClutterColor.
410  *
411  * Return value: a packed color
412  */
413 guint32
414 clutter_color_to_pixel (const ClutterColor *color)
415 {
416   g_return_val_if_fail (color != NULL, 0);
417   
418   return (color->alpha       |
419           color->blue  << 8  |
420           color->green << 16 |
421           color->red   << 24);
422 }
423
424 /**
425  * clutter_color_from_pixel:
426  * @color: (out caller-allocates): return location for a #ClutterColor
427  * @pixel: a 32 bit packed integer containing a color
428  *
429  * Converts @pixel from the packed representation of a four 8 bit channel
430  * color to a #ClutterColor.
431  */
432 void
433 clutter_color_from_pixel (ClutterColor *color,
434                           guint32       pixel)
435 {
436   g_return_if_fail (color != NULL);
437
438   color->red   =  pixel >> 24;
439   color->green = (pixel >> 16) & 0xff;
440   color->blue  = (pixel >> 8)  & 0xff;
441   color->alpha =  pixel        & 0xff;
442 }
443
444 static inline void
445 skip_whitespace (gchar **str)
446 {
447   while (g_ascii_isspace (**str))
448     *str += 1;
449 }
450
451 static inline void
452 parse_rgb_value (gchar   *str,
453                  guint8  *color,
454                  gchar  **endp)
455 {
456   gdouble number;
457   gchar *p;
458
459   skip_whitespace (&str);
460
461   number = g_ascii_strtod (str, endp);
462
463   p = *endp;
464
465   skip_whitespace (&p);
466
467   if (*p == '%')
468     {
469       *endp = (gchar *) (p + 1);
470
471       *color = CLAMP (number / 100.0, 0.0, 1.0) * 255;
472     }
473   else
474     *color = CLAMP (number, 0, 255);
475 }
476
477 static gboolean
478 parse_rgba (ClutterColor *color,
479             gchar        *str,
480             gboolean      has_alpha)
481 {
482   skip_whitespace (&str);
483
484   if (*str != '(')
485     return FALSE;
486
487   str += 1;
488
489   /* red */
490   parse_rgb_value (str, &color->red, &str);
491   skip_whitespace (&str);
492   if (*str != ',')
493     return FALSE;
494
495   str += 1;
496
497   /* green */
498   parse_rgb_value (str, &color->green, &str);
499   skip_whitespace (&str);
500   if (*str != ',')
501     return FALSE;
502
503   str += 1;
504
505   /* blue */
506   parse_rgb_value (str, &color->blue, &str);
507   skip_whitespace (&str);
508
509   /* alpha (optional); since the alpha channel value can only
510    * be between 0 and 1 we don't use the parse_rgb_value()
511    * function
512    */
513   if (has_alpha)
514     {
515       gdouble number;
516
517       if (*str != ',')
518         return FALSE;
519
520       str += 1;
521
522       skip_whitespace (&str);
523       number = g_ascii_strtod (str, &str);
524
525       color->alpha = CLAMP (number * 255.0, 0, 255);
526     }
527   else
528     color->alpha = 255;
529
530   skip_whitespace (&str);
531   if (*str != ')')
532     return FALSE;
533
534   return TRUE;
535 }
536
537 static gboolean
538 parse_hsla (ClutterColor *color,
539             gchar        *str,
540             gboolean      has_alpha)
541 {
542   gdouble number;
543   gdouble h, l, s;
544
545   skip_whitespace (&str);
546
547   if (*str != '(')
548     return FALSE;
549
550   str += 1;
551
552   /* hue */
553   skip_whitespace (&str);
554   /* we don't do any angle normalization here because
555    * clutter_color_from_hls() will do it for us
556    */
557   number = g_ascii_strtod (str, &str);
558   skip_whitespace (&str);
559   if (*str != ',')
560     return FALSE;
561
562   h = number;
563
564   str += 1;
565
566   /* saturation */
567   skip_whitespace (&str);
568   number = g_ascii_strtod (str, &str);
569   skip_whitespace (&str);
570   if (*str != '%')
571     return FALSE;
572
573   str += 1;
574
575   s = CLAMP (number / 100.0, 0.0, 1.0);
576   skip_whitespace (&str);
577   if (*str != ',')
578     return FALSE;
579
580   str += 1;
581
582   /* luminance */
583   skip_whitespace (&str);
584   number = g_ascii_strtod (str, &str);
585   skip_whitespace (&str);
586   if (*str != '%')
587     return FALSE;
588
589   str += 1;
590
591   l = CLAMP (number / 100.0, 0.0, 1.0);
592
593   /* alpha (optional); since the alpha channel value can only
594    * be between 0 and 1 we don't use the parse_rgb_value()
595    * function
596    */
597   if (has_alpha)
598     {
599       if (*str != ',')
600         return FALSE;
601
602       str += 1;
603
604       skip_whitespace (&str);
605       number = g_ascii_strtod (str, &str);
606
607       color->alpha = CLAMP (number * 255.0, 0, 255);
608     }
609   else
610     color->alpha = 255;
611
612   skip_whitespace (&str);
613   if (*str != ')')
614     return FALSE;
615
616   clutter_color_from_hls (color, h, l, s);
617
618   return TRUE;
619 }
620
621 /**
622  * clutter_color_from_string:
623  * @color: (out caller-allocates): return location for a #ClutterColor
624  * @str: a string specifiying a color
625  *
626  * Parses a string definition of a color, filling the
627  * <structfield>red</structfield>, <structfield>green</structfield>, 
628  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
629  * channels of @color.
630  *
631  * The @color is not allocated.
632  *
633  * The format of @str can be either one of:
634  *
635  * <itemizedlist>
636  * <listitem>
637  *   <para>a standard name (as taken from the X11 rgb.txt file)</para>
638  * </listitem>
639  * <listitem>
640  *   <para>an hexadecimal value in the form: <literal>&num;rgb</literal>,
641  *   <literal>&num;rrggbb</literal>, <literal>&num;rgba</literal> or
642  *   <literal>&num;rrggbbaa</literal></para>
643  * </listitem>
644  * <listitem>
645  *   <para>a RGB color in the form: <literal>rgb(r, g, b)</literal></para>
646  * </listitem>
647  * <listitem>
648  *   <para>a RGB color in the form: <literal>rgba(r, g, b, a)</literal></para>
649  * </listitem>
650  * <listitem>
651  *   <para>a HSL color in the form: <literal>hsl(h, s, l)</literal></para>
652  * </listitem>
653  * <listitem>
654  *   <para>a HSL color in the form: <literal>hsla(h, s, l, a)</literal></para>
655  * </listitem>
656  * </itemizedlist>
657  *
658  * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue color
659  * intensities and the opacity. The 'h', 's' and 'l' are (respectively) the
660  * hue, saturation and luminance values.
661  *
662  * In the rgb() and rgba() formats, the 'r', 'g', and 'b' values are either
663  * integers between 0 and 255, or percentage values in the range between 0%
664  * and 100%; the percentages require the '%' character. The 'a' value, if
665  * specified, can only be a floating point value between 0.0 and 1.0.
666  *
667  * In the hls() and hlsa() formats, the 'h' value (hue) it's an angle between
668  * 0 and 360.0 degrees; the 'l' and 's' values (luminance and saturation) are
669  * a floating point value between 0.0 and 1.0. The 'a' value, if specified,
670  * can only be a floating point value between 0.0 and 1.0.
671  *
672  * Whitespace inside the definitions is ignored; no leading whitespace
673  * is allowed.
674  *
675  * If the alpha component is not specified then it is assumed to be set to
676  * be fully opaque.
677  *
678  * Return value: %TRUE if parsing succeeded, and %FALSE otherwise
679  *
680  * Since: 1.0
681  */
682 gboolean
683 clutter_color_from_string (ClutterColor *color,
684                            const gchar  *str)
685 {
686   PangoColor pango_color = { 0, };
687
688   g_return_val_if_fail (color != NULL, FALSE);
689   g_return_val_if_fail (str != NULL, FALSE);
690
691   if (strncmp (str, "rgb", 3) == 0)
692     {
693       gchar *s = (gchar *) str;
694       gboolean res;
695
696       if (strncmp (str, "rgba", 4) == 0)
697         res = parse_rgba (color, s + 4, TRUE);
698       else
699         res = parse_rgba (color, s + 3, FALSE);
700
701       return res;
702     }
703
704   if (strncmp (str, "hsl", 3) == 0)
705     {
706       gchar *s = (gchar *) str;
707       gboolean res;
708
709       if (strncmp (str, "hsla", 4) == 0)
710         res = parse_hsla (color, s + 4, TRUE);
711       else
712         res = parse_hsla (color, s + 3, FALSE);
713
714       return res;
715     }
716
717   /* if the string contains a color encoded using the hexadecimal
718    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
719    * parsing the color ourselves, as we need the alpha channel that
720    * Pango can't retrieve.
721    */
722   if (str[0] == '#')
723     {
724       gint32 result;
725
726       if (sscanf (str + 1, "%x", &result))
727         {
728           gsize length = strlen (str);
729
730           switch (length)
731             {
732             case 9: /* rrggbbaa */
733               color->red   = (result >> 24) & 0xff;
734               color->green = (result >> 16) & 0xff;
735               color->blue  = (result >>  8) & 0xff;
736
737               color->alpha = result & 0xff;
738
739               return TRUE;
740
741             case 7: /* #rrggbb */
742               color->red   = (result >> 16) & 0xff;
743               color->green = (result >>  8) & 0xff;
744               color->blue  = result & 0xff;
745
746               color->alpha = 0xff;
747
748               return TRUE;
749
750             case 5: /* #rgba */
751               color->red   = ((result >> 12) & 0xf);
752               color->green = ((result >>  8) & 0xf);
753               color->blue  = ((result >>  4) & 0xf);
754               color->alpha = result & 0xf;
755
756               color->red   = (color->red   << 4) | color->red;
757               color->green = (color->green << 4) | color->green;
758               color->blue  = (color->blue  << 4) | color->blue;
759               color->alpha = (color->alpha << 4) | color->alpha;
760
761               return TRUE;
762
763             case 4: /* #rgb */
764               color->red   = ((result >>  8) & 0xf);
765               color->green = ((result >>  4) & 0xf);
766               color->blue  = result & 0xf;
767
768               color->red   = (color->red   << 4) | color->red;
769               color->green = (color->green << 4) | color->green;
770               color->blue  = (color->blue  << 4) | color->blue;
771
772               color->alpha = 0xff;
773
774               return TRUE;
775
776             default:
777               /* pass through to Pango */
778               break;
779             }
780         }
781     }
782
783   /* Fall back to pango for named colors */
784   if (pango_color_parse (&pango_color, str))
785     {
786       color->red   = pango_color.red;
787       color->green = pango_color.green;
788       color->blue  = pango_color.blue;
789
790       color->alpha = 0xff;
791
792       return TRUE;
793     }
794
795   return FALSE;
796 }
797
798 /**
799  * clutter_color_to_string:
800  * @color: a #ClutterColor
801  *
802  * Returns a textual specification of @color in the hexadecimal form
803  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
804  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
805  * hexadecimal digits representing the red, green, blue and alpha components
806  * respectively.
807  *
808  * Return value: (transfer full): a newly-allocated text string
809  *
810  * Since: 0.2
811  */
812 gchar *
813 clutter_color_to_string (const ClutterColor *color)
814 {
815   g_return_val_if_fail (color != NULL, NULL);
816
817   return g_strdup_printf ("#%02x%02x%02x%02x",
818                           color->red,
819                           color->green,
820                           color->blue,
821                           color->alpha);
822 }
823
824 /**
825  * clutter_color_equal:
826  * @v1: a #ClutterColor
827  * @v2: a #ClutterColor
828  *
829  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
830  *
831  * This function can be passed to g_hash_table_new() as the @key_equal_func
832  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
833  *
834  * Return value: %TRUE if the two colors are the same.
835  *
836  * Since: 0.2
837  */
838 gboolean
839 clutter_color_equal (gconstpointer v1,
840                      gconstpointer v2)
841 {
842   const ClutterColor *a, *b;
843
844   g_return_val_if_fail (v1 != NULL, FALSE);
845   g_return_val_if_fail (v2 != NULL, FALSE);
846
847   if (v1 == v2)
848     return TRUE;
849
850   a = v1;
851   b = v2;
852
853   return (a->red   == b->red   &&
854           a->green == b->green &&
855           a->blue  == b->blue  &&
856           a->alpha == b->alpha);
857 }
858
859 /**
860  * clutter_color_hash:
861  * @v: a #ClutterColor
862  *
863  * Converts a #ClutterColor to a hash value.
864  *
865  * This function can be passed to g_hash_table_new() as the @hash_func
866  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
867  *
868  * Return value: a hash value corresponding to the color
869  *
870  * Since: 1.0
871  */
872 guint
873 clutter_color_hash (gconstpointer v)
874 {
875   return clutter_color_to_pixel ((const ClutterColor *) v);
876 }
877
878 /**
879  * clutter_color_interpolate:
880  * @initial: the initial #ClutterColor
881  * @final: the final #ClutterColor
882  * @progress: the interpolation progress
883  * @result: (out): return location for the interpolation
884  *
885  * Interpolates between @initial and @final #ClutterColor<!-- -->s
886  * using @progress
887  *
888  * Since: 1.6
889  */
890 void
891 clutter_color_interpolate (const ClutterColor *initial,
892                            const ClutterColor *final,
893                            gdouble             progress,
894                            ClutterColor       *result)
895 {
896   g_return_if_fail (initial != NULL);
897   g_return_if_fail (final != NULL);
898   g_return_if_fail (result != NULL);
899
900   result->red   = initial->red   + (final->red   - initial->red)   * progress;
901   result->green = initial->green + (final->green - initial->green) * progress;
902   result->blue  = initial->blue  + (final->blue  - initial->blue)  * progress;
903   result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress;
904 }
905
906 static gboolean
907 clutter_color_progress (const GValue *a,
908                         const GValue *b,
909                         gdouble       progress,
910                         GValue       *retval)
911 {
912   const ClutterColor *a_color = clutter_value_get_color (a);
913   const ClutterColor *b_color = clutter_value_get_color (b);
914   ClutterColor res = { 0, };
915
916   clutter_color_interpolate (a_color, b_color, progress, &res);
917   clutter_value_set_color (retval, &res);
918
919   return TRUE;
920 }
921
922 /**
923  * clutter_color_copy:
924  * @color: a #ClutterColor
925  *
926  * Makes a copy of the color structure.  The result must be
927  * freed using clutter_color_free().
928  *
929  * Return value: (transfer full): an allocated copy of @color.
930  *
931  * Since: 0.2
932  */
933 ClutterColor *
934 clutter_color_copy (const ClutterColor *color)
935 {
936   if (G_LIKELY (color != NULL))
937     return g_slice_dup (ClutterColor, color);
938
939   return NULL;
940 }
941
942 /**
943  * clutter_color_free:
944  * @color: a #ClutterColor
945  *
946  * Frees a color structure created with clutter_color_copy().
947  *
948  * Since: 0.2
949  */
950 void
951 clutter_color_free (ClutterColor *color)
952 {
953   if (G_LIKELY (color != NULL))
954     g_slice_free (ClutterColor, color);
955 }
956
957 /**
958  * clutter_color_new:
959  * @red: red component of the color, between 0 and 255
960  * @green: green component of the color, between 0 and 255
961  * @blue: blue component of the color, between 0 and 255
962  * @alpha: alpha component of the color, between 0 and 255
963  *
964  * Creates a new #ClutterColor with the given values.
965  *
966  * Return value: (transfer full): the newly allocated color.
967  *   Use clutter_color_free() when done
968  *
969  * Since: 0.8.4
970  */
971 ClutterColor *
972 clutter_color_new (guint8 red,
973                    guint8 green,
974                    guint8 blue,
975                    guint8 alpha)
976 {
977   ClutterColor *color;
978
979   color = g_slice_new (ClutterColor);
980
981   color->red   = red;
982   color->green = green;
983   color->blue  = blue;
984   color->alpha = alpha;
985
986   return color;
987 }
988
989 static void
990 clutter_value_transform_color_string (const GValue *src,
991                                       GValue       *dest)
992 {
993   const ClutterColor *color = g_value_get_boxed (src);
994
995   if (color)
996     {
997       gchar *string = clutter_color_to_string (color);
998
999       g_value_take_string (dest, string);
1000     }
1001   else
1002     g_value_set_string (dest, NULL);
1003 }
1004
1005 static void
1006 clutter_value_transform_string_color (const GValue *src,
1007                                       GValue       *dest)
1008 {
1009   const char *str = g_value_get_string (src);
1010
1011   if (str)
1012     {
1013       ClutterColor color = { 0, };
1014
1015       clutter_color_from_string (&color, str);
1016
1017       clutter_value_set_color (dest, &color);
1018     }
1019   else
1020     clutter_value_set_color (dest, NULL);
1021 }
1022
1023 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color,
1024                                clutter_color_copy,
1025                                clutter_color_free,
1026                                CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string)
1027                                CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color)
1028                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress));
1029
1030 /**
1031  * clutter_value_set_color:
1032  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1033  * @color: the color to set
1034  *
1035  * Sets @value to @color.
1036  *
1037  * Since: 0.8.4
1038  */
1039 void
1040 clutter_value_set_color (GValue             *value,
1041                          const ClutterColor *color)
1042 {
1043   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
1044
1045   g_value_set_boxed (value, color);
1046 }
1047
1048 /**
1049  * clutter_value_get_color:
1050  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1051  *
1052  * Gets the #ClutterColor contained in @value.
1053  *
1054  * Return value: (transfer none): the color inside the passed #GValue
1055  *
1056  * Since: 0.8.4
1057  */
1058 const ClutterColor *
1059 clutter_value_get_color (const GValue *value)
1060 {
1061   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
1062
1063   return g_value_get_boxed (value);
1064 }
1065
1066 static void
1067 param_color_init (GParamSpec *pspec)
1068 {
1069   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1070
1071   cspec->default_value = NULL;
1072 }
1073
1074 static void
1075 param_color_finalize (GParamSpec *pspec)
1076 {
1077   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1078
1079   clutter_color_free (cspec->default_value);
1080 }
1081
1082 static void
1083 param_color_set_default (GParamSpec *pspec,
1084                          GValue     *value)
1085 {
1086   const ClutterColor *default_value =
1087     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
1088   clutter_value_set_color (value, default_value);
1089 }
1090
1091 static gint
1092 param_color_values_cmp (GParamSpec   *pspec,
1093                         const GValue *value1,
1094                         const GValue *value2)
1095 {
1096   const ClutterColor *color1 = g_value_get_boxed (value1);
1097   const ClutterColor *color2 = g_value_get_boxed (value2);
1098   int pixel1, pixel2;
1099
1100   if (color1 == NULL)
1101     return color2 == NULL ? 0 : -1;
1102
1103   pixel1 = clutter_color_to_pixel (color1);
1104   pixel2 = clutter_color_to_pixel (color2);
1105
1106   if (pixel1 < pixel2)
1107     return -1;
1108   else if (pixel1 == pixel2)
1109     return 0;
1110   else
1111     return 1;
1112 }
1113
1114 GType
1115 clutter_param_color_get_type (void)
1116 {
1117   static GType pspec_type = 0;
1118
1119   if (G_UNLIKELY (pspec_type == 0))
1120     {
1121       const GParamSpecTypeInfo pspec_info = {
1122         sizeof (ClutterParamSpecColor),
1123         16,
1124         param_color_init,
1125         CLUTTER_TYPE_COLOR,
1126         param_color_finalize,
1127         param_color_set_default,
1128         NULL,
1129         param_color_values_cmp,
1130       };
1131
1132       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
1133                                                  &pspec_info);
1134     }
1135
1136   return pspec_type;
1137 }
1138
1139 /**
1140  * clutter_param_spec_color: (skip)
1141  * @name: name of the property
1142  * @nick: short name
1143  * @blurb: description (can be translatable)
1144  * @default_value: default value
1145  * @flags: flags for the param spec
1146  *
1147  * Creates a #GParamSpec for properties using #ClutterColor.
1148  *
1149  * Return value: the newly created #GParamSpec
1150  *
1151  * Since: 0.8.4
1152  */
1153 GParamSpec *
1154 clutter_param_spec_color (const gchar        *name,
1155                           const gchar        *nick,
1156                           const gchar        *blurb,
1157                           const ClutterColor *default_value,
1158                           GParamFlags         flags)
1159 {
1160   ClutterParamSpecColor *cspec;
1161
1162   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
1163                                  name, nick, blurb, flags);
1164
1165   cspec->default_value = clutter_color_copy (default_value);
1166
1167   return G_PARAM_SPEC (cspec);
1168 }