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