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