]> Pileus Git - ~andy/gtk/blob - gdk/gdkrgba.c
Switch to CSS interpretation of rgb() and rgba() colors
[~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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26
27 #include "config.h"
28 #include "gdkrgba.h"
29 #include <string.h>
30
31 /**
32  * SECTION:rgba_colors
33  * @Short_description: RGBA colors
34  * @Title: RGBA Colors
35  */
36
37 G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
38                      gdk_rgba_copy, gdk_rgba_free)
39
40 /**
41  * gdk_rgba_copy:
42  * @rgba: a #GdkRGBA
43  *
44  * Makes a copy of a #GdkRGBA structure, the result must be freed
45  * through gdk_rgba_free().
46  *
47  * Returns: A newly allocated #GdkRGBA
48  **/
49 GdkRGBA *
50 gdk_rgba_copy (GdkRGBA *rgba)
51 {
52   GdkRGBA *copy;
53
54   copy = g_slice_new (GdkRGBA);
55   copy->red = rgba->red;
56   copy->green = rgba->green;
57   copy->blue = rgba->blue;
58   copy->alpha = rgba->alpha;
59
60   return copy;
61 }
62
63 /**
64  * gdk_rgba_free:
65  * @rgba: a #GdkRGBA
66  *
67  * Frees a #GdkRGBA struct created with gdk_rgba_copy()
68  **/
69 void
70 gdk_rgba_free (GdkRGBA *rgba)
71 {
72   g_slice_free (GdkRGBA, rgba);
73 }
74
75 #define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
76
77 /* Parses a single color component from a rgb() or rgba() specification
78  * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
79  * in what we accept as follows:
80  *
81  *  - For non-percentage values, we accept floats in the range 0-255
82  *    not just [0-9]+ integers
83  *  - For percentage values we accept any float, not just
84  *     [ 0-9]+ | [0-9]* '.' [0-9]+
85  *  - We accept mixed percentages and non-percentages in a single
86  *    rgb() or rgba() specification.
87  */
88 static double
89 parse_rgb_value (const char  *str,
90                  char       **endp)
91 {
92   double number;
93   const char *p;
94
95   number = g_ascii_strtod (str, endp);
96
97   p = *endp;
98
99   SKIP_WHITESPACES (p);
100
101   if (*p == '%')
102     {
103       *endp = (char *)(p + 1);
104       return CLAMP(number / 100., 0., 1.);
105     }
106   else
107     {
108       return CLAMP(number / 255., 0., 1.);
109     }
110 }
111
112 /**
113  * gdk_rgba_parse:
114  * @spec: the string specifying the color
115  * @rgba: the #GdkRGBA struct to fill in
116  *
117  * Parses a textual representation of a color, filling in
118  * the <structfield>red</structfield>, <structfield>green</structfield>,
119  * <structfield>blue</structfield> and <structfield>alpha</structfield>
120  * fields of the @rgba struct.
121  *
122  * The string can be either one of:
123  * <itemizedlist>
124  * <listitem>
125  * A standard name (Taken from the X11 rgb.txt file).
126  * </listitem>
127  * <listitem>
128  * A hex value in the form '#rgb' '#rrggbb' '#rrrgggbbb' or '#rrrrggggbbbb'
129  * </listitem>
130  * <listitem>
131  * A RGB color in the form 'rgb(r,g,b)' (In this case the color will
132  * have full opacity)
133  * </listitem>
134  * <listitem>
135  * A RGBA color in the form 'rgba(r,g,b,a)'
136  * </listitem>
137  * </itemizedlist>
138  *
139  * Where 'r', 'g', 'b' and 'a' are respectively the red, green, blue and
140  * alpha color values. In the last two cases, r g and b are either integers
141  * in the range 0 to 255 or precentage values in the range 0% to 100%, and
142  * a is a floating point value in the range 0 to 1.
143  *
144  * Returns: %TRUE if the parsing succeeded
145  **/
146 gboolean
147 gdk_rgba_parse (const gchar *spec,
148                 GdkRGBA     *rgba)
149 {
150   gboolean has_alpha;
151   gdouble r, g, b, a;
152   gchar *str = (gchar *) spec;
153
154   if (strncmp (str, "rgba", 4) == 0)
155     {
156       has_alpha = TRUE;
157       str += 4;
158     }
159   else if (strncmp (str, "rgb", 3) == 0)
160     {
161       has_alpha = FALSE;
162       a = 1;
163       str += 3;
164     }
165   else
166     {
167       PangoColor pango_color;
168
169       /* Resort on PangoColor for rgb.txt color
170        * map and '#' prefixed colors */
171       if (pango_color_parse (&pango_color, str))
172         {
173           if (rgba)
174             {
175               rgba->red = pango_color.red / 65535.;
176               rgba->green = pango_color.green / 65535.;
177               rgba->blue = pango_color.blue / 65535.;
178               rgba->alpha = 1;
179             }
180
181           return TRUE;
182         }
183       else
184         return FALSE;
185     }
186
187   SKIP_WHITESPACES (str);
188
189   if (*str != '(')
190     return FALSE;
191
192   str++;
193
194   /* Parse red */
195   SKIP_WHITESPACES (str);
196   r = parse_rgb_value (str, &str);
197   SKIP_WHITESPACES (str);
198
199   if (*str != ',')
200     return FALSE;
201
202   str++;
203
204   /* Parse green */
205   SKIP_WHITESPACES (str);
206   g = parse_rgb_value (str, &str);
207   SKIP_WHITESPACES (str);
208
209   if (*str != ',')
210     return FALSE;
211
212   str++;
213
214   /* Parse blue */
215   SKIP_WHITESPACES (str);
216   b = parse_rgb_value (str, &str);
217   SKIP_WHITESPACES (str);
218
219   if (has_alpha)
220     {
221       if (*str != ',')
222         return FALSE;
223
224       str++;
225
226       SKIP_WHITESPACES (str);
227       a = g_ascii_strtod (str, &str);
228       SKIP_WHITESPACES (str);
229     }
230
231   if (*str != ')')
232     return FALSE;
233
234   if (rgba)
235     {
236       rgba->red = CLAMP (r, 0, 1);
237       rgba->green = CLAMP (g, 0, 1);
238       rgba->blue = CLAMP (b, 0, 1);
239       rgba->alpha = CLAMP (a, 0, 1);
240     }
241
242   return TRUE;
243 }
244
245 #undef SKIP_WHITESPACES
246
247 /**
248  * gdk_rgba_hash:
249  * @p: a #GdkRGBA pointer.
250  *
251  * A hash function suitable for using for a hash
252  * table that stores #GdkRGBA<!-- -->s.
253  *
254  * Return value: The hash function applied to @p
255  **/
256 guint
257 gdk_rgba_hash (gconstpointer p)
258 {
259   const GdkRGBA *rgba = p;
260
261   return ((guint) (rgba->red * 65535) +
262           ((guint) (rgba->green * 65535) << 11) +
263           ((guint) (rgba->blue * 65535) << 22) +
264           ((guint) (rgba->alpha * 65535) >> 6));
265 }
266
267 /**
268  * gdk_rgba_equal:
269  * @p1: a #GdkRGBA pointer.
270  * @p2: another #GdkRGBA pointer.
271  *
272  * Compares two RGBA colors.
273  *
274  * Return value: %TRUE if the two colors compare equal
275  **/
276 gboolean
277 gdk_rgba_equal (gconstpointer p1,
278                 gconstpointer p2)
279 {
280   const GdkRGBA *rgba1, *rgba2;
281
282   rgba1 = p1;
283   rgba2 = p2;
284
285   if (rgba1->red == rgba2->red &&
286       rgba1->green == rgba2->green &&
287       rgba1->blue == rgba2->blue &&
288       rgba1->alpha == rgba2->alpha)
289     return TRUE;
290
291   return FALSE;
292 }
293
294 /**
295  * gdk_rgba_to_string:
296  * @rgba: a #GdkRGBA
297  *
298  * Returns a textual specification of @rgba in the form <literal>rgb
299  * (r, g, b)</literal> or <literal>rgba (r, g, b, a)</literal>,
300  * where 'r', 'g', 'b' and 'a' represent the red, green, blue and alpha values
301  * respectively. r, g, and b are integers in the range 0 to 255, and a
302  * is a floating point value in the range 0 to 1.
303  *
304  * (These string forms are string forms those supported by the CSS3 colors module)
305  *
306  * Returns: A newly allocated text string
307  **/
308 gchar *
309 gdk_rgba_to_string (const GdkRGBA *rgba)
310 {
311   if (rgba->alpha > 0.999)
312     {
313       return g_strdup_printf ("rgb(%d,%d,%d)",
314                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
315                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
316                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
317     }
318   else
319     {
320       gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
321
322       g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
323
324       return g_strdup_printf ("rgba(%d,%d,%d,%s)",
325                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
326                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
327                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
328                               alpha);
329     }
330 }