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 * #GdkRGBA 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
46G_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 * A #GdkRGBA 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.
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 */
73GdkRGBA *
74gdk_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 created with gdk_rgba_copy()
84 *
85 * Since: 3.0
86 */
87void
88gdk_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 */
106static gboolean
107parse_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 to fill in
138 * @spec: the string specifying the color
139 *
140 * Parses a textual representation of a color, filling in
141 * the @red, @green, @blue and @alpha fields of the @rgba #GdkRGBA.
142 *
143 * The string can be either one of:
144 * - A standard name (Taken from the X11 rgb.txt file).
145 * - A hexadecimal value in the form “\#rgb”, “\#rrggbb”,
146 * “\#rrrgggbbb” or ”\#rrrrggggbbbb”
147 * - A RGB color in the form “rgb(r,g,b)” (In this case the color will
148 * have full opacity)
149 * - A RGBA color in the form “rgba(r,g,b,a)”
150 *
151 * Where “r”, “g”, “b” and “a” are respectively the red, green, blue and
152 * alpha color values. In the last two cases, r g and b are either integers
153 * in the range 0 to 255 or precentage values in the range 0% to 100%, and
154 * a is a floating point value in the range 0 to 1.
155 *
156 * Returns: %TRUE if the parsing succeeded
157 *
158 * Since: 3.0
159 */
160gboolean
161gdk_rgba_parse (GdkRGBA *rgba,
162 const gchar *spec)
163{
164 gboolean has_alpha;
165 gdouble r, g, b, a;
166 gchar *str = (gchar *) spec;
167 gchar *p;
168
169 g_return_val_if_fail (spec != NULL, FALSE);
170
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 if (!parse_rgb_value (str, &str, &r))
216 return FALSE;
217 SKIP_WHITESPACES (str);
218
219 if (*str != ',')
220 return FALSE;
221
222 str++;
223
224 /* Parse green */
225 SKIP_WHITESPACES (str);
226 if (!parse_rgb_value (str, &str, &g))
227 return FALSE;
228 SKIP_WHITESPACES (str);
229
230 if (*str != ',')
231 return FALSE;
232
233 str++;
234
235 /* Parse blue */
236 SKIP_WHITESPACES (str);
237 if (!parse_rgb_value (str, &str, &b))
238 return FALSE;
239 SKIP_WHITESPACES (str);
240
241 if (has_alpha)
242 {
243 if (*str != ',')
244 return FALSE;
245
246 str++;
247
248 SKIP_WHITESPACES (str);
249 a = g_ascii_strtod (str, &p);
250 if (errno == ERANGE || p == str ||
251 isinf (a) || isnan (a))
252 return FALSE;
253 str = p;
254 SKIP_WHITESPACES (str);
255 }
256
257 if (*str != ')')
258 return FALSE;
259
260 str++;
261
262 SKIP_WHITESPACES (str);
263
264 if (*str != '\0')
265 return FALSE;
266
267 if (rgba)
268 {
269 rgba->red = CLAMP (r, 0, 1);
270 rgba->green = CLAMP (g, 0, 1);
271 rgba->blue = CLAMP (b, 0, 1);
272 rgba->alpha = CLAMP (a, 0, 1);
273 }
274
275 return TRUE;
276}
277
278#undef SKIP_WHITESPACES
279
280/**
281 * gdk_rgba_hash:
282 * @p: (type GdkRGBA): a #GdkRGBA pointer
283 *
284 * A hash function suitable for using for a hash
285 * table that stores #GdkRGBAs.
286 *
287 * Returns: The hash value for @p
288 *
289 * Since: 3.0
290 */
291guint
292gdk_rgba_hash (gconstpointer p)
293{
294 const GdkRGBA *rgba = p;
295
296 return ((guint) (rgba->red * 65535) +
297 ((guint) (rgba->green * 65535) << 11) +
298 ((guint) (rgba->blue * 65535) << 22) +
299 ((guint) (rgba->alpha * 65535) >> 6));
300}
301
302/**
303 * gdk_rgba_equal:
304 * @p1: (type GdkRGBA): a #GdkRGBA pointer
305 * @p2: (type GdkRGBA): another #GdkRGBA pointer
306 *
307 * Compares two RGBA colors.
308 *
309 * Returns: %TRUE if the two colors compare equal
310 *
311 * Since: 3.0
312 */
313gboolean
314gdk_rgba_equal (gconstpointer p1,
315 gconstpointer p2)
316{
317 const GdkRGBA *rgba1, *rgba2;
318
319 rgba1 = p1;
320 rgba2 = p2;
321
322 if (rgba1->red == rgba2->red &&
323 rgba1->green == rgba2->green &&
324 rgba1->blue == rgba2->blue &&
325 rgba1->alpha == rgba2->alpha)
326 return TRUE;
327
328 return FALSE;
329}
330
331/**
332 * gdk_rgba_to_string:
333 * @rgba: a #GdkRGBA
334 *
335 * Returns a textual specification of @rgba in the form
336 * `rgb (r, g, b)` or
337 * `rgba (r, g, b, a)`,
338 * where “r”, “g”, “b” and “a” represent the red, green,
339 * blue and alpha values respectively. r, g, and b are
340 * represented as integers in the range 0 to 255, and a
341 * is represented as floating point value in the range 0 to 1.
342 *
343 * These string forms are string forms those supported by
344 * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
345 *
346 * Note that this string representation may lose some
347 * precision, since r, g and b are represented as 8-bit
348 * integers. If this is a concern, you should use a
349 * different representation.
350 *
351 * Returns: A newly allocated text string
352 *
353 * Since: 3.0
354 */
355gchar *
356gdk_rgba_to_string (const GdkRGBA *rgba)
357{
358 if (rgba->alpha > 0.999)
359 {
360 return g_strdup_printf ("rgb(%d,%d,%d)",
361 (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
362 (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
363 (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
364 }
365 else
366 {
367 gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
368
369 g_ascii_formatd (alpha, G_ASCII_DTOSTR_BUF_SIZE, "%g", CLAMP (rgba->alpha, 0, 1));
370
371 return g_strdup_printf ("rgba(%d,%d,%d,%s)",
372 (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
373 (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
374 (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
375 alpha);
376 }
377}
378