]> Pileus Git - ~andy/gtk/blob - gdk/gdkrgba.c
Deprecate all the public API that is using GdkColor struct
[~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   GdkRGBA *copy;
75
76   copy = g_slice_new (GdkRGBA);
77   copy->red = rgba->red;
78   copy->green = rgba->green;
79   copy->blue = rgba->blue;
80   copy->alpha = rgba->alpha;
81
82   return copy;
83 }
84
85 /**
86  * gdk_rgba_free:
87  * @rgba: a #GdkRGBA
88  *
89  * Frees a #GdkRGBA struct created with gdk_rgba_copy()
90  *
91  * Since: 3.0
92  */
93 void
94 gdk_rgba_free (GdkRGBA *rgba)
95 {
96   g_slice_free (GdkRGBA, rgba);
97 }
98
99 #define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
100
101 /* Parses a single color component from a rgb() or rgba() specification
102  * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
103  * in what we accept as follows:
104  *
105  *  - For non-percentage values, we accept floats in the range 0-255
106  *    not just [0-9]+ integers
107  *  - For percentage values we accept any float, not just
108  *     [ 0-9]+ | [0-9]* '.' [0-9]+
109  *  - We accept mixed percentages and non-percentages in a single
110  *    rgb() or rgba() specification.
111  */
112 static double
113 parse_rgb_value (const char  *str,
114                  char       **endp)
115 {
116   double number;
117   const char *p;
118
119   number = g_ascii_strtod (str, endp);
120
121   p = *endp;
122
123   SKIP_WHITESPACES (p);
124
125   if (*p == '%')
126     {
127       *endp = (char *)(p + 1);
128       return CLAMP(number / 100., 0., 1.);
129     }
130   else
131     {
132       return CLAMP(number / 255., 0., 1.);
133     }
134 }
135
136 /**
137  * gdk_rgba_parse:
138  * @rgba: the #GdkRGBA struct to fill in
139  * @spec: the string specifying the color
140  *
141  * Parses a textual representation of a color, filling in
142  * the <structfield>red</structfield>, <structfield>green</structfield>,
143  * <structfield>blue</structfield> and <structfield>alpha</structfield>
144  * fields of the @rgba struct.
145  *
146  * The string can be either one of:
147  * <itemizedlist>
148  * <listitem>
149  * A standard name (Taken from the X11 rgb.txt file).
150  * </listitem>
151  * <listitem>
152  * A hex value in the form '#rgb' '#rrggbb' '#rrrgggbbb' or '#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
180   if (strncmp (str, "rgba", 4) == 0)
181     {
182       has_alpha = TRUE;
183       str += 4;
184     }
185   else if (strncmp (str, "rgb", 3) == 0)
186     {
187       has_alpha = FALSE;
188       a = 1;
189       str += 3;
190     }
191   else
192     {
193       PangoColor pango_color;
194
195       /* Resort on PangoColor for rgb.txt color
196        * map and '#' prefixed colors
197        */
198       if (pango_color_parse (&pango_color, str))
199         {
200           if (rgba)
201             {
202               rgba->red = pango_color.red / 65535.;
203               rgba->green = pango_color.green / 65535.;
204               rgba->blue = pango_color.blue / 65535.;
205               rgba->alpha = 1;
206             }
207
208           return TRUE;
209         }
210       else
211         return FALSE;
212     }
213
214   SKIP_WHITESPACES (str);
215
216   if (*str != '(')
217     return FALSE;
218
219   str++;
220
221   /* Parse red */
222   SKIP_WHITESPACES (str);
223   r = parse_rgb_value (str, &str);
224   SKIP_WHITESPACES (str);
225
226   if (*str != ',')
227     return FALSE;
228
229   str++;
230
231   /* Parse green */
232   SKIP_WHITESPACES (str);
233   g = parse_rgb_value (str, &str);
234   SKIP_WHITESPACES (str);
235
236   if (*str != ',')
237     return FALSE;
238
239   str++;
240
241   /* Parse blue */
242   SKIP_WHITESPACES (str);
243   b = parse_rgb_value (str, &str);
244   SKIP_WHITESPACES (str);
245
246   if (has_alpha)
247     {
248       if (*str != ',')
249         return FALSE;
250
251       str++;
252
253       SKIP_WHITESPACES (str);
254       a = g_ascii_strtod (str, &str);
255       SKIP_WHITESPACES (str);
256     }
257
258   if (*str != ')')
259     return FALSE;
260
261   if (rgba)
262     {
263       rgba->red = CLAMP (r, 0, 1);
264       rgba->green = CLAMP (g, 0, 1);
265       rgba->blue = CLAMP (b, 0, 1);
266       rgba->alpha = CLAMP (a, 0, 1);
267     }
268
269   return TRUE;
270 }
271
272 #undef SKIP_WHITESPACES
273
274 /**
275  * gdk_rgba_hash:
276  * @p: (type GdkRGBA): a #GdkRGBA pointer
277  *
278  * A hash function suitable for using for a hash
279  * table that stores #GdkRGBAs.
280  *
281  * Return value: The hash value for @p
282  *
283  * Since: 3.0
284  */
285 guint
286 gdk_rgba_hash (gconstpointer p)
287 {
288   const GdkRGBA *rgba = p;
289
290   return ((guint) (rgba->red * 65535) +
291           ((guint) (rgba->green * 65535) << 11) +
292           ((guint) (rgba->blue * 65535) << 22) +
293           ((guint) (rgba->alpha * 65535) >> 6));
294 }
295
296 /**
297  * gdk_rgba_equal:
298  * @p1: (type GdkRGBA): a #GdkRGBA pointer
299  * @p2: (type GdkRGBA): another #GdkRGBA pointer
300  *
301  * Compares two RGBA colors.
302  *
303  * Return value: %TRUE if the two colors compare equal
304  *
305  * Since: 3.0
306  */
307 gboolean
308 gdk_rgba_equal (gconstpointer p1,
309                 gconstpointer p2)
310 {
311   const GdkRGBA *rgba1, *rgba2;
312
313   rgba1 = p1;
314   rgba2 = p2;
315
316   if (rgba1->red == rgba2->red &&
317       rgba1->green == rgba2->green &&
318       rgba1->blue == rgba2->blue &&
319       rgba1->alpha == rgba2->alpha)
320     return TRUE;
321
322   return FALSE;
323 }
324
325 /**
326  * gdk_rgba_to_string:
327  * @rgba: a #GdkRGBA
328  *
329  * Returns a textual specification of @rgba in the form
330  * <literal>rgb (r, g, b)</literal> or
331  * <literal>rgba (r, g, b, a)</literal>,
332  * where 'r', 'g', 'b' and 'a' represent the red, green,
333  * blue and alpha values respectively. r, g, and b are
334  * represented as integers in the range 0 to 255, and a
335  * is represented as floating point value in the range 0 to 1.
336  *
337  * These string forms are string forms those supported by
338  * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
339  *
340  * Note that this string representation may loose some
341  * precision, since r, g and b are represented as 8-bit
342  * integers. If this is a concern, you should use a
343  * different representation.
344  *
345  * Returns: A newly allocated text string
346  *
347  * Since: 3.0
348  */
349 gchar *
350 gdk_rgba_to_string (const GdkRGBA *rgba)
351 {
352   if (rgba->alpha > 0.999)
353     {
354       return g_strdup_printf ("rgb(%d,%d,%d)",
355                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
356                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
357                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
358     }
359   else
360     {
361       gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
362
363       g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
364
365       return g_strdup_printf ("rgba(%d,%d,%d,%s)",
366                               (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
367                               (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
368                               (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
369                               alpha);
370     }
371 }