2007-10-01 Tomas Frydrych <tf@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 }
417
418 /**
419  * clutter_color_to_pixel:
420  * @src: a #ClutterColor
421  *
422  * Converts @src into a packed 32 bit integer, containing
423  * all the four 8 bit channels used by #ClutterColor.
424  *
425  * Return value: a packed color
426  */
427 guint32
428 clutter_color_to_pixel (const ClutterColor *src)
429 {
430   g_return_val_if_fail (src != NULL, 0);
431   
432   return (src->alpha | src->blue << 8 | src->green << 16  | src->red << 24);
433 }
434
435 /**
436  * clutter_color_from_pixel:
437  * @dest: return location for a #ClutterColor
438  * @pixel: a 32 bit packed integer containing a color
439  *
440  * Converts @pixel from the packed representation of a four 8 bit channel
441  * color to a #ClutterColor.
442  */
443 void
444 clutter_color_from_pixel (ClutterColor *dest,
445                           guint32       pixel)
446 {
447   g_return_if_fail (dest != NULL);
448
449   dest->red = pixel >> 24;
450   dest->green = (pixel >> 16) & 0xff;
451   dest->blue = (pixel >> 8) & 0xff;
452   dest->alpha = pixel & 0xff;
453 }
454
455 /**
456  * clutter_color_parse:
457  * @color: a string specifiying a color (named color or #RRGGBBAA)
458  * @dest: return location for a #ClutterColor
459  *
460  * Parses a string definition of a color, filling the
461  * <structfield>red</structfield>, <structfield>green</structfield>, 
462  * <structfield>blue</structfield> and <structfield>alpha</structfield> 
463  * channels of @dest. If alpha is not specified it will be set full opaque.
464  * The color in @dest is not allocated.
465  *
466  * The color may be defined by any of the formats understood by
467  * <function>pango_color_parse</function>; these include literal color
468  * names, like <literal>Red</literal> or <literal>DarkSlateGray</literal>,
469  * or hexadecimal specifications like <literal>&num;3050b2</literal> or
470  * <literal>&num;333</literal>.
471  *
472  * Return value: %TRUE if parsing succeeded.
473  *
474  * Since: 0.2
475  */
476 gboolean
477 clutter_color_parse (const gchar  *color,
478                      ClutterColor *dest)
479 {
480   PangoColor pango_color;
481
482   /* parse ourselves to get alpha */
483   if (color[0] == '#')
484     {
485       gint32 result;
486
487       if (sscanf (color+1, "%x", &result))
488         {
489           if (strlen(color) == 9)
490             {
491               dest->red   = result >> 24 & 0xff;
492               dest->green = (result >> 16) & 0xff;
493               dest->blue  = (result >> 8) & 0xff;
494               dest->alpha = result & 0xff;
495               return TRUE;
496             }
497           else if (strlen(color) == 7)
498             {
499               dest->red   = (result >> 16) & 0xff;
500               dest->green = (result >> 8) & 0xff;
501               dest->blue  = result & 0xff;
502               dest->alpha = 0xff;
503               return TRUE;
504             }
505         }
506     }
507   
508   /* Fall back to pango for named colors - note pango does not handle alpha */
509   if (pango_color_parse (&pango_color, color))
510     {
511       dest->red   = pango_color.red;
512       dest->green = pango_color.green;
513       dest->blue  = pango_color.blue;
514       dest->alpha = 0xff;
515       return TRUE;
516     }
517
518   return FALSE;
519 }
520
521 /**
522  * clutter_color_to_string:
523  * @color: a #ClutterColor
524  *
525  * Returns a textual specification of @color in the hexadecimal form
526  * <literal>&num;rrrrggggbbbbaaaa</literal>, where <literal>r</literal>,
527  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
528  * hex digits representing the red, green, blue and alpha components
529  * respectively.
530  *
531  * Note: the returned string cannot be used to get the color back with
532  * clutter_color_parse().
533  *
534  * Return value: a newly-allocated text string
535  *
536  * Since: 0.2
537  */
538 gchar *
539 clutter_color_to_string (const ClutterColor *color)
540 {
541   g_return_val_if_fail (color != NULL, NULL);
542
543   return g_strdup_printf ("#%04x%04x%04x%04x",
544                           color->red,
545                           color->green,
546                           color->blue,
547                           color->alpha);
548 }
549
550 /**
551  * clutter_color_equal:
552  * @a: a #ClutterColor
553  * @b: a #ClutterColor
554  *
555  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
556  *
557  * Return value: %TRUE if the two colors are the same.
558  *
559  * Since: 0.2
560  */
561 gboolean
562 clutter_color_equal (const ClutterColor *a,
563                      const ClutterColor *b)
564 {
565   g_return_val_if_fail (a != NULL, FALSE);
566   g_return_val_if_fail (b != NULL, FALSE);
567
568   if (a == b)
569     return TRUE;
570
571   return (a->red == b->red &&
572           a->green == b->green &&
573           a->blue == b->blue &&
574           a->alpha == b->alpha);
575 }
576
577 /**
578  * clutter_color_copy:
579  * @color: a #ClutterColor
580  *
581  * Makes a copy of the color structure.  The result must be
582  * freed using clutter_color_free().
583  *
584  * Return value: an allocated copy of @color.
585  *
586  * Since: 0.2
587  */
588 ClutterColor *
589 clutter_color_copy (const ClutterColor *color)
590 {
591   ClutterColor *result;
592   
593   g_return_val_if_fail (color != NULL, NULL);
594
595   result = g_slice_new (ClutterColor);
596   *result = *color;
597
598   return result;
599 }
600
601 /**
602  * clutter_color_free:
603  * @color: a #ClutterColor
604  *
605  * Frees a color structure created with clutter_color_copy().
606  *
607  * Since: 0.2
608  */
609 void
610 clutter_color_free (ClutterColor *color)
611 {
612   g_return_if_fail (color != NULL);
613
614   g_slice_free (ClutterColor, color);
615 }
616
617 GType
618 clutter_color_get_type (void)
619 {
620   static GType our_type = 0;
621   
622   if (!our_type)
623     our_type = g_boxed_type_register_static ("ClutterColor",
624                                              (GBoxedCopyFunc) clutter_color_copy,
625                                              (GBoxedFreeFunc) clutter_color_free);
626   return our_type;
627 }