]> Pileus Git - ~andy/gtk/blob - gdk/gdkrgba.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gdk / gdkrgba.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23  */
24
25 #include "config.h"
26 #include "gdkrgba.h"
27 #include <string.h>
28 #include <errno.h>
29 #include <math.h>
30
31 #include "fallback-c89.c"
32
33 /**
34  * SECTION:rgba_colors
35  * @Short_description: RGBA colors
36  * @Title: RGBA Colors
37  *
38  * The #GdkRGBA struct is a convenient way to pass rgba colors around.
39  * It's based on cairo's way to deal with colors and mirrors its behavior.
40  * All values are in the range from 0.0 to 1.0 inclusive. So the color
41  * (0.0, 0.0, 0.0, 0.0) represents transparent black and
42  * (1.0, 1.0, 1.0, 1.0) is opaque white. Other values will be clamped
43  * to this range when drawing.
44  */
45
46 G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
47                      gdk_rgba_copy, gdk_rgba_free)
48
49 /**
50  * GdkRGBA:
51  * @red: The intensity of the red channel from 0.0 to 1.0 inclusive
52  * @green: The intensity of the green channel from 0.0 to 1.0 inclusive
53  * @blue: The intensity of the blue channel from 0.0 to 1.0 inclusive
54  * @alpha: The opacity of the color from 0.0 for completely translucent to
55  *   1.0 for opaque
56  *
57  * The GdkRGBA structure is used to represent a (possibly translucent)
58  * color, in a way that is compatible with cairos notion of color.
59  */
60
61 /**
62  * gdk_rgba_copy:
63  * @rgba: a #GdkRGBA
64  *
65  * Makes a copy of a #GdkRGBA structure.
66  *
67  * The result must be freed through gdk_rgba_free().
68  *
69  * Returns: A newly allocated #GdkRGBA, with the same contents as @rgba
70  *
71  * Since: 3.0
72  */
73 GdkRGBA *
74 gdk_rgba_copy (const GdkRGBA *rgba)
75 {
76   return g_slice_dup (GdkRGBA, rgba);
77 }
78
79 /**
80  * gdk_rgba_free:
81  * @rgba: a #GdkRGBA
82  *
83  * Frees a #GdkRGBA struct created with gdk_rgba_copy()
84  *
85  * Since: 3.0
86  */
87 void
88 gdk_rgba_free (GdkRGBA *rgba)
89 {
90   g_slice_free (GdkRGBA, rgba);
91 }
92
93 #define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
94
95 /* Parses a single color component from a rgb() or rgba() specification
96  * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
97  * in what we accept as follows:
98  *
99  *  - For non-percentage values, we accept floats in the range 0-255
100  *    not just [0-9]+ integers
101  *  - For percentage values we accept any float, not just
102  *     [ 0-9]+ | [0-9]* '.' [0-9]+
103  *  - We accept mixed percentages and non-percentages in a single
104  *    rgb() or rgba() specification.
105  */
106 static gboolean
107 parse_rgb_value (const gchar  *str,
108                  gchar       **endp,
109                  gdouble      *number)
110 {
111   const char *p;
112
113   *number = g_ascii_strtod (str, endp);
114   if (errno == ERANGE || *endp == str ||
115       isinf (*number) || isnan (*number))
116     return FALSE;
117
118   p = *endp;
119
120   SKIP_WHITESPACES (p);
121
122   if (*p == '%')
123     {
124       *endp = (char *)(p + 1);
125       *number = CLAMP(*number / 100., 0., 1.);
126     }
127   else
128     {
129       *number = CLAMP(*number / 255., 0., 1.);
130     }
131
132   return TRUE;
133 }
134
135 /**
136  * gdk_rgba_parse:
137  * @rgba: the #GdkRGBA struct to fill in
138  * @spec: the string specifying the color
139  *
140  * Parses a textual representation of a color, filling in
141  * the <structfield>red</structfield>, <structfield>green</structfield>,
142  * <structfield>blue</structfield> and <structfield>alpha</structfield>
143  * fields of the @rgba struct.
144  *
145  * The string can be either one of:
146  * <itemizedlist>
147  * <listitem>
148  * A standard name (Taken from the X11 rgb.txt file).
149  * </listitem>
150  * <listitem>
151  * A hex value in the form '&num;rgb' '&num;rrggbb' '&num;rrrgggbbb'
152  * or '&num;rrrrggggbbbb'
153  * </listitem>
154  * <listitem>
155  * A RGB color in the form 'rgb(r,g,b)' (In this case the color will
156  * have full opacity)
157  * </listitem>
158  * <listitem>
159  * A RGBA color in the form 'rgba(r,g,b,a)'
160  * </listitem>
161  * </itemizedlist>
162  *
163  * Where 'r', 'g', 'b' and 'a' are respectively the red, green, blue and
164  * alpha color values. In the last two cases, r g and b are either integers
165  * in the range 0 to 255 or precentage values in the range 0% to 100%, and
166  * a is a floating point value in the range 0 to 1.
167  *
168  * Returns: %TRUE if the parsing succeeded
169  *
170  * Since: 3.0
171  */
172 gboolean
173 gdk_rgba_parse (GdkRGBA     *rgba,
174                 const gchar *spec)
175 {
176   gboolean has_alpha;
177   gdouble r, g, b, a;
178   gchar *str = (gchar *) spec;
179   gchar *p;
180
181   if (strncmp (str, "rgba", 4) == 0)
182     {
183       has_alpha = TRUE;
184       str += 4;
185     }
186   else if (strncmp (str, "rgb", 3) == 0)
187     {
188       has_alpha = FALSE;
189       a = 1;
190       str += 3;
191     }
192   else
193     {
194       PangoColor pango_color;
195
196       /* Resort on PangoColor for rgb.txt color
197        * map and '#' prefixed colors
198        */
199       if (pango_color_parse (&pango_color, str))
200         {
201           if (rgba)
202             {
203               rgba->red = pango_color.red / 65535.;
204               rgba->green = pango_color.green / 65535.;
205               rgba->blue = pango_color.blue / 65535.;
206               rgba->alpha = 1;
207             }
208
209           return TRUE;
210         }
211       else
212         return FALSE;
213     }
214
215   SKIP_WHITESPACES (str);
216
217   if (*str != '(')
218     return FALSE;
219
220   str++;
221
222   /* Parse red */
223   SKIP_WHITESPACES (str);
224   if (!parse_rgb_value (str, &str, &r))
225     return FALSE;
226   SKIP_WHITESPACES (str);
227
228   if (*str != ',')
229     return FALSE;
230
231   str++;
232
233   /* Parse green */
234   SKIP_WHITESPACES (str);
235   if (!parse_rgb_value (str, &str, &g))
236     return FALSE;
237   SKIP_WHITESPACES (str);
238
239   if (*str != ',')
240     return FALSE;
241
242   str++;
243
244   /* Parse blue */
245   SKIP_WHITESPACES (str);
246   if (!parse_rgb_value (str, &str, &b))
247     return FALSE;
248   SKIP_WHITESPACES (str);
249
250   if (has_alpha)
251     {
252       if (*str != ',')
253         return FALSE;
254
255       str++;
256
257       SKIP_WHITESPACES (str);
258       a = g_ascii_strtod (str, &p);
259       if (errno == ERANGE || p == str ||
260           isinf (a) || isnan (a))
261         return FALSE;
262       str = p;
263       SKIP_WHITESPACES (str);
264     }
265
266   if (*str != ')')
267     return FALSE;
268
269   str++;
270
271   SKIP_WHITESPACES (str);
272
273   if (*str != '\0')
274     return FALSE;
275
276   if (rgba)
277     {
278       rgba->red = CLAMP (r, 0, 1);
279       rgba->green = CLAMP (g, 0, 1);
280       rgba->blue = CLAMP (b, 0, 1);
281       rgba->alpha = CLAMP (a, 0, 1);
282     }
283
284   return TRUE;
285 }
286
287 #undef SKIP_WHITESPACES
288
289 /**
290  * gdk_rgba_hash:
291  * @p: (type GdkRGBA): a #GdkRGBA pointer
292  *
293  * A hash function suitable for using for a hash
294  * table that stores #GdkRGBAs.
295  *
296  * Return value: The hash value for @p
297  *
298  * Since: 3.0
299  */
300 guint
301 gdk_rgba_hash (gconstpointer p)
302 {
303   const GdkRGBA *rgba = p;
304
305   return ((guint) (rgba->red * 65535) +
306           ((guint) (rgba->green * 65535) << 11) +
307           ((guint) (rgba->blue * 65535) << 22) +
308           ((guint) (rgba->alpha * 65535) >> 6));
309 }
310
311 /**
312  * gdk_rgba_equal:
313  * @p1: (type GdkRGBA): a #GdkRGBA pointer
314  * @p2: (type GdkRGBA): another #GdkRGBA pointer
315  *
316  * Compares two RGBA colors.
317  *
318  * Return value: %TRUE if the two colors compare equal
319  *
320  * Since: 3.0
321  */
322 gboolean
323 gdk_rgba_equal (gconstpointer p1,
324                 gconstpointer p2)
325 {
326   const GdkRGBA *rgba1, *rgba2;
327
328   rgba1 = p1;
329   rgba2 = p2;
330
331   if (rgba1->red == rgba2->red &&
332       rgba1->green == rgba2->green &&
333       rgba1->blue == rgba2->blue &&
334       rgba1->alpha == rgba2->alpha)
335     return TRUE;
336
337   return FALSE;
338 }
339
340 /**
341  * gdk_rgba_to_string:
342  * @rgba: a #GdkRGBA
343  *
344  * Returns a textual specification of @rgba in the form
345  * <literal>rgb (r, g, b)</literal> or
346  * <literal>rgba (r, g, b, a)</literal>,
347  * where 'r', 'g', 'b' and 'a' represent the red, green,
348  * blue and alpha values respectively. r, g, and b are
349  * represented as integers in the range 0 to 255, and a
350  * is represented as floating point value in the range 0 to 1.
351  *
352  * These string forms are string forms those supported by
353  * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
354  *
355  * Note that this string representation may loose some
356  * precision, since r, g and b are represented as 8-bit
357  * integers. If this is a concern, you should use a
358  * different representation.
359  *
360  * Returns: A newly allocated text string
361  *
362  * Since: 3.0
363  */
364 gchar *
365 gdk_rgba_to_string (const GdkRGBA *rgba)
366 {
367   if (rgba->alpha > 0.999)
368     {
369       return g_strdup_printf ("rgb(%d,%d,%d)",
370                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
371                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
372                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
373     }
374   else
375     {
376       gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
377
378       g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
379
380       return g_strdup_printf ("rgba(%d,%d,%d,%s)",
381                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
382                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
383                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
384                               alpha);
385     }
386 }