2007-01-17 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.
78  *
79  * The alpha channel of @dest is set as the minimum value
80  * between the alpha channels of @src1 and @src2.
81  */
82 void
83 clutter_color_subtract (const ClutterColor *src1,
84                         const ClutterColor *src2,
85                         ClutterColor       *dest)
86 {
87   g_return_if_fail (src1 != NULL);
88   g_return_if_fail (src2 != NULL);
89   g_return_if_fail (dest != NULL);
90
91   dest->red   = CLAMP (src2->red   - src1->red,   0, 255);
92   dest->green = CLAMP (src2->green - src1->green, 0, 255);
93   dest->blue  = CLAMP (src2->blue  - src1->blue,  0, 255);
94
95   dest->alpha = MIN (src1->alpha, src2->alpha);
96 }
97
98 /**
99  * clutter_color_lighten:
100  * @src: a #ClutterColor
101  * @dest: return location for the lighter color
102  *
103  * Lightens @src by a fixed amount, and saves the changed
104  * color in @dest.
105  */
106 void
107 clutter_color_lighten (const ClutterColor *src,
108                        ClutterColor       *dest)
109 {
110   /* 0x14ccd is ClutterFixed for 1.3 */
111   clutter_color_shade (src, dest, 0x14ccd);
112 }
113
114 /**
115  * clutter_color_darken:
116  * @src: a #ClutterColor
117  * @dest: return location for the darker color
118  *
119  * Darkens @src by a fixed amount, and saves the changed color
120  * in @dest.
121  */
122 void
123 clutter_color_darken (const ClutterColor *src,
124                       ClutterColor       *dest)
125 {
126   /* 0xb333 is ClutterFixed for 0.7 */
127   clutter_color_shade (src, dest, 0xb333);
128 }
129
130 /**
131  * clutter_color_to_hls:
132  * @src: a #ClutterColor
133  * @hue: return location for the hue value or %NULL
134  * @luminance: return location for the luminance value or %NULL
135  * @saturation: return location for the saturation value or %NULL
136  *
137  * Converts @src to the HLS format.
138  */
139 void
140 clutter_color_to_hls (const ClutterColor *src,
141                       guint8             *hue,
142                       guint8             *luminance,
143                       guint8             *saturation)
144 {
145   ClutterFixed red, green, blue;
146   ClutterFixed min, max, delta;
147   ClutterFixed h, l, s;
148   
149   g_return_if_fail (src != NULL);
150
151   red   = CLUTTER_INT_TO_FIXED (src->red)   / 255;
152   green = CLUTTER_INT_TO_FIXED (src->green) / 255;
153   blue  = CLUTTER_INT_TO_FIXED (src->blue)  / 255;
154
155   if (red > green)
156     {
157       if (red > blue)
158         max = red;
159       else
160         max = blue;
161
162       if (green < blue)
163         min = green;
164       else
165         min = blue;
166     }
167   else
168     {
169       if (green > blue)
170         max = green;
171       else
172         max = blue;
173
174       if (red < blue)
175         min = red;
176       else
177         min = blue;
178     }
179
180   l = (max + min) / 2;
181   s = 0;
182   h = 0;
183
184   if (max != min)
185     {
186       if (l <= CFX_ONE/2)
187         s = CFX_DIV ((max - min), (max + min));
188       else
189         s = CFX_DIV ((max - min), (2 - max - min));
190
191       delta = max - min;
192       if (red == max)
193         h = CFX_DIV ((green - blue), delta);
194       else if (green == max)
195         h = CLUTTER_INT_TO_FIXED (2) + CFX_DIV ((blue - red), delta);
196       else if (blue == max)
197         h = CLUTTER_INT_TO_FIXED (4) + CFX_DIV ((red - green), delta);
198
199       h *= 60;
200       if (h < 0)
201         h += CLUTTER_INT_TO_FIXED (360);
202     }
203
204   if (hue)
205     *hue = (guint8) CFX_INT (h * 255);
206
207   if (luminance)
208     *luminance = (guint8) CFX_INT (l * 255);
209
210   if (saturation)
211     *saturation = (guint8) CFX_INT (s * 255);
212 }
213
214 /**
215  * clutter_color_from_hls:
216  * @dest: return location for a #ClutterColor
217  * @hue: hue value (0 .. 255)
218  * @luminance: luminance value (0 .. 255)
219  * @saturation: saturation value (0 .. 255)
220  *
221  * Converts a color expressed in HLS (hue, luminance and saturation)
222  * values into a #ClutterColor.
223  */
224
225 void
226 clutter_color_from_hls (ClutterColor *dest,
227                         guint8        hue,
228                         guint8        luminance,
229                         guint8        saturation)
230 {
231   ClutterFixed h, l, s;
232   ClutterFixed m1, m2;
233   
234   g_return_if_fail (dest != NULL);
235
236   l = CLUTTER_INT_TO_FIXED (luminance)  / 255;
237   s = CLUTTER_INT_TO_FIXED (saturation) / 255;
238
239   if (l <= CFX_ONE/2)
240     m2 = CFX_MUL (l, (CFX_ONE - s));
241   else
242     m2 = l + s - CFX_MUL (l,s);
243
244   m1 = 2 * l - m2;
245
246   if (s == 0)
247     {
248       dest->red   = (guint8) CFX_INT (l * 255);
249       dest->green = (guint8) CFX_INT (l * 255);
250       dest->blue  = (guint8) CFX_INT (l * 255);
251     }
252   else
253     {
254       h = (CLUTTER_INT_TO_FIXED (hue)/ 255) + CFX_120;
255       while (h > CFX_360)
256         h -= CFX_360;
257       while (h < 0)
258         h += CFX_360;
259
260       if (h < CFX_60)
261         dest->red = (guint8) CFX_INT((m1 + CFX_MUL((m2-m1), h) / 60) * 255);
262       else if (h < CFX_180)
263         dest->red = (guint8) CFX_INT (m2 * 255);
264       else if (h < CFX_240)
265         dest->red = (guint8)CFX_INT((m1+CFX_MUL((m2-m1),(CFX_240-h))/60)*255);
266       else
267         dest->red = (guint8) CFX_INT (m1 * 255);
268
269       h = CLUTTER_INT_TO_FIXED (hue) / 255;
270       while (h > CFX_360)
271         h -= CFX_360;
272       while (h < 0)
273         h += CFX_360;
274
275       if (h < CFX_60)
276         dest->green = (guint8)CFX_INT((m1 + CFX_MUL((m2 - m1), h) / 60) * 255);
277       else if (h < CFX_180)
278         dest->green = (guint8) CFX_INT (m2 * 255);
279       else if (h < CFX_240)
280         dest->green =
281             (guint8) CFX_INT((m1 + CFX_MUL ((m2-m1), (CFX_240-h)) / 60) * 255);
282       else
283         dest->green = (guint8) CFX_INT (m1 * 255);
284
285       h = (CLUTTER_INT_TO_FIXED (hue) / 255) - CFX_120;
286       while (h > CFX_360)
287         h -= CFX_360;
288       while (h < 0)
289         h += CFX_360;
290
291       if (h < CFX_60)
292         dest->blue = (guint8) CFX_INT ((m1 + CFX_MUL ((m2-m1), h) / 60) * 255);
293       else if (h < CFX_180)
294         dest->blue = (guint8) CFX_INT (m2 * 255);
295       else if (h < CFX_240)
296         dest->blue = (guint8)CFX_INT((m1+CFX_MUL((m2-m1),(CFX_240-h))/60)*255);
297       else
298         dest->blue = (guint8) CFX_INT(m1 * 255);
299     }
300 }
301
302 /**
303  * clutter_color_shade:
304  * @src: a #ClutterColor
305  * @dest: return location for the shaded color
306  * @shade: the shade factor to apply
307  * 
308  * Shades @src by the factor of @shade and saves the modified
309  * color into @dest.
310  */
311 void
312 clutter_color_shade (const ClutterColor *src,
313                      ClutterColor       *dest,
314                      gdouble             shade)
315 {
316     clutter_color_shadex (src, dest, CLUTTER_FLOAT_TO_FIXED (shade));
317 }
318
319 /**
320  * clutter_color_shadex:
321  * @src: a #ClutterColor
322  * @dest: return location for the shaded color
323  * @shade: #ClutterFixed the shade factor to apply
324  * 
325  * Fixed point version of clutter_color_shade().
326  *
327  * Shades @src by the factor of @shade and saves the modified
328  * color into @dest.
329  *
330  * Since: 0.2
331  */
332 void
333 clutter_color_shadex (const ClutterColor *src,
334                       ClutterColor       *dest,
335                       ClutterFixed        shade)
336 {
337   guint8 h, l, s;
338   ClutterFixed l1, s1;
339
340   g_return_if_fail (src != NULL);
341   g_return_if_fail (dest != NULL);
342   
343   clutter_color_to_hls (src, &h, &l, &s);
344
345   l1 = CLUTTER_INT_TO_FIXED (l) / 255;
346   s1 = CLUTTER_INT_TO_FIXED (s) / 255;
347
348   l1 = CFX_MUL (l1, shade);
349   if (l1 > CFX_ONE)
350     l1 = CFX_ONE;
351   else if (l1 < 0)
352     l1 = 0;
353
354   s1 = CFX_MUL (s1, shade);
355   if (s1 > CFX_ONE)
356     s1 = CFX_ONE;
357   else if (s1 < 0)
358     s1 = 0;
359   
360   l = (guint8) CFX_INT (l1 * 255);
361   s = (guint8) CFX_INT (s1 * 255);
362
363   clutter_color_from_hls (dest, h, l, s);
364 }
365
366 /**
367  * clutter_color_to_pixel:
368  * @src: a #ClutterColor
369  *
370  * Converts @src into a packed 32 bit integer, containing
371  * all the four 8 bit channels used by #ClutterColor.
372  *
373  * Return value: a packed color
374  */
375 guint32
376 clutter_color_to_pixel (const ClutterColor *src)
377 {
378   g_return_val_if_fail (src != NULL, 0);
379   
380   return (src->alpha | src->blue << 8 | src->green << 16  | src->red << 24);
381 }
382
383 /**
384  * clutter_color_from_pixel:
385  * @dest: return location for a #ClutterColor
386  * @pixel: a 32 bit packed integer containing a color
387  *
388  * Converts @pixel from the packed representation of a four 8 bit channel
389  * color to a #ClutterColor.
390  */
391 void
392 clutter_color_from_pixel (ClutterColor *dest,
393                           guint32       pixel)
394 {
395   g_return_if_fail (dest != NULL);
396
397   dest->red = pixel >> 24;
398   dest->green = (pixel >> 16) & 0xff;
399   dest->blue = (pixel >> 8) & 0xff;
400   dest->alpha = pixel % 0xff;
401 }
402
403 /**
404  * clutter_color_parse:
405  * @color: a string specifiying a color
406  * @dest: return location for a #ClutterColor
407  *
408  * Parses a string definition of a color, filling the
409  * <structfield>red</structfield>, <structfield>green</structfield> and
410  * <structfield>blue</structfield> channels of @dest. The
411  * <structfield>alpha</structfield> channel is not changed. The
412  * color in @dest is not allocated.
413  *
414  * The color may be defined by any of the formats understood by
415  * <function>XParseColor</function>; these include literal color
416  * names, like <literal>Red</literal> or <literal>DarkSlateGray</literal>,
417  * or hexadecimal specifications like <literal>&num;3050b2</literal> or
418  * <literal>&num;333</literal>.
419  *
420  * Return value: %TRUE if parsing succeeded.
421  *
422  * Since: 0.2
423  */
424 gboolean
425 clutter_color_parse (const gchar  *color,
426                      ClutterColor *dest)
427 {
428   PangoColor pango_color;
429
430   if (pango_color_parse (&pango_color, color))
431     {
432       dest->red = pango_color.red;
433       dest->green = pango_color.green;
434       dest->blue = pango_color.blue;
435
436       return TRUE;
437     }
438   else
439     return FALSE;
440 }
441
442 /**
443  * clutter_color_to_string:
444  * @color: a #ClutterColor
445  *
446  * Returns a textual specification of @color in the hexadecimal form
447  * <literal>&num;rrrrggggbbbbaaaa</literal>, where <literal>r</literal>,
448  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
449  * hex digits representing the red, green, blue and alpha components
450  * respectively.
451  *
452  * Note: the returned string cannot be used to get the color back with
453  * clutter_color_parse().
454  *
455  * Return value: a newly-allocated text string
456  *
457  * Since: 0.2
458  */
459 gchar *
460 clutter_color_to_string (const ClutterColor *color)
461 {
462   g_return_val_if_fail (color != NULL, NULL);
463
464   return g_strdup_printf ("#%04x%04x%04x%04x",
465                           color->red,
466                           color->green,
467                           color->blue,
468                           color->alpha);
469 }
470
471 /**
472  * clutter_color_equal:
473  * @a: a #ClutterColor
474  * @b: a #ClutterColor
475  *
476  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
477  *
478  * Return value: %TRUE if the two colors are the same.
479  *
480  * Since: 0.2
481  */
482 gboolean
483 clutter_color_equal (const ClutterColor *a,
484                      const ClutterColor *b)
485 {
486   g_return_val_if_fail (a != NULL, FALSE);
487   g_return_val_if_fail (b != NULL, FALSE);
488
489   if (a == b)
490     return TRUE;
491
492   return (a->red == b->red &&
493           a->green == b->green &&
494           a->blue == b->blue &&
495           a->alpha == b->alpha);
496 }
497
498 /**
499  * clutter_color_copy:
500  * @color: a #ClutterColor
501  *
502  * Makes a copy of the color structure.  The result must be
503  * freed using clutter_color_free().
504  *
505  * Return value: an allocated copy of @color.
506  *
507  * Since: 0.2
508  */
509 ClutterColor *
510 clutter_color_copy (const ClutterColor *color)
511 {
512   ClutterColor *result;
513   
514   g_return_val_if_fail (color != NULL, NULL);
515
516   result = g_slice_new (ClutterColor);
517   *result = *color;
518
519   return result;
520 }
521
522 /**
523  * clutter_color_free:
524  * @color: a #ClutterColor
525  *
526  * Frees a color structure created with clutter_color_copy().
527  *
528  * Since: 0.2
529  */
530 void
531 clutter_color_free (ClutterColor *color)
532 {
533   g_return_if_fail (color != NULL);
534
535   g_slice_free (ClutterColor, color);
536 }
537
538 GType
539 clutter_color_get_type (void)
540 {
541   static GType our_type = 0;
542   
543   if (!our_type)
544     our_type = g_boxed_type_register_static ("ClutterColor",
545                                              (GBoxedCopyFunc) clutter_color_copy,
546                                              (GBoxedFreeFunc) clutter_color_free);
547   return our_type;
548 }