2007-01-16 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_shade:
321  * @src: a #ClutterColor
322  * @dest: return location for the shaded color
323  * @shade: #ClutterFixed the shade factor to apply
324  * 
325  * Shades @src by the factor of @shade and saves the modified
326  * color into @dest.
327  */
328 void
329 clutter_color_shadex (const ClutterColor *src,
330                       ClutterColor       *dest,
331                       ClutterFixed        shade)
332 {
333   guint8 h, l, s;
334   ClutterFixed l1, s1;
335
336   g_return_if_fail (src != NULL);
337   g_return_if_fail (dest != NULL);
338   
339   clutter_color_to_hls (src, &h, &l, &s);
340
341   l1 = CLUTTER_INT_TO_FIXED (l) / 255;
342   s1 = CLUTTER_INT_TO_FIXED (s) / 255;
343
344   l1 = CFX_MUL (l1, shade);
345   if (l1 > CFX_ONE)
346     l1 = CFX_ONE;
347   else if (l1 < 0)
348     l1 = 0;
349
350   s1 = CFX_MUL (s1, shade);
351   if (s1 > CFX_ONE)
352     s1 = CFX_ONE;
353   else if (s1 < 0)
354     s1 = 0;
355   
356   l = (guint8) CFX_INT (l1 * 255);
357   s = (guint8) CFX_INT (s1 * 255);
358
359   clutter_color_from_hls (dest, h, l, s);
360 }
361
362 /**
363  * clutter_color_to_pixel:
364  * @src: a #ClutterColor
365  *
366  * Converts @src into a packed 32 bit integer, containing
367  * all the four 8 bit channels used by #ClutterColor.
368  *
369  * Return value: a packed color
370  */
371 guint32
372 clutter_color_to_pixel (const ClutterColor *src)
373 {
374   g_return_val_if_fail (src != NULL, 0);
375   
376   return (src->alpha | src->blue << 8 | src->green << 16  | src->red << 24);
377 }
378
379 /**
380  * clutter_color_from_pixel:
381  * @dest: return location for a #ClutterColor
382  * @pixel: a 32 bit packed integer containing a color
383  *
384  * Converts @pixel from the packed representation of a four 8 bit channel
385  * color to a #ClutterColor.
386  */
387 void
388 clutter_color_from_pixel (ClutterColor *dest,
389                           guint32       pixel)
390 {
391   g_return_if_fail (dest != NULL);
392
393   dest->red = pixel >> 24;
394   dest->green = (pixel >> 16) & 0xff;
395   dest->blue = (pixel >> 8) & 0xff;
396   dest->alpha = pixel % 0xff;
397 }
398
399 /**
400  * clutter_color_parse:
401  * @color: a string specifiying a color
402  * @dest: return location for a #ClutterColor
403  *
404  * Parses a string definition of a color, filling the
405  * <structfield>red</structfield>, <structfield>green</structfield> and
406  * <structfield>blue</structfield> channels of @dest. The
407  * <structfield>alpha</structfield> channel is not changed. The
408  * color in @dest is not allocated.
409  *
410  * The color may be defined by any of the formats understood by
411  * <function>XParseColor</function>; these include literal color
412  * names, like <literal>Red</literal> or <literal>DarkSlateGray</literal>,
413  * or hexadecimal specifications like <literal>&num;3050b2</literal> or
414  * <literal>&num;333</literal>.
415  *
416  * Return value: %TRUE if parsing succeeded.
417  *
418  * Since: 0.2
419  */
420 gboolean
421 clutter_color_parse (const gchar  *color,
422                      ClutterColor *dest)
423 {
424   PangoColor pango_color;
425
426   if (pango_color_parse (&pango_color, color))
427     {
428       dest->red = pango_color.red;
429       dest->green = pango_color.green;
430       dest->blue = pango_color.blue;
431
432       return TRUE;
433     }
434   else
435     return FALSE;
436 }
437
438 /**
439  * clutter_color_to_string:
440  * @color: a #ClutterColor
441  *
442  * Returns a textual specification of @color in the hexadecimal form
443  * <literal>&num;rrrrggggbbbbaaaa</literal>, where <literal>r</literal>,
444  * <literal>g</literal>, <literal>b</literal> and <literal>a</literal> are
445  * hex digits representing the red, green, blue and alpha components
446  * respectively.
447  *
448  * Note: the returned string cannot be used to get the color back with
449  * clutter_color_parse().
450  *
451  * Return value: a newly-allocated text string
452  *
453  * Since: 0.2
454  */
455 gchar *
456 clutter_color_to_string (const ClutterColor *color)
457 {
458   g_return_val_if_fail (color != NULL, NULL);
459
460   return g_strdup_printf ("#%04x%04x%04x%04x",
461                           color->red,
462                           color->green,
463                           color->blue,
464                           color->alpha);
465 }
466
467 /**
468  * clutter_color_equal:
469  * @a: a #ClutterColor
470  * @b: a #ClutterColor
471  *
472  * Compares two #ClutterColor<!-- -->s and checks if they are the same.
473  *
474  * Return: %TRUE if the two colors are the same.
475  *
476  * Since: 0.2
477  */
478 gboolean
479 clutter_color_equal (const ClutterColor *a,
480                      const ClutterColor *b)
481 {
482   g_return_val_if_fail (a != NULL, FALSE);
483   g_return_val_if_fail (b != NULL, FALSE);
484
485   if (a == b)
486     return TRUE;
487
488   return (a->red == b->red &&
489           a->green == b->green &&
490           a->blue == b->blue &&
491           a->alpha == b->alpha);
492 }
493
494 /**
495  * clutter_color_copy:
496  * @color: a #ClutterColor
497  *
498  * Makes a copy of the color structure.  The result must be
499  * freed using clutter_color_free().
500  *
501  * Return value: an allocated copy of @color.
502  *
503  * Since: 0.2
504  */
505 ClutterColor *
506 clutter_color_copy (const ClutterColor *color)
507 {
508   ClutterColor *result;
509   
510   g_return_val_if_fail (color != NULL, NULL);
511
512   result = g_slice_new (ClutterColor);
513   *result = *color;
514
515   return result;
516 }
517
518 /**
519  * clutter_color_free:
520  * @color: a #ClutterColor
521  *
522  * Frees a color structure created with clutter_color_copy().
523  *
524  * Since: 0.2
525  */
526 void
527 clutter_color_free (ClutterColor *color)
528 {
529   g_return_if_fail (color != NULL);
530
531   g_slice_free (ClutterColor, color);
532 }
533
534 GType
535 clutter_color_get_type (void)
536 {
537   static GType our_type = 0;
538   
539   if (!our_type)
540     our_type = g_boxed_type_register_static ("ClutterColor",
541                                              (GBoxedCopyFunc) clutter_color_copy,
542                                              (GBoxedFreeFunc) clutter_color_free);
543   return our_type;
544 }