]> Pileus Git - ~andy/gtk/blob - gtk/gtksymboliccolor.c
tests: Add simple test for image clipboard
[~andy/gtk] / gtk / gtksymboliccolor.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gtksymboliccolor.h"
22 #include "gtkstyleproperties.h"
23 #include "gtkintl.h"
24
25 /**
26  * SECTION:gtksymboliccolor
27  * @Short_description: Symbolic colors
28  * @Title: GtkSymbolicColor
29  *
30  * GtkSymbolicColor is a boxed type that represents a symbolic color.
31  * It is the result of parsing a
32  * <link linkend="gtkcssprovider-symbolic-colors">color expression</link>.
33  * To obtain the color represented by a GtkSymbolicColor, it has to
34  * be resolved with gtk_symbolic_color_resolve(), which replaces all
35  * symbolic color references by the colors they refer to (in a given
36  * context) and evaluates mix, shade and other expressions, resulting
37  * in a #GdkRGBA value.
38  *
39  * It is not normally necessary to deal directly with #GtkSymbolicColors,
40  * since they are mostly used behind the scenes by #GtkStyleContext and
41  * #GtkCssProvider.
42  */
43
44 G_DEFINE_BOXED_TYPE (GtkSymbolicColor, gtk_symbolic_color,
45                      gtk_symbolic_color_ref, gtk_symbolic_color_unref)
46
47 /* Symbolic colors */
48 typedef enum {
49   COLOR_TYPE_LITERAL,
50   COLOR_TYPE_NAME,
51   COLOR_TYPE_SHADE,
52   COLOR_TYPE_ALPHA,
53   COLOR_TYPE_MIX
54 } ColorType;
55
56 struct _GtkSymbolicColor
57 {
58   ColorType type;
59   guint ref_count;
60
61   union
62   {
63     GdkRGBA color;
64     gchar *name;
65
66     struct
67     {
68       GtkSymbolicColor *color;
69       gdouble factor;
70     } shade, alpha;
71
72     struct
73     {
74       GtkSymbolicColor *color1;
75       GtkSymbolicColor *color2;
76       gdouble factor;
77     } mix;
78   };
79 };
80
81 /**
82  * gtk_symbolic_color_new_literal:
83  * @color: a #GdkRGBA
84  *
85  * Creates a symbolic color pointing to a literal color.
86  *
87  * Returns: a newly created #GtkSymbolicColor
88  *
89  * Since: 3.0
90  **/
91 GtkSymbolicColor *
92 gtk_symbolic_color_new_literal (const GdkRGBA *color)
93 {
94   GtkSymbolicColor *symbolic_color;
95
96   g_return_val_if_fail (color != NULL, NULL);
97
98   symbolic_color = g_slice_new0 (GtkSymbolicColor);
99   symbolic_color->type = COLOR_TYPE_LITERAL;
100   symbolic_color->color = *color;
101   symbolic_color->ref_count = 1;
102
103   return symbolic_color;
104 }
105
106 /**
107  * gtk_symbolic_color_new_name:
108  * @name: color name
109  *
110  * Creates a symbolic color pointing to an unresolved named
111  * color. See gtk_style_context_lookup_color() and
112  * gtk_style_properties_lookup_color().
113  *
114  * Returns: a newly created #GtkSymbolicColor
115  *
116  * Since: 3.0
117  **/
118 GtkSymbolicColor *
119 gtk_symbolic_color_new_name (const gchar *name)
120 {
121   GtkSymbolicColor *symbolic_color;
122
123   g_return_val_if_fail (name != NULL, NULL);
124
125   symbolic_color = g_slice_new0 (GtkSymbolicColor);
126   symbolic_color->type = COLOR_TYPE_NAME;
127   symbolic_color->name = g_strdup (name);
128   symbolic_color->ref_count = 1;
129
130   return symbolic_color;
131 }
132
133 /**
134  * gtk_symbolic_color_new_shade:
135  * @color: another #GtkSymbolicColor
136  * @factor: shading factor to apply to @color
137  *
138  * Creates a symbolic color defined as a shade of
139  * another color. A factor > 1.0 would resolve to
140  * a brighter color, while < 1.0 would resolve to
141  * a darker color.
142  *
143  * Returns: A newly created #GtkSymbolicColor
144  *
145  * Since: 3.0
146  **/
147 GtkSymbolicColor *
148 gtk_symbolic_color_new_shade (GtkSymbolicColor *color,
149                               gdouble           factor)
150 {
151   GtkSymbolicColor *symbolic_color;
152
153   g_return_val_if_fail (color != NULL, NULL);
154
155   symbolic_color = g_slice_new0 (GtkSymbolicColor);
156   symbolic_color->type = COLOR_TYPE_SHADE;
157   symbolic_color->shade.color = gtk_symbolic_color_ref (color);
158   symbolic_color->shade.factor = factor;
159   symbolic_color->ref_count = 1;
160
161   return symbolic_color;
162 }
163
164 /**
165  * gtk_symbolic_color_new_alpha:
166  * @color: another #GtkSymbolicColor
167  * @factor: factor to apply to @color alpha
168  *
169  * Creates a symbolic color by modifying the relative alpha
170  * value of @color. A factor < 1.0 would resolve to a more
171  * transparent color, while > 1.0 would resolve to a more
172  * opaque color.
173  *
174  * Returns: A newly created #GtkSymbolicColor
175  *
176  * Since: 3.0
177  **/
178 GtkSymbolicColor *
179 gtk_symbolic_color_new_alpha (GtkSymbolicColor *color,
180                               gdouble           factor)
181 {
182   GtkSymbolicColor *symbolic_color;
183
184   g_return_val_if_fail (color != NULL, NULL);
185
186   symbolic_color = g_slice_new0 (GtkSymbolicColor);
187   symbolic_color->type = COLOR_TYPE_ALPHA;
188   symbolic_color->alpha.color = gtk_symbolic_color_ref (color);
189   symbolic_color->alpha.factor = factor;
190   symbolic_color->ref_count = 1;
191
192   return symbolic_color;
193 }
194
195 /**
196  * gtk_symbolic_color_new_mix:
197  * @color1: color to mix
198  * @color2: another color to mix
199  * @factor: mix factor
200  *
201  * Creates a symbolic color defined as a mix of another
202  * two colors. a mix factor of 0 would resolve to @color1,
203  * while a factor of 1 would resolve to @color2.
204  *
205  * Returns: A newly created #GtkSymbolicColor
206  *
207  * Since: 3.0
208  **/
209 GtkSymbolicColor *
210 gtk_symbolic_color_new_mix (GtkSymbolicColor *color1,
211                             GtkSymbolicColor *color2,
212                             gdouble           factor)
213 {
214   GtkSymbolicColor *symbolic_color;
215
216   g_return_val_if_fail (color1 != NULL, NULL);
217   g_return_val_if_fail (color1 != NULL, NULL);
218
219   symbolic_color = g_slice_new0 (GtkSymbolicColor);
220   symbolic_color->type = COLOR_TYPE_MIX;
221   symbolic_color->mix.color1 = gtk_symbolic_color_ref (color1);
222   symbolic_color->mix.color2 = gtk_symbolic_color_ref (color2);
223   symbolic_color->mix.factor = factor;
224   symbolic_color->ref_count = 1;
225
226   return symbolic_color;
227 }
228
229 /**
230  * gtk_symbolic_color_ref:
231  * @color: a #GtkSymbolicColor
232  *
233  * Increases the reference count of @color
234  *
235  * Returns: the same @color
236  *
237  * Since: 3.0
238  **/
239 GtkSymbolicColor *
240 gtk_symbolic_color_ref (GtkSymbolicColor *color)
241 {
242   g_return_val_if_fail (color != NULL, NULL);
243
244   color->ref_count++;
245
246   return color;
247 }
248
249 /**
250  * gtk_symbolic_color_unref:
251  * @color: a #GtkSymbolicColor
252  *
253  * Decreases the reference count of @color, freeing its memory if the
254  * reference count reaches 0.
255  *
256  * Since: 3.0
257  **/
258 void
259 gtk_symbolic_color_unref (GtkSymbolicColor *color)
260 {
261   g_return_if_fail (color != NULL);
262
263   color->ref_count--;
264
265   if (color->ref_count == 0)
266     {
267       switch (color->type)
268         {
269         case COLOR_TYPE_NAME:
270           g_free (color->name);
271           break;
272         case COLOR_TYPE_SHADE:
273           gtk_symbolic_color_unref (color->shade.color);
274           break;
275         case COLOR_TYPE_ALPHA:
276           gtk_symbolic_color_unref (color->alpha.color);
277           break;
278         case COLOR_TYPE_MIX:
279           gtk_symbolic_color_unref (color->mix.color1);
280           gtk_symbolic_color_unref (color->mix.color2);
281           break;
282         default:
283           break;
284         }
285
286       g_slice_free (GtkSymbolicColor, color);
287     }
288 }
289
290 static void
291 rgb_to_hls (gdouble *r,
292             gdouble *g,
293             gdouble *b)
294 {
295   gdouble min;
296   gdouble max;
297   gdouble red;
298   gdouble green;
299   gdouble blue;
300   gdouble h, l, s;
301   gdouble delta;
302   
303   red = *r;
304   green = *g;
305   blue = *b;
306   
307   if (red > green)
308     {
309       if (red > blue)
310         max = red;
311       else
312         max = blue;
313       
314       if (green < blue)
315         min = green;
316       else
317         min = blue;
318     }
319   else
320     {
321       if (green > blue)
322         max = green;
323       else
324         max = blue;
325       
326       if (red < blue)
327         min = red;
328       else
329         min = blue;
330     }
331   
332   l = (max + min) / 2;
333   s = 0;
334   h = 0;
335   
336   if (max != min)
337     {
338       if (l <= 0.5)
339         s = (max - min) / (max + min);
340       else
341         s = (max - min) / (2 - max - min);
342       
343       delta = max -min;
344       if (red == max)
345         h = (green - blue) / delta;
346       else if (green == max)
347         h = 2 + (blue - red) / delta;
348       else if (blue == max)
349         h = 4 + (red - green) / delta;
350       
351       h *= 60;
352       if (h < 0.0)
353         h += 360;
354     }
355   
356   *r = h;
357   *g = l;
358   *b = s;
359 }
360
361 static void
362 hls_to_rgb (gdouble *h,
363             gdouble *l,
364             gdouble *s)
365 {
366   gdouble hue;
367   gdouble lightness;
368   gdouble saturation;
369   gdouble m1, m2;
370   gdouble r, g, b;
371   
372   lightness = *l;
373   saturation = *s;
374   
375   if (lightness <= 0.5)
376     m2 = lightness * (1 + saturation);
377   else
378     m2 = lightness + saturation - lightness * saturation;
379   m1 = 2 * lightness - m2;
380   
381   if (saturation == 0)
382     {
383       *h = lightness;
384       *l = lightness;
385       *s = lightness;
386     }
387   else
388     {
389       hue = *h + 120;
390       while (hue > 360)
391         hue -= 360;
392       while (hue < 0)
393         hue += 360;
394       
395       if (hue < 60)
396         r = m1 + (m2 - m1) * hue / 60;
397       else if (hue < 180)
398         r = m2;
399       else if (hue < 240)
400         r = m1 + (m2 - m1) * (240 - hue) / 60;
401       else
402         r = m1;
403       
404       hue = *h;
405       while (hue > 360)
406         hue -= 360;
407       while (hue < 0)
408         hue += 360;
409       
410       if (hue < 60)
411         g = m1 + (m2 - m1) * hue / 60;
412       else if (hue < 180)
413         g = m2;
414       else if (hue < 240)
415         g = m1 + (m2 - m1) * (240 - hue) / 60;
416       else
417         g = m1;
418       
419       hue = *h - 120;
420       while (hue > 360)
421         hue -= 360;
422       while (hue < 0)
423         hue += 360;
424       
425       if (hue < 60)
426         b = m1 + (m2 - m1) * hue / 60;
427       else if (hue < 180)
428         b = m2;
429       else if (hue < 240)
430         b = m1 + (m2 - m1) * (240 - hue) / 60;
431       else
432         b = m1;
433       
434       *h = r;
435       *l = g;
436       *s = b;
437     }
438 }
439
440 static void
441 _shade_color (GdkRGBA *color,
442               gdouble  factor)
443 {
444   GdkRGBA temp;
445
446   temp = *color;
447   rgb_to_hls (&temp.red, &temp.green, &temp.blue);
448
449   temp.green *= factor;
450   if (temp.green > 1.0)
451     temp.green = 1.0;
452   else if (temp.green < 0.0)
453     temp.green = 0.0;
454
455   temp.blue *= factor;
456   if (temp.blue > 1.0)
457     temp.blue = 1.0;
458   else if (temp.blue < 0.0)
459     temp.blue = 0.0;
460
461   hls_to_rgb (&temp.red, &temp.green, &temp.blue);
462   *color = temp;
463 }
464
465 /**
466  * gtk_symbolic_color_resolve:
467  * @color: a #GtkSymbolicColor
468  * @props: (allow-none): #GtkStyleProperties to use when resolving
469  *    named colors, or %NULL
470  * @resolved_color: (out): return location for the resolved color
471  *
472  * If @color is resolvable, @resolved_color will be filled in
473  * with the resolved color, and %TRUE will be returned. Generally,
474  * if @color can't be resolved, it is due to it being defined on
475  * top of a named color that doesn't exist in @props.
476  *
477  * When @props is %NULL, resolving of named colors will fail, so if
478  * your @color is or references such a color, this function will
479  * return %FALSE.
480  *
481  * Returns: %TRUE if the color has been resolved
482  *
483  * Since: 3.0
484  **/
485 gboolean
486 gtk_symbolic_color_resolve (GtkSymbolicColor   *color,
487                             GtkStyleProperties *props,
488                             GdkRGBA            *resolved_color)
489 {
490   g_return_val_if_fail (color != NULL, FALSE);
491   g_return_val_if_fail (resolved_color != NULL, FALSE);
492   g_return_val_if_fail (props == NULL || GTK_IS_STYLE_PROPERTIES (props), FALSE);
493
494   switch (color->type)
495     {
496     case COLOR_TYPE_LITERAL:
497       *resolved_color = color->color;
498       return TRUE;
499     case COLOR_TYPE_NAME:
500       {
501         GtkSymbolicColor *named_color;
502
503         if (props == NULL)
504           return FALSE;
505
506         named_color = gtk_style_properties_lookup_color (props, color->name);
507
508         if (!named_color)
509           return FALSE;
510
511         return gtk_symbolic_color_resolve (named_color, props, resolved_color);
512       }
513
514       break;
515     case COLOR_TYPE_SHADE:
516       {
517         GdkRGBA shade;
518
519         if (!gtk_symbolic_color_resolve (color->shade.color, props, &shade))
520           return FALSE;
521
522         _shade_color (&shade, color->shade.factor);
523         *resolved_color = shade;
524
525         return TRUE;
526       }
527
528       break;
529     case COLOR_TYPE_ALPHA:
530       {
531         GdkRGBA alpha;
532
533         if (!gtk_symbolic_color_resolve (color->alpha.color, props, &alpha))
534           return FALSE;
535
536         *resolved_color = alpha;
537         resolved_color->alpha = CLAMP (alpha.alpha * color->alpha.factor, 0, 1);
538
539         return TRUE;
540       }
541     case COLOR_TYPE_MIX:
542       {
543         GdkRGBA color1, color2;
544
545         if (!gtk_symbolic_color_resolve (color->mix.color1, props, &color1))
546           return FALSE;
547
548         if (!gtk_symbolic_color_resolve (color->mix.color2, props, &color2))
549           return FALSE;
550
551         resolved_color->red = CLAMP (color1.red + ((color2.red - color1.red) * color->mix.factor), 0, 1);
552         resolved_color->green = CLAMP (color1.green + ((color2.green - color1.green) * color->mix.factor), 0, 1);
553         resolved_color->blue = CLAMP (color1.blue + ((color2.blue - color1.blue) * color->mix.factor), 0, 1);
554         resolved_color->alpha = CLAMP (color1.alpha + ((color2.alpha - color1.alpha) * color->mix.factor), 0, 1);
555
556         return TRUE;
557       }
558
559       break;
560     default:
561       g_assert_not_reached ();
562     }
563
564   return FALSE;
565 }
566
567 /**
568  * gtk_symbolic_color_to_string:
569  * @color: color to convert to a string
570  *
571  * Converts the given @color to a string representation. This is useful
572  * both for debugging and for serialization of strings. The format of
573  * the string may change between different versions of GTK, but it is
574  * guaranteed that the GTK css parser is able to read the string and
575  * create the same symbolic color from it.
576  *
577  * Returns: a new string representing @color
578  **/
579 char *
580 gtk_symbolic_color_to_string (GtkSymbolicColor *color)
581 {
582   char *s;
583
584   g_return_val_if_fail (color != NULL, NULL);
585
586   switch (color->type)
587     {
588     case COLOR_TYPE_LITERAL:
589       s = gdk_rgba_to_string (&color->color);
590       break;
591     case COLOR_TYPE_NAME:
592       s = g_strconcat ("@", color->name, NULL);
593       break;
594     case COLOR_TYPE_SHADE:
595       {
596         char *color_string = gtk_symbolic_color_to_string (color->shade.color);
597         char factor[G_ASCII_DTOSTR_BUF_SIZE];
598
599         g_ascii_dtostr (factor, sizeof (factor), color->shade.factor);
600         s = g_strdup_printf ("shade (%s, %s)", color_string, factor);
601         g_free (color_string);
602       }
603       break;
604     case COLOR_TYPE_ALPHA:
605       {
606         char *color_string = gtk_symbolic_color_to_string (color->shade.color);
607         char factor[G_ASCII_DTOSTR_BUF_SIZE];
608
609         g_ascii_dtostr (factor, sizeof (factor), color->alpha.factor);
610         s = g_strdup_printf ("alpha (%s, %s)", color_string, factor);
611         g_free (color_string);
612       }
613       break;
614     case COLOR_TYPE_MIX:
615       {
616         char *color_string1 = gtk_symbolic_color_to_string (color->mix.color1);
617         char *color_string2 = gtk_symbolic_color_to_string (color->mix.color2);
618         char factor[G_ASCII_DTOSTR_BUF_SIZE];
619
620         g_ascii_dtostr (factor, sizeof (factor), color->mix.factor);
621         s = g_strdup_printf ("mix (%s, %s, %s)", color_string1, color_string2, factor);
622         g_free (color_string1);
623         g_free (color_string2);
624       }
625       break;
626     default:
627       g_assert_not_reached ();
628     }
629
630   return s;
631
632 }