]> Pileus Git - ~andy/gtk/blob - gdk/gdkrgba.c
Use g_slice_dup
[~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  * 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 double
105 parse_rgb_value (const char  *str,
106                  char       **endp)
107 {
108   double number;
109   const char *p;
110
111   number = g_ascii_strtod (str, endp);
112
113   p = *endp;
114
115   SKIP_WHITESPACES (p);
116
117   if (*p == '%')
118     {
119       *endp = (char *)(p + 1);
120       return CLAMP(number / 100., 0., 1.);
121     }
122   else
123     {
124       return CLAMP(number / 255., 0., 1.);
125     }
126 }
127
128 /**
129  * gdk_rgba_parse:
130  * @rgba: the #GdkRGBA struct to fill in
131  * @spec: the string specifying the color
132  *
133  * Parses a textual representation of a color, filling in
134  * the <structfield>red</structfield>, <structfield>green</structfield>,
135  * <structfield>blue</structfield> and <structfield>alpha</structfield>
136  * fields of the @rgba struct.
137  *
138  * The string can be either one of:
139  * <itemizedlist>
140  * <listitem>
141  * A standard name (Taken from the X11 rgb.txt file).
142  * </listitem>
143  * <listitem>
144  * A hex value in the form '#rgb' '#rrggbb' '#rrrgggbbb' or '#rrrrggggbbbb'
145  * </listitem>
146  * <listitem>
147  * A RGB color in the form 'rgb(r,g,b)' (In this case the color will
148  * have full opacity)
149  * </listitem>
150  * <listitem>
151  * A RGBA color in the form 'rgba(r,g,b,a)'
152  * </listitem>
153  * </itemizedlist>
154  *
155  * Where 'r', 'g', 'b' and 'a' are respectively the red, green, blue and
156  * alpha color values. In the last two cases, r g and b are either integers
157  * in the range 0 to 255 or precentage values in the range 0% to 100%, and
158  * a is a floating point value in the range 0 to 1.
159  *
160  * Returns: %TRUE if the parsing succeeded
161  *
162  * Since: 3.0
163  */
164 gboolean
165 gdk_rgba_parse (GdkRGBA     *rgba,
166                 const gchar *spec)
167 {
168   gboolean has_alpha;
169   gdouble r, g, b, a;
170   gchar *str = (gchar *) spec;
171
172   if (strncmp (str, "rgba", 4) == 0)
173     {
174       has_alpha = TRUE;
175       str += 4;
176     }
177   else if (strncmp (str, "rgb", 3) == 0)
178     {
179       has_alpha = FALSE;
180       a = 1;
181       str += 3;
182     }
183   else
184     {
185       PangoColor pango_color;
186
187       /* Resort on PangoColor for rgb.txt color
188        * map and '#' prefixed colors
189        */
190       if (pango_color_parse (&pango_color, str))
191         {
192           if (rgba)
193             {
194               rgba->red = pango_color.red / 65535.;
195               rgba->green = pango_color.green / 65535.;
196               rgba->blue = pango_color.blue / 65535.;
197               rgba->alpha = 1;
198             }
199
200           return TRUE;
201         }
202       else
203         return FALSE;
204     }
205
206   SKIP_WHITESPACES (str);
207
208   if (*str != '(')
209     return FALSE;
210
211   str++;
212
213   /* Parse red */
214   SKIP_WHITESPACES (str);
215   r = parse_rgb_value (str, &str);
216   SKIP_WHITESPACES (str);
217
218   if (*str != ',')
219     return FALSE;
220
221   str++;
222
223   /* Parse green */
224   SKIP_WHITESPACES (str);
225   g = parse_rgb_value (str, &str);
226   SKIP_WHITESPACES (str);
227
228   if (*str != ',')
229     return FALSE;
230
231   str++;
232
233   /* Parse blue */
234   SKIP_WHITESPACES (str);
235   b = parse_rgb_value (str, &str);
236   SKIP_WHITESPACES (str);
237
238   if (has_alpha)
239     {
240       if (*str != ',')
241         return FALSE;
242
243       str++;
244
245       SKIP_WHITESPACES (str);
246       a = g_ascii_strtod (str, &str);
247       SKIP_WHITESPACES (str);
248     }
249
250   if (*str != ')')
251     return FALSE;
252
253   if (rgba)
254     {
255       rgba->red = CLAMP (r, 0, 1);
256       rgba->green = CLAMP (g, 0, 1);
257       rgba->blue = CLAMP (b, 0, 1);
258       rgba->alpha = CLAMP (a, 0, 1);
259     }
260
261   return TRUE;
262 }
263
264 #undef SKIP_WHITESPACES
265
266 /**
267  * gdk_rgba_hash:
268  * @p: (type GdkRGBA): a #GdkRGBA pointer
269  *
270  * A hash function suitable for using for a hash
271  * table that stores #GdkRGBAs.
272  *
273  * Return value: The hash value for @p
274  *
275  * Since: 3.0
276  */
277 guint
278 gdk_rgba_hash (gconstpointer p)
279 {
280   const GdkRGBA *rgba = p;
281
282   return ((guint) (rgba->red * 65535) +
283           ((guint) (rgba->green * 65535) << 11) +
284           ((guint) (rgba->blue * 65535) << 22) +
285           ((guint) (rgba->alpha * 65535) >> 6));
286 }
287
288 /**
289  * gdk_rgba_equal:
290  * @p1: (type GdkRGBA): a #GdkRGBA pointer
291  * @p2: (type GdkRGBA): another #GdkRGBA pointer
292  *
293  * Compares two RGBA colors.
294  *
295  * Return value: %TRUE if the two colors compare equal
296  *
297  * Since: 3.0
298  */
299 gboolean
300 gdk_rgba_equal (gconstpointer p1,
301                 gconstpointer p2)
302 {
303   const GdkRGBA *rgba1, *rgba2;
304
305   rgba1 = p1;
306   rgba2 = p2;
307
308   if (rgba1->red == rgba2->red &&
309       rgba1->green == rgba2->green &&
310       rgba1->blue == rgba2->blue &&
311       rgba1->alpha == rgba2->alpha)
312     return TRUE;
313
314   return FALSE;
315 }
316
317 /**
318  * gdk_rgba_to_string:
319  * @rgba: a #GdkRGBA
320  *
321  * Returns a textual specification of @rgba in the form
322  * <literal>rgb (r, g, b)</literal> or
323  * <literal>rgba (r, g, b, a)</literal>,
324  * where 'r', 'g', 'b' and 'a' represent the red, green,
325  * blue and alpha values respectively. r, g, and b are
326  * represented as integers in the range 0 to 255, and a
327  * is represented as floating point value in the range 0 to 1.
328  *
329  * These string forms are string forms those supported by
330  * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
331  *
332  * Note that this string representation may loose some
333  * precision, since r, g and b are represented as 8-bit
334  * integers. If this is a concern, you should use a
335  * different representation.
336  *
337  * Returns: A newly allocated text string
338  *
339  * Since: 3.0
340  */
341 gchar *
342 gdk_rgba_to_string (const GdkRGBA *rgba)
343 {
344   if (rgba->alpha > 0.999)
345     {
346       return g_strdup_printf ("rgb(%d,%d,%d)",
347                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
348                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
349                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
350     }
351   else
352     {
353       gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
354
355       g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
356
357       return g_strdup_printf ("rgba(%d,%d,%d,%s)",
358                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
359                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
360                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
361                               alpha);
362     }
363 }