color: Add initializers for ClutterColor
[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   skip_whitespace (&str);
593
594   /* alpha (optional); since the alpha channel value can only
595    * be between 0 and 1 we don't use the parse_rgb_value()
596    * function
597    */
598   if (has_alpha)
599     {
600       if (*str != ',')
601         return FALSE;
602
603       str += 1;
604
605       skip_whitespace (&str);
606       number = g_ascii_strtod (str, &str);
607
608       color->alpha = CLAMP (number * 255.0, 0, 255);
609     }
610   else
611     color->alpha = 255;
612
613   skip_whitespace (&str);
614   if (*str != ')')
615     return FALSE;
616
617   clutter_color_from_hls (color, h, l, s);
618
619   return TRUE;
620 }
621
622 /**
623  * clutter_color_from_string:
624  * @color: (out caller-allocates): return location for a #ClutterColor
625  * @str: a string specifiying a color
626  *
627  * Parses a string definition of a color, filling the
628  * <structfield>red</structfield>, <structfield>green</structfield>, 
629  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
630  * channels of @color.
631  *
632  * The @color is not allocated.
633  *
634  * The format of @str can be either one of:
635  *
636  * <itemizedlist>
637  * <listitem>
638  *   <para>a standard name (as taken from the X11 rgb.txt file)</para>
639  * </listitem>
640  * <listitem>
641  *   <para>an hexadecimal value in the form: <literal>&num;rgb</literal>,
642  *   <literal>&num;rrggbb</literal>, <literal>&num;rgba</literal> or
643  *   <literal>&num;rrggbbaa</literal></para>
644  * </listitem>
645  * <listitem>
646  *   <para>a RGB color in the form: <literal>rgb(r, g, b)</literal></para>
647  * </listitem>
648  * <listitem>
649  *   <para>a RGB color in the form: <literal>rgba(r, g, b, a)</literal></para>
650  * </listitem>
651  * <listitem>
652  *   <para>a HSL color in the form: <literal>hsl(h, s, l)</literal></para>
653  * </listitem>
654  * <listitem>
655  *   <para>a HSL color in the form: <literal>hsla(h, s, l, a)</literal></para>
656  * </listitem>
657  * </itemizedlist>
658  *
659  * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue color
660  * intensities and the opacity. The 'h', 's' and 'l' are (respectively) the
661  * hue, saturation and luminance values.
662  *
663  * In the rgb() and rgba() formats, the 'r', 'g', and 'b' values are either
664  * integers between 0 and 255, or percentage values in the range between 0%
665  * and 100%; the percentages require the '%' character. The 'a' value, if
666  * specified, can only be a floating point value between 0.0 and 1.0.
667  *
668  * In the hls() and hlsa() formats, the 'h' value (hue) it's an angle between
669  * 0 and 360.0 degrees; the 'l' and 's' values (luminance and saturation) are
670  * a floating point value between 0.0 and 1.0. The 'a' value, if specified,
671  * can only be a floating point value between 0.0 and 1.0.
672  *
673  * Whitespace inside the definitions is ignored; no leading whitespace
674  * is allowed.
675  *
676  * If the alpha component is not specified then it is assumed to be set to
677  * be fully opaque.
678  *
679  * Return value: %TRUE if parsing succeeded, and %FALSE otherwise
680  *
681  * Since: 1.0
682  */
683 gboolean
684 clutter_color_from_string (ClutterColor *color,
685                            const gchar  *str)
686 {
687   PangoColor pango_color = { 0, };
688
689   g_return_val_if_fail (color != NULL, FALSE);
690   g_return_val_if_fail (str != NULL, FALSE);
691
692   if (strncmp (str, "rgb", 3) == 0)
693     {
694       gchar *s = (gchar *) str;
695       gboolean res;
696
697       if (strncmp (str, "rgba", 4) == 0)
698         res = parse_rgba (color, s + 4, TRUE);
699       else
700         res = parse_rgba (color, s + 3, FALSE);
701
702       return res;
703     }
704
705   if (strncmp (str, "hsl", 3) == 0)
706     {
707       gchar *s = (gchar *) str;
708       gboolean res;
709
710       if (strncmp (str, "hsla", 4) == 0)
711         res = parse_hsla (color, s + 4, TRUE);
712       else
713         res = parse_hsla (color, s + 3, FALSE);
714
715       return res;
716     }
717
718   /* if the string contains a color encoded using the hexadecimal
719    * notations (#rrggbbaa or #rgba) we attempt a rough pass at
720    * parsing the color ourselves, as we need the alpha channel that
721    * Pango can't retrieve.
722    */
723   if (str[0] == '#' && str[1] != '\0')
724     {
725       gsize length = strlen (str + 1);
726       gint32 result;
727
728       if (sscanf (str + 1, "%x", &result) == 1)
729         {
730           switch (length)
731             {
732             case 8: /* 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 6: /* #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 4: /* #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 3: /* #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               return FALSE;
778             }
779         }
780     }
781
782   /* fall back to pango for X11-style named colors; see:
783    *
784    *   http://en.wikipedia.org/wiki/X11_color_names
785    *
786    * for a list. at some point we might even ship with our own list generated
787    * from X11/rgb.txt, like we generate the key symbols.
788    */
789   if (pango_color_parse (&pango_color, str))
790     {
791       color->red   = pango_color.red;
792       color->green = pango_color.green;
793       color->blue  = pango_color.blue;
794
795       color->alpha = 0xff;
796
797       return TRUE;
798     }
799
800   return FALSE;
801 }
802
803 /**
804  * clutter_color_to_string:
805  * @color: a #ClutterColor
806  *
807  * Returns a textual specification of @color in the hexadecimal form
808  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
809  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
810  * hexadecimal digits representing the red, green, blue and alpha components
811  * respectively.
812  *
813  * Return value: (transfer full): a newly-allocated text string
814  *
815  * Since: 0.2
816  */
817 gchar *
818 clutter_color_to_string (const ClutterColor *color)
819 {
820   g_return_val_if_fail (color != NULL, NULL);
821
822   return g_strdup_printf ("#%02x%02x%02x%02x",
823                           color->red,
824                           color->green,
825                           color->blue,
826                           color->alpha);
827 }
828
829 /**
830  * clutter_color_equal:
831  * @v1: a #ClutterColor
832  * @v2: a #ClutterColor
833  *
834  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
835  *
836  * This function can be passed to g_hash_table_new() as the @key_equal_func
837  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
838  *
839  * Return value: %TRUE if the two colors are the same.
840  *
841  * Since: 0.2
842  */
843 gboolean
844 clutter_color_equal (gconstpointer v1,
845                      gconstpointer v2)
846 {
847   const ClutterColor *a, *b;
848
849   g_return_val_if_fail (v1 != NULL, FALSE);
850   g_return_val_if_fail (v2 != NULL, FALSE);
851
852   if (v1 == v2)
853     return TRUE;
854
855   a = v1;
856   b = v2;
857
858   return (a->red   == b->red   &&
859           a->green == b->green &&
860           a->blue  == b->blue  &&
861           a->alpha == b->alpha);
862 }
863
864 /**
865  * clutter_color_hash:
866  * @v: a #ClutterColor
867  *
868  * Converts a #ClutterColor to a hash value.
869  *
870  * This function can be passed to g_hash_table_new() as the @hash_func
871  * parameter, when using #ClutterColor<!-- -->s as keys in a #GHashTable.
872  *
873  * Return value: a hash value corresponding to the color
874  *
875  * Since: 1.0
876  */
877 guint
878 clutter_color_hash (gconstpointer v)
879 {
880   return clutter_color_to_pixel ((const ClutterColor *) v);
881 }
882
883 /**
884  * clutter_color_interpolate:
885  * @initial: the initial #ClutterColor
886  * @final: the final #ClutterColor
887  * @progress: the interpolation progress
888  * @result: (out): return location for the interpolation
889  *
890  * Interpolates between @initial and @final #ClutterColor<!-- -->s
891  * using @progress
892  *
893  * Since: 1.6
894  */
895 void
896 clutter_color_interpolate (const ClutterColor *initial,
897                            const ClutterColor *final,
898                            gdouble             progress,
899                            ClutterColor       *result)
900 {
901   g_return_if_fail (initial != NULL);
902   g_return_if_fail (final != NULL);
903   g_return_if_fail (result != NULL);
904
905   result->red   = initial->red   + (final->red   - initial->red)   * progress;
906   result->green = initial->green + (final->green - initial->green) * progress;
907   result->blue  = initial->blue  + (final->blue  - initial->blue)  * progress;
908   result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress;
909 }
910
911 static gboolean
912 clutter_color_progress (const GValue *a,
913                         const GValue *b,
914                         gdouble       progress,
915                         GValue       *retval)
916 {
917   const ClutterColor *a_color = clutter_value_get_color (a);
918   const ClutterColor *b_color = clutter_value_get_color (b);
919   ClutterColor res = { 0, };
920
921   clutter_color_interpolate (a_color, b_color, progress, &res);
922   clutter_value_set_color (retval, &res);
923
924   return TRUE;
925 }
926
927 /**
928  * clutter_color_copy:
929  * @color: a #ClutterColor
930  *
931  * Makes a copy of the color structure.  The result must be
932  * freed using clutter_color_free().
933  *
934  * Return value: (transfer full): an allocated copy of @color.
935  *
936  * Since: 0.2
937  */
938 ClutterColor *
939 clutter_color_copy (const ClutterColor *color)
940 {
941   if (G_LIKELY (color != NULL))
942     return g_slice_dup (ClutterColor, color);
943
944   return NULL;
945 }
946
947 /**
948  * clutter_color_free:
949  * @color: a #ClutterColor
950  *
951  * Frees a color structure created with clutter_color_copy().
952  *
953  * Since: 0.2
954  */
955 void
956 clutter_color_free (ClutterColor *color)
957 {
958   if (G_LIKELY (color != NULL))
959     g_slice_free (ClutterColor, color);
960 }
961
962 /**
963  * clutter_color_new:
964  * @red: red component of the color, between 0 and 255
965  * @green: green component of the color, between 0 and 255
966  * @blue: blue component of the color, between 0 and 255
967  * @alpha: alpha component of the color, between 0 and 255
968  *
969  * Creates a new #ClutterColor with the given values.
970  *
971  * This function is the equivalent of:
972  *
973  * |[
974  *   clutter_color_init (clutter_color_alloc (), red, green, blue, alpha);
975  * ]|
976  *
977  * Return value: (transfer full): the newly allocated color.
978  *   Use clutter_color_free() when done
979  *
980  * Since: 0.8.4
981  */
982 ClutterColor *
983 clutter_color_new (guint8 red,
984                    guint8 green,
985                    guint8 blue,
986                    guint8 alpha)
987 {
988   return clutter_color_init (clutter_color_alloc (),
989                              red,
990                              green,
991                              blue,
992                              alpha);
993 }
994
995 /**
996  * clutter_color_alloc:
997  *
998  * Allocates a new, transparent black #ClutterColor.
999  *
1000  * Return value: (transfer full): the newly allocated #ClutterColor; use
1001  *   clutter_color_free() to free its resources
1002  *
1003  * Since: 1.12
1004  */
1005 ClutterColor *
1006 clutter_color_alloc (void)
1007 {
1008   return g_slice_new0 (ClutterColor);
1009 }
1010
1011 /**
1012  * clutter_color_init:
1013  * @color: a #ClutterColor
1014  * @red: red component of the color, between 0 and 255
1015  * @green: green component of the color, between 0 and 255
1016  * @blue: blue component of the color, between 0 and 255
1017  * @alpha: alpha component of the color, between 0 and 255
1018  *
1019  * Initializes @color with the given values.
1020  *
1021  * Return value: (transfer none): the initialized #ClutterColor
1022  *
1023  * Since: 1.12
1024  */
1025 ClutterColor *
1026 clutter_color_init (ClutterColor *color,
1027                     guint8        red,
1028                     guint8        green,
1029                     guint8        blue,
1030                     guint8        alpha)
1031 {
1032   g_return_val_if_fail (color != NULL, NULL);
1033
1034   color->red = red;
1035   color->green = green;
1036   color->blue = blue;
1037   color->alpha = alpha;
1038
1039   return color;
1040 }
1041
1042 static void
1043 clutter_value_transform_color_string (const GValue *src,
1044                                       GValue       *dest)
1045 {
1046   const ClutterColor *color = g_value_get_boxed (src);
1047
1048   if (color)
1049     {
1050       gchar *string = clutter_color_to_string (color);
1051
1052       g_value_take_string (dest, string);
1053     }
1054   else
1055     g_value_set_string (dest, NULL);
1056 }
1057
1058 static void
1059 clutter_value_transform_string_color (const GValue *src,
1060                                       GValue       *dest)
1061 {
1062   const char *str = g_value_get_string (src);
1063
1064   if (str)
1065     {
1066       ClutterColor color = { 0, };
1067
1068       clutter_color_from_string (&color, str);
1069
1070       clutter_value_set_color (dest, &color);
1071     }
1072   else
1073     clutter_value_set_color (dest, NULL);
1074 }
1075
1076 G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color,
1077                                clutter_color_copy,
1078                                clutter_color_free,
1079                                CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string)
1080                                CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color)
1081                                CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress));
1082
1083 /**
1084  * clutter_value_set_color:
1085  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1086  * @color: the color to set
1087  *
1088  * Sets @value to @color.
1089  *
1090  * Since: 0.8.4
1091  */
1092 void
1093 clutter_value_set_color (GValue             *value,
1094                          const ClutterColor *color)
1095 {
1096   g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value));
1097
1098   g_value_set_boxed (value, color);
1099 }
1100
1101 /**
1102  * clutter_value_get_color:
1103  * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR
1104  *
1105  * Gets the #ClutterColor contained in @value.
1106  *
1107  * Return value: (transfer none): the color inside the passed #GValue
1108  *
1109  * Since: 0.8.4
1110  */
1111 const ClutterColor *
1112 clutter_value_get_color (const GValue *value)
1113 {
1114   g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL);
1115
1116   return g_value_get_boxed (value);
1117 }
1118
1119 static void
1120 param_color_init (GParamSpec *pspec)
1121 {
1122   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1123
1124   cspec->default_value = NULL;
1125 }
1126
1127 static void
1128 param_color_finalize (GParamSpec *pspec)
1129 {
1130   ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec);
1131
1132   clutter_color_free (cspec->default_value);
1133 }
1134
1135 static void
1136 param_color_set_default (GParamSpec *pspec,
1137                          GValue     *value)
1138 {
1139   const ClutterColor *default_value =
1140     CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value;
1141   clutter_value_set_color (value, default_value);
1142 }
1143
1144 static gint
1145 param_color_values_cmp (GParamSpec   *pspec,
1146                         const GValue *value1,
1147                         const GValue *value2)
1148 {
1149   const ClutterColor *color1 = g_value_get_boxed (value1);
1150   const ClutterColor *color2 = g_value_get_boxed (value2);
1151   int pixel1, pixel2;
1152
1153   if (color1 == NULL)
1154     return color2 == NULL ? 0 : -1;
1155
1156   pixel1 = clutter_color_to_pixel (color1);
1157   pixel2 = clutter_color_to_pixel (color2);
1158
1159   if (pixel1 < pixel2)
1160     return -1;
1161   else if (pixel1 == pixel2)
1162     return 0;
1163   else
1164     return 1;
1165 }
1166
1167 GType
1168 clutter_param_color_get_type (void)
1169 {
1170   static GType pspec_type = 0;
1171
1172   if (G_UNLIKELY (pspec_type == 0))
1173     {
1174       const GParamSpecTypeInfo pspec_info = {
1175         sizeof (ClutterParamSpecColor),
1176         16,
1177         param_color_init,
1178         CLUTTER_TYPE_COLOR,
1179         param_color_finalize,
1180         param_color_set_default,
1181         NULL,
1182         param_color_values_cmp,
1183       };
1184
1185       pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"),
1186                                                  &pspec_info);
1187     }
1188
1189   return pspec_type;
1190 }
1191
1192 /**
1193  * clutter_param_spec_color: (skip)
1194  * @name: name of the property
1195  * @nick: short name
1196  * @blurb: description (can be translatable)
1197  * @default_value: default value
1198  * @flags: flags for the param spec
1199  *
1200  * Creates a #GParamSpec for properties using #ClutterColor.
1201  *
1202  * Return value: the newly created #GParamSpec
1203  *
1204  * Since: 0.8.4
1205  */
1206 GParamSpec *
1207 clutter_param_spec_color (const gchar        *name,
1208                           const gchar        *nick,
1209                           const gchar        *blurb,
1210                           const ClutterColor *default_value,
1211                           GParamFlags         flags)
1212 {
1213   ClutterParamSpecColor *cspec;
1214
1215   cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR,
1216                                  name, nick, blurb, flags);
1217
1218   cspec->default_value = clutter_color_copy (default_value);
1219
1220   return G_PARAM_SPEC (cspec);
1221 }