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