2008-05-28 Emmanuele Bassi <ebassi@openedhand.com>
[profile/ivi/clutter.git] / clutter / clutter-color.c
1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Authored By Matthew Allum  <mallum@openedhand.com>
7  *
8  * Copyright (C) 2006 OpenedHand
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the
22  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 /**
27  * SECTION:clutter-color
28  * @short_description: Color management and manipulation.
29  *
30  * #ClutterColor is a simple type for representing colors.
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <pango/pango-attributes.h>
38
39 #include "clutter-main.h"
40 #include "clutter-color.h"
41 #include "clutter-private.h"
42 #include "clutter-debug.h"
43
44 /**
45  * clutter_color_add:
46  * @src1: a #ClutterColor
47  * @src2: a #ClutterColor
48  * @dest: return location for the result
49  *
50  * Adds @src2 to @src1 and saves the resulting color
51  * inside @dest.
52  *
53  * The alpha channel of @dest is as the maximum value
54  * between the alpha channels of @src1 and @src2.
55  */
56 void
57 clutter_color_add (const ClutterColor *src1,
58                    const ClutterColor *src2,
59                    ClutterColor       *dest)
60 {
61   g_return_if_fail (src1 != NULL);
62   g_return_if_fail (src2 != NULL);
63   g_return_if_fail (dest != NULL);
64
65   dest->red   = CLAMP (src1->red   + src2->red,   0, 255);
66   dest->green = CLAMP (src1->green + src2->green, 0, 255);
67   dest->blue  = CLAMP (src1->blue  + src2->blue,  0, 255);
68
69   dest->alpha = MAX (src1->alpha, src2->alpha);
70 }
71
72 /**
73  * clutter_color_subtract:
74  * @src1: a #ClutterColor
75  * @src2: a #ClutterColor
76  * @dest: return location for the result
77  *
78  * Subtracts @src2 from @src1 and saves the resulting
79  * color inside @dest. This function assumes that the components
80  * of @src1 are greater than the components of @src2; the result is,
81  * otherwise, undefined.
82  *
83  * The alpha channel of @dest is set as the minimum value
84  * between the alpha channels of @src1 and @src2.
85  */
86 void
87 clutter_color_subtract (const ClutterColor *src1,
88                         const ClutterColor *src2,
89                         ClutterColor       *dest)
90 {
91   g_return_if_fail (src1 != NULL);
92   g_return_if_fail (src2 != NULL);
93   g_return_if_fail (dest != NULL);
94
95   dest->red   = CLAMP (src1->red   - src2->red,   0, 255);
96   dest->green = CLAMP (src1->green - src2->green, 0, 255);
97   dest->blue  = CLAMP (src1->blue  - src2->blue,  0, 255);
98
99   dest->alpha = MIN (src1->alpha, src2->alpha);
100 }
101
102 /**
103  * clutter_color_lighten:
104  * @src: a #ClutterColor
105  * @dest: return location for the lighter color
106  *
107  * Lightens @src by a fixed amount, and saves the changed
108  * color in @dest.
109  */
110 void
111 clutter_color_lighten (const ClutterColor *src,
112                        ClutterColor       *dest)
113 {
114   /* 0x14ccd is ClutterFixed for 1.3 */
115   clutter_color_shadex (src, dest, 0x14ccd);
116 }
117
118 /**
119  * clutter_color_darken:
120  * @src: a #ClutterColor
121  * @dest: return location for the darker color
122  *
123  * Darkens @src by a fixed amount, and saves the changed color
124  * in @dest.
125  */
126 void
127 clutter_color_darken (const ClutterColor *src,
128                       ClutterColor       *dest)
129 {
130   /* 0xb333 is ClutterFixed for 0.7 */
131   clutter_color_shadex (src, dest, 0xb333);
132 }
133
134 /**
135  * clutter_color_to_hlsx:
136  * @src: a #ClutterColor
137  * @hue: return location for the hue value or %NULL
138  * @luminance: return location for the luminance value or %NULL
139  * @saturation: return location for the saturation value or %NULL
140  *
141  * Converts @src to the HLS format. Returned hue is in degrees (0 .. 360),
142  * luminance and saturation from interval <0 .. 1>.
143  */
144 void
145 clutter_color_to_hlsx (const ClutterColor *src,
146                        ClutterFixed       *hue,
147                        ClutterFixed       *luminance,
148                        ClutterFixed       *saturation)
149 {
150   ClutterFixed red, green, blue;
151   ClutterFixed min, max, delta;
152   ClutterFixed h, l, s;
153   
154   g_return_if_fail (src != NULL);
155
156   red   = CLUTTER_INT_TO_FIXED (src->red)   / 255;
157   green = CLUTTER_INT_TO_FIXED (src->green) / 255;
158   blue  = CLUTTER_INT_TO_FIXED (src->blue)  / 255;
159
160   if (red > green)
161     {
162       if (red > blue)
163         max = red;
164       else
165         max = blue;
166
167       if (green < blue)
168         min = green;
169       else
170         min = blue;
171     }
172   else
173     {
174       if (green > blue)
175         max = green;
176       else
177         max = blue;
178
179       if (red < blue)
180         min = red;
181       else
182         min = blue;
183     }
184
185   l = (max + min) / 2;
186   s = 0;
187   h = 0;
188
189   if (max != min)
190     {
191       if (l <= CFX_ONE/2)
192         s = CFX_DIV ((max - min), (max + min));
193       else
194         s = CFX_DIV ((max - min), (CLUTTER_INT_TO_FIXED (2) - max - min));
195
196       delta = max - min;
197       if (red == max)
198         h = CFX_DIV ((green - blue), delta);
199       else if (green == max)
200         h = CLUTTER_INT_TO_FIXED (2) + CFX_DIV ((blue - red), delta);
201       else if (blue == max)
202         h = CLUTTER_INT_TO_FIXED (4) + CFX_DIV ((red - green), delta);
203
204       h *= 60;
205       if (h < 0)
206         h += CLUTTER_INT_TO_FIXED (360);
207     }
208
209   if (hue)
210     *hue = h;
211
212   if (luminance)
213     *luminance = l;
214
215   if (saturation)
216     *saturation = s;
217 }
218
219 /**
220  * clutter_color_from_hlsx:
221  * @dest: return location for a #ClutterColor
222  * @hue: hue value (0 .. 360)
223  * @luminance: luminance value (0 .. 1)
224  * @saturation: saturation value (0 .. 1)
225  *
226  * Converts a color expressed in HLS (hue, luminance and saturation)
227  * values into a #ClutterColor.
228  */
229
230 void
231 clutter_color_from_hlsx (ClutterColor *dest,
232                          ClutterFixed   hue,
233                          ClutterFixed   luminance,
234                          ClutterFixed   saturation)
235 {
236   ClutterFixed h, l, s;
237   ClutterFixed m1, m2;
238   
239   g_return_if_fail (dest != NULL);
240
241   l = luminance;
242   s = saturation;
243
244   if (l <= CFX_ONE/2)
245     m2 = CFX_MUL (l, (CFX_ONE + s));
246   else
247     m2 = l + s - CFX_MUL (l,s);
248
249   m1 = 2 * l - m2;
250
251   if (s == 0)
252     {
253       dest->red   = (guint8) CFX_INT (l * 255);
254       dest->green = (guint8) CFX_INT (l * 255);
255       dest->blue  = (guint8) CFX_INT (l * 255);
256     }
257   else
258     {
259       h = hue + CFX_120;
260       while (h > CFX_360)
261         h -= CFX_360;
262       while (h < 0)
263         h += CFX_360;
264
265       if (h < CFX_60)
266         dest->red = (guint8) CFX_INT((m1 + CFX_MUL((m2-m1), h) / 60) * 255);
267       else if (h < CFX_180)
268         dest->red = (guint8) CFX_INT (m2 * 255);
269       else if (h < CFX_240)
270         dest->red = (guint8)CFX_INT((m1+CFX_MUL((m2-m1),(CFX_240-h))/60)*255);
271       else
272         dest->red = (guint8) CFX_INT (m1 * 255);
273
274       h = hue;
275       while (h > CFX_360)
276         h -= CFX_360;
277       while (h < 0)
278         h += CFX_360;
279
280       if (h < CFX_60)
281         dest->green = (guint8)CFX_INT((m1 + CFX_MUL((m2 - m1), h) / 60) * 255);
282       else if (h < CFX_180)
283         dest->green = (guint8) CFX_INT (m2 * 255);
284       else if (h < CFX_240)
285         dest->green =
286             (guint8) CFX_INT((m1 + CFX_MUL ((m2-m1), (CFX_240-h)) / 60) * 255);
287       else
288         dest->green = (guint8) CFX_INT (m1 * 255);
289
290       h = hue - CFX_120;
291       while (h > CFX_360)
292         h -= CFX_360;
293       while (h < 0)
294         h += CFX_360;
295
296       if (h < CFX_60)
297         dest->blue = (guint8) CFX_INT ((m1 + CFX_MUL ((m2-m1), h) / 60) * 255);
298       else if (h < CFX_180)
299         dest->blue = (guint8) CFX_INT (m2 * 255);
300       else if (h < CFX_240)
301         dest->blue = (guint8)CFX_INT((m1+CFX_MUL((m2-m1),(CFX_240-h))/60)*255);
302       else
303         dest->blue = (guint8) CFX_INT(m1 * 255);
304     }
305 }
306
307 /**
308  * clutter_color_to_hls:
309  * @src: a #ClutterColor
310  * @hue: return location for the hue value or %NULL
311  * @luminance: return location for the luminance value or %NULL
312  * @saturation: return location for the saturation value or %NULL
313  *
314  * Converts @src to the HLS format. Returned HLS values are from interval
315  * 0 .. 255.
316  */
317 void
318 clutter_color_to_hls (const ClutterColor *src,
319                       guint8             *hue,
320                       guint8             *luminance,
321                       guint8             *saturation)
322 {
323   ClutterFixed h, l, s;
324   
325   clutter_color_to_hlsx (src, &h, &l, &s);
326   
327   if (hue)
328     *hue = (guint8) CFX_INT (h * 255) / 360;
329
330   if (luminance)
331     *luminance = (guint8) CFX_INT (l * 255);
332
333   if (saturation)
334     *saturation = (guint8) CFX_INT (s * 255);
335 }
336
337 /**
338  * clutter_color_from_hls:
339  * @dest: return location for a #ClutterColor
340  * @hue: hue value (0 .. 255)
341  * @luminance: luminance value (0 .. 255)
342  * @saturation: saturation value (0 .. 255)
343  *
344  * Converts a color expressed in HLS (hue, luminance and saturation)
345  * values into a #ClutterColor.
346  */
347
348 void
349 clutter_color_from_hls (ClutterColor *dest,
350                         guint8        hue,
351                         guint8        luminance,
352                         guint8        saturation)
353 {
354   ClutterFixed h, l, s;
355
356   h = CLUTTER_INT_TO_FIXED (hue * 360) / 255;
357   l = CLUTTER_INT_TO_FIXED (luminance)  / 255;
358   s = CLUTTER_INT_TO_FIXED (saturation) / 255;
359
360   clutter_color_from_hlsx (dest, h, l, s);
361 }
362
363 /**
364  * clutter_color_shade:
365  * @src: a #ClutterColor
366  * @dest: return location for the shaded color
367  * @shade: the shade factor to apply
368  * 
369  * Shades @src by the factor of @shade and saves the modified
370  * color into @dest.
371  */
372 void
373 clutter_color_shade (const ClutterColor *src,
374                      ClutterColor       *dest,
375                      gdouble             shade)
376 {
377   clutter_color_shadex (src, dest, CLUTTER_FLOAT_TO_FIXED (shade));
378 }
379
380 /**
381  * clutter_color_shadex:
382  * @src: a #ClutterColor
383  * @dest: return location for the shaded color
384  * @shade: #ClutterFixed the shade factor to apply
385  * 
386  * Fixed point version of clutter_color_shade().
387  *
388  * Shades @src by the factor of @shade and saves the modified
389  * color into @dest.
390  *
391  * Since: 0.2
392  */
393 void
394 clutter_color_shadex (const ClutterColor *src,
395                       ClutterColor       *dest,
396                       ClutterFixed        shade)
397 {
398   ClutterFixed h, l, s;
399
400   g_return_if_fail (src != NULL);
401   g_return_if_fail (dest != NULL);
402   
403   clutter_color_to_hlsx (src, &h, &l, &s);
404
405   l = CFX_MUL (l, shade);
406   if (l > CFX_ONE)
407     l = CFX_ONE;
408   else if (l < 0)
409     l = 0;
410
411   s = CFX_MUL (s, shade);
412   if (s > CFX_ONE)
413     s = CFX_ONE;
414   else if (s < 0)
415     s = 0;
416   
417   clutter_color_from_hlsx (dest, h, l, s);
418   dest->alpha = src->alpha;
419 }
420
421 /**
422  * clutter_color_to_pixel:
423  * @src: a #ClutterColor
424  *
425  * Converts @src into a packed 32 bit integer, containing
426  * all the four 8 bit channels used by #ClutterColor.
427  *
428  * Return value: a packed color
429  */
430 guint32
431 clutter_color_to_pixel (const ClutterColor *src)
432 {
433   g_return_val_if_fail (src != NULL, 0);
434   
435   return (src->alpha | src->blue << 8 | src->green << 16  | src->red << 24);
436 }
437
438 /**
439  * clutter_color_from_pixel:
440  * @dest: return location for a #ClutterColor
441  * @pixel: a 32 bit packed integer containing a color
442  *
443  * Converts @pixel from the packed representation of a four 8 bit channel
444  * color to a #ClutterColor.
445  */
446 void
447 clutter_color_from_pixel (ClutterColor *dest,
448                           guint32       pixel)
449 {
450   g_return_if_fail (dest != NULL);
451
452   dest->red = pixel >> 24;
453   dest->green = (pixel >> 16) & 0xff;
454   dest->blue = (pixel >> 8) & 0xff;
455   dest->alpha = pixel & 0xff;
456 }
457
458 /**
459  * clutter_color_parse:
460  * @color: a string specifiying a color (named color or #RRGGBBAA)
461  * @dest: return location for a #ClutterColor
462  *
463  * Parses a string definition of a color, filling the
464  * <structfield>red</structfield>, <structfield>green</structfield>, 
465  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
466  * channels of @dest. If alpha is not specified it will be set full opaque.
467  * The color in @dest is not allocated.
468  *
469  * The color may be defined by any of the formats understood by
470  * <function>pango_color_parse</function>; these include literal color
471  * names, like <literal>Red</literal> or <literal>DarkSlateGray</literal>,
472  * or hexadecimal specifications like <literal>&num;3050b2</literal> or
473  * <literal>&num;333</literal>.
474  *
475  * Return value: %TRUE if parsing succeeded.
476  *
477  * Since: 0.2
478  */
479 gboolean
480 clutter_color_parse (const gchar  *color,
481                      ClutterColor *dest)
482 {
483   PangoColor pango_color;
484
485   /* parse ourselves to get alpha */
486   if (color[0] == '#')
487     {
488       gint32 result;
489
490       if (sscanf (color + 1, "%x", &result))
491         {
492           if (strlen (color) == 9)
493             {
494               dest->red   = result >> 24 & 0xff;
495               dest->green = (result >> 16) & 0xff;
496               dest->blue  = (result >> 8) & 0xff;
497               dest->alpha = result & 0xff;
498
499               return TRUE;
500             }
501           else if (strlen (color) == 7)
502             {
503               dest->red   = (result >> 16) & 0xff;
504               dest->green = (result >> 8) & 0xff;
505               dest->blue  = result & 0xff;
506               dest->alpha = 0xff;
507
508               return TRUE;
509             }
510         }
511     }
512   
513   /* Fall back to pango for named colors - note pango does not handle alpha */
514   if (pango_color_parse (&pango_color, color))
515     {
516       dest->red   = pango_color.red;
517       dest->green = pango_color.green;
518       dest->blue  = pango_color.blue;
519       dest->alpha = 0xff;
520
521       return TRUE;
522     }
523
524   return FALSE;
525 }
526
527 /**
528  * clutter_color_to_string:
529  * @color: a #ClutterColor
530  *
531  * Returns a textual specification of @color in the hexadecimal form
532  * <literal>&num;rrggbbaa</literal>, where <literal>r</literal>,
533  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
534  * hex digits representing the red, green, blue and alpha components
535  * respectively.
536  *
537  * Return value: a newly-allocated text string
538  *
539  * Since: 0.2
540  */
541 gchar *
542 clutter_color_to_string (const ClutterColor *color)
543 {
544   g_return_val_if_fail (color != NULL, NULL);
545
546   return g_strdup_printf ("#%02x%02x%02x%02x",
547                           color->red,
548                           color->green,
549                           color->blue,
550                           color->alpha);
551 }
552
553 /**
554  * clutter_color_equal:
555  * @a: a #ClutterColor
556  * @b: a #ClutterColor
557  *
558  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
559  *
560  * Return value: %TRUE if the two colors are the same.
561  *
562  * Since: 0.2
563  */
564 gboolean
565 clutter_color_equal (const ClutterColor *a,
566                      const ClutterColor *b)
567 {
568   g_return_val_if_fail (a != NULL, FALSE);
569   g_return_val_if_fail (b != NULL, FALSE);
570
571   if (a == b)
572     return TRUE;
573
574   return (a->red == b->red &&
575           a->green == b->green &&
576           a->blue == b->blue &&
577           a->alpha == b->alpha);
578 }
579
580 /**
581  * clutter_color_copy:
582  * @color: a #ClutterColor
583  *
584  * Makes a copy of the color structure.  The result must be
585  * freed using clutter_color_free().
586  *
587  * Return value: an allocated copy of @color.
588  *
589  * Since: 0.2
590  */
591 ClutterColor *
592 clutter_color_copy (const ClutterColor *color)
593 {
594   ClutterColor *result;
595   
596   g_return_val_if_fail (color != NULL, NULL);
597
598   result = g_slice_new (ClutterColor);
599   *result = *color;
600
601   return result;
602 }
603
604 /**
605  * clutter_color_free:
606  * @color: a #ClutterColor
607  *
608  * Frees a color structure created with clutter_color_copy().
609  *
610  * Since: 0.2
611  */
612 void
613 clutter_color_free (ClutterColor *color)
614 {
615   g_return_if_fail (color != NULL);
616
617   g_slice_free (ClutterColor, color);
618 }
619
620 GType
621 clutter_color_get_type (void)
622 {
623   static GType our_type = 0;
624   
625   if (!our_type)
626     our_type = g_boxed_type_register_static (I_("ClutterColor"),
627                                              (GBoxedCopyFunc) clutter_color_copy,
628                                              (GBoxedFreeFunc) clutter_color_free);
629   return our_type;
630 }