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 | |
27 | #include "gdkrgbaprivate.h" |
28 | |
29 | #include <string.h> |
30 | #include <errno.h> |
31 | #include <math.h> |
32 | |
33 | #include "gdkhslaprivate.h" |
34 | |
35 | G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba, |
36 | gdk_rgba_copy, gdk_rgba_free) |
37 | |
38 | /** |
39 | * GdkRGBA: |
40 | * @red: The intensity of the red channel from 0.0 to 1.0 inclusive |
41 | * @green: The intensity of the green channel from 0.0 to 1.0 inclusive |
42 | * @blue: The intensity of the blue channel from 0.0 to 1.0 inclusive |
43 | * @alpha: The opacity of the color from 0.0 for completely translucent to |
44 | * 1.0 for opaque |
45 | * |
46 | * A `GdkRGBA` is used to represent a color, in a way that is compatible |
47 | * with cairo’s notion of color. |
48 | * |
49 | * `GdkRGBA` is a convenient way to pass colors around. It’s based on |
50 | * cairo’s way to deal with colors and mirrors its behavior. All values |
51 | * are in the range from 0.0 to 1.0 inclusive. So the color |
52 | * (0.0, 0.0, 0.0, 0.0) represents transparent black and |
53 | * (1.0, 1.0, 1.0, 1.0) is opaque white. Other values will |
54 | * be clamped to this range when drawing. |
55 | */ |
56 | |
57 | /** |
58 | * gdk_rgba_copy: |
59 | * @rgba: a `GdkRGBA` |
60 | * |
61 | * Makes a copy of a `GdkRGBA`. |
62 | * |
63 | * The result must be freed through [method@Gdk.RGBA.free]. |
64 | * |
65 | * Returns: A newly allocated `GdkRGBA`, with the same contents as @rgba |
66 | */ |
67 | GdkRGBA * |
68 | gdk_rgba_copy (const GdkRGBA *rgba) |
69 | { |
70 | return g_slice_dup (GdkRGBA, rgba); |
71 | } |
72 | |
73 | /** |
74 | * gdk_rgba_free: |
75 | * @rgba: a `GdkRGBA` |
76 | * |
77 | * Frees a `GdkRGBA`. |
78 | */ |
79 | void |
80 | gdk_rgba_free (GdkRGBA *rgba) |
81 | { |
82 | g_slice_free (GdkRGBA, rgba); |
83 | } |
84 | |
85 | /** |
86 | * gdk_rgba_is_clear: |
87 | * @rgba: a `GdkRGBA` |
88 | * |
89 | * Checks if an @rgba value is transparent. |
90 | * |
91 | * That is, drawing with the value would not produce any change. |
92 | * |
93 | * Returns: %TRUE if the @rgba is clear |
94 | */ |
95 | gboolean |
96 | gdk_rgba_is_clear (const GdkRGBA *rgba) |
97 | { |
98 | return rgba->alpha < ((float) 0x00ff / (float) 0xffff); |
99 | } |
100 | |
101 | /** |
102 | * gdk_rgba_is_opaque: |
103 | * @rgba: a `GdkRGBA` |
104 | * |
105 | * Checks if an @rgba value is opaque. |
106 | * |
107 | * That is, drawing with the value will not retain any results |
108 | * from previous contents. |
109 | * |
110 | * Returns: %TRUE if the @rgba is opaque |
111 | */ |
112 | gboolean |
113 | gdk_rgba_is_opaque (const GdkRGBA *rgba) |
114 | { |
115 | return rgba->alpha > ((float)0xff00 / (float)0xffff); |
116 | } |
117 | |
118 | #define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++; |
119 | |
120 | /* Parses a single color component from a rgb() or rgba() specification |
121 | * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal |
122 | * in what we accept as follows: |
123 | * |
124 | * - For non-percentage values, we accept floats in the range 0-255 |
125 | * not just [0-9]+ integers |
126 | * - For percentage values we accept any float, not just [ 0-9]+ | [0-9]* “.” [0-9]+ |
127 | * - We accept mixed percentages and non-percentages in a single |
128 | * rgb() or rgba() specification. |
129 | */ |
130 | static gboolean |
131 | parse_rgb_value (const char *str, |
132 | char **endp, |
133 | double *number) |
134 | { |
135 | const char *p; |
136 | |
137 | *number = g_ascii_strtod (nptr: str, endptr: endp); |
138 | if (errno == ERANGE || *endp == str || |
139 | isinf (*number) || isnan (*number)) |
140 | return FALSE; |
141 | |
142 | p = *endp; |
143 | |
144 | SKIP_WHITESPACES (p); |
145 | |
146 | if (*p == '%') |
147 | { |
148 | *endp = (char *)(p + 1); |
149 | *number = CLAMP(*number / 100., 0., 1.); |
150 | } |
151 | else |
152 | { |
153 | *number = CLAMP(*number / 255., 0., 1.); |
154 | } |
155 | |
156 | return TRUE; |
157 | } |
158 | |
159 | /** |
160 | * gdk_rgba_parse: |
161 | * @rgba: the `GdkRGBA` to fill in |
162 | * @spec: the string specifying the color |
163 | * |
164 | * Parses a textual representation of a color. |
165 | * |
166 | * The string can be either one of: |
167 | * |
168 | * - A standard name (Taken from the Css specification). |
169 | * - A hexadecimal value in the form “\#rgb”, “\#rrggbb”, |
170 | * “\#rrrgggbbb” or ”\#rrrrggggbbbb” |
171 | * - A hexadecimal value in the form “\#rgba”, “\#rrggbbaa”, |
172 | * or ”\#rrrrggggbbbbaaaa” |
173 | * - A RGB color in the form “rgb(r,g,b)” (In this case the color |
174 | * will have full opacity) |
175 | * - A RGBA color in the form “rgba(r,g,b,a)” |
176 | * |
177 | * Where “r”, “g”, “b” and “a” are respectively the red, green, |
178 | * blue and alpha color values. In the last two cases, “r”, “g”, |
179 | * and “b” are either integers in the range 0 to 255 or percentage |
180 | * values in the range 0% to 100%, and a is a floating point value |
181 | * in the range 0 to 1. |
182 | * |
183 | * Returns: %TRUE if the parsing succeeded |
184 | */ |
185 | gboolean |
186 | gdk_rgba_parse (GdkRGBA *rgba, |
187 | const char *spec) |
188 | { |
189 | gboolean has_alpha; |
190 | gboolean is_hsl; |
191 | double r, g, b, a; |
192 | char *str = (char *) spec; |
193 | char *p; |
194 | |
195 | g_return_val_if_fail (spec != NULL, FALSE); |
196 | |
197 | |
198 | if (strncmp (s1: str, s2: "rgba" , n: 4) == 0) |
199 | { |
200 | has_alpha = TRUE; |
201 | is_hsl = FALSE; |
202 | str += 4; |
203 | } |
204 | else if (strncmp (s1: str, s2: "rgb" , n: 3) == 0) |
205 | { |
206 | has_alpha = FALSE; |
207 | is_hsl = FALSE; |
208 | a = 1; |
209 | str += 3; |
210 | } |
211 | else if (strncmp (s1: str, s2: "hsla" , n: 4) == 0) |
212 | { |
213 | has_alpha = TRUE; |
214 | is_hsl = TRUE; |
215 | str += 4; |
216 | } |
217 | else if (strncmp (s1: str, s2: "hsl" , n: 3) == 0) |
218 | { |
219 | has_alpha = FALSE; |
220 | is_hsl = TRUE; |
221 | a = 1; |
222 | str += 3; |
223 | } |
224 | else |
225 | { |
226 | PangoColor pango_color; |
227 | guint16 alpha; |
228 | |
229 | /* Resort on PangoColor for rgb.txt color |
230 | * map and '#' prefixed colors |
231 | */ |
232 | if (pango_color_parse_with_alpha (color: &pango_color, alpha: &alpha, spec: str)) |
233 | { |
234 | if (rgba) |
235 | { |
236 | rgba->red = pango_color.red / 65535.; |
237 | rgba->green = pango_color.green / 65535.; |
238 | rgba->blue = pango_color.blue / 65535.; |
239 | rgba->alpha = alpha / 65535.; |
240 | } |
241 | |
242 | return TRUE; |
243 | } |
244 | else |
245 | return FALSE; |
246 | } |
247 | |
248 | SKIP_WHITESPACES (str); |
249 | |
250 | if (*str != '(') |
251 | return FALSE; |
252 | |
253 | str++; |
254 | |
255 | /* Parse red */ |
256 | SKIP_WHITESPACES (str); |
257 | if (!parse_rgb_value (str, endp: &str, number: &r)) |
258 | return FALSE; |
259 | SKIP_WHITESPACES (str); |
260 | |
261 | if (*str != ',') |
262 | return FALSE; |
263 | |
264 | str++; |
265 | |
266 | /* Parse green */ |
267 | SKIP_WHITESPACES (str); |
268 | if (!parse_rgb_value (str, endp: &str, number: &g)) |
269 | return FALSE; |
270 | SKIP_WHITESPACES (str); |
271 | |
272 | if (*str != ',') |
273 | return FALSE; |
274 | |
275 | str++; |
276 | |
277 | /* Parse blue */ |
278 | SKIP_WHITESPACES (str); |
279 | if (!parse_rgb_value (str, endp: &str, number: &b)) |
280 | return FALSE; |
281 | SKIP_WHITESPACES (str); |
282 | |
283 | if (has_alpha) |
284 | { |
285 | if (*str != ',') |
286 | return FALSE; |
287 | |
288 | str++; |
289 | |
290 | SKIP_WHITESPACES (str); |
291 | a = g_ascii_strtod (nptr: str, endptr: &p); |
292 | if (errno == ERANGE || p == str || |
293 | isinf (a) || isnan (a)) |
294 | return FALSE; |
295 | str = p; |
296 | SKIP_WHITESPACES (str); |
297 | } |
298 | |
299 | if (*str != ')') |
300 | return FALSE; |
301 | |
302 | str++; |
303 | |
304 | SKIP_WHITESPACES (str); |
305 | |
306 | if (*str != '\0') |
307 | return FALSE; |
308 | |
309 | if (rgba) |
310 | { |
311 | if (is_hsl) |
312 | { |
313 | GdkHSLA hsla; |
314 | hsla.hue = r * 255; |
315 | hsla.saturation = CLAMP (g, 0, 1); |
316 | hsla.lightness = CLAMP (b, 0, 1); |
317 | hsla.alpha = CLAMP (a, 0, 1); |
318 | _gdk_rgba_init_from_hsla (rgba, hsla: &hsla); |
319 | } |
320 | else |
321 | { |
322 | rgba->red = CLAMP (r, 0, 1); |
323 | rgba->green = CLAMP (g, 0, 1); |
324 | rgba->blue = CLAMP (b, 0, 1); |
325 | rgba->alpha = CLAMP (a, 0, 1); |
326 | } |
327 | } |
328 | |
329 | return TRUE; |
330 | } |
331 | |
332 | #undef SKIP_WHITESPACES |
333 | |
334 | /** |
335 | * gdk_rgba_hash: |
336 | * @p: (type GdkRGBA): a `GdkRGBA` |
337 | * |
338 | * A hash function suitable for using for a hash |
339 | * table that stores `GdkRGBA`s. |
340 | * |
341 | * Returns: The hash value for @p |
342 | */ |
343 | guint |
344 | gdk_rgba_hash (gconstpointer p) |
345 | { |
346 | const GdkRGBA *rgba = p; |
347 | |
348 | return ((guint) (rgba->red * 65535) + |
349 | ((guint) (rgba->green * 65535) << 11) + |
350 | ((guint) (rgba->blue * 65535) << 22) + |
351 | ((guint) (rgba->alpha * 65535) >> 6)); |
352 | } |
353 | |
354 | /** |
355 | * gdk_rgba_equal: |
356 | * @p1: (type GdkRGBA): a `GdkRGBA` |
357 | * @p2: (type GdkRGBA): another `GdkRGBA` |
358 | * |
359 | * Compares two `GdkRGBA` colors. |
360 | * |
361 | * Returns: %TRUE if the two colors compare equal |
362 | */ |
363 | gboolean |
364 | gdk_rgba_equal (gconstpointer p1, |
365 | gconstpointer p2) |
366 | { |
367 | const GdkRGBA *rgba1, *rgba2; |
368 | |
369 | rgba1 = p1; |
370 | rgba2 = p2; |
371 | |
372 | if (rgba1->red == rgba2->red && |
373 | rgba1->green == rgba2->green && |
374 | rgba1->blue == rgba2->blue && |
375 | rgba1->alpha == rgba2->alpha) |
376 | return TRUE; |
377 | |
378 | return FALSE; |
379 | } |
380 | |
381 | /** |
382 | * gdk_rgba_to_string: |
383 | * @rgba: a `GdkRGBA` |
384 | * |
385 | * Returns a textual specification of @rgba in the form |
386 | * `rgb(r,g,b)` or `rgba(r,g,b,a)`, where “r”, “g”, “b” and |
387 | * “a” represent the red, green, blue and alpha values |
388 | * respectively. “r”, “g”, and “b” are represented as integers |
389 | * in the range 0 to 255, and “a” is represented as a floating |
390 | * point value in the range 0 to 1. |
391 | * |
392 | * These string forms are string forms that are supported by |
393 | * the CSS3 colors module, and can be parsed by [method@Gdk.RGBA.parse]. |
394 | * |
395 | * Note that this string representation may lose some precision, |
396 | * since “r”, “g” and “b” are represented as 8-bit integers. If |
397 | * this is a concern, you should use a different representation. |
398 | * |
399 | * Returns: A newly allocated text string |
400 | */ |
401 | char * |
402 | gdk_rgba_to_string (const GdkRGBA *rgba) |
403 | { |
404 | if (rgba->alpha > 0.999) |
405 | { |
406 | return g_strdup_printf (format: "rgb(%d,%d,%d)" , |
407 | (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.), |
408 | (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.), |
409 | (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.)); |
410 | } |
411 | else |
412 | { |
413 | char alpha[G_ASCII_DTOSTR_BUF_SIZE]; |
414 | |
415 | g_ascii_formatd (buffer: alpha, G_ASCII_DTOSTR_BUF_SIZE, format: "%g" , CLAMP (rgba->alpha, 0, 1)); |
416 | |
417 | return g_strdup_printf (format: "rgba(%d,%d,%d,%s)" , |
418 | (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.), |
419 | (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.), |
420 | (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.), |
421 | alpha); |
422 | } |
423 | } |
424 | |
425 | static gboolean |
426 | parse_color_channel_value (GtkCssParser *parser, |
427 | float *value, |
428 | gboolean is_percentage) |
429 | { |
430 | double dvalue; |
431 | |
432 | if (is_percentage) |
433 | { |
434 | if (!gtk_css_parser_consume_percentage (self: parser, number: &dvalue)) |
435 | return FALSE; |
436 | |
437 | *value = CLAMP (dvalue, 0.0, 100.0) / 100.0; |
438 | return TRUE; |
439 | } |
440 | else |
441 | { |
442 | if (!gtk_css_parser_consume_number (self: parser, number: &dvalue)) |
443 | return FALSE; |
444 | |
445 | *value = CLAMP (dvalue, 0.0, 255.0) / 255.0; |
446 | return TRUE; |
447 | } |
448 | } |
449 | |
450 | static guint |
451 | parse_color_channel (GtkCssParser *parser, |
452 | guint arg, |
453 | gpointer data) |
454 | { |
455 | GdkRGBA *rgba = data; |
456 | double dvalue; |
457 | |
458 | switch (arg) |
459 | { |
460 | case 0: |
461 | /* We abuse rgba->alpha to store if we use percentages or numbers */ |
462 | if (gtk_css_token_is (gtk_css_parser_get_token (parser), GTK_CSS_TOKEN_PERCENTAGE)) |
463 | rgba->alpha = 1.0; |
464 | else |
465 | rgba->alpha = 0.0; |
466 | |
467 | if (!parse_color_channel_value (parser, value: &rgba->red, is_percentage: rgba->alpha != 0.0)) |
468 | return 0; |
469 | return 1; |
470 | |
471 | case 1: |
472 | if (!parse_color_channel_value (parser, value: &rgba->green, is_percentage: rgba->alpha != 0.0)) |
473 | return 0; |
474 | return 1; |
475 | |
476 | case 2: |
477 | if (!parse_color_channel_value (parser, value: &rgba->blue, is_percentage: rgba->alpha != 0.0)) |
478 | return 0; |
479 | return 1; |
480 | |
481 | case 3: |
482 | if (!gtk_css_parser_consume_number (self: parser, number: &dvalue)) |
483 | return 0; |
484 | |
485 | rgba->alpha = CLAMP (dvalue, 0.0, 1.0); |
486 | return 1; |
487 | |
488 | default: |
489 | g_assert_not_reached (); |
490 | return 0; |
491 | } |
492 | } |
493 | |
494 | static guint |
495 | parse_hsla_color_channel (GtkCssParser *parser, |
496 | guint arg, |
497 | gpointer data) |
498 | { |
499 | GdkHSLA *hsla = data; |
500 | double dvalue; |
501 | |
502 | switch (arg) |
503 | { |
504 | case 0: |
505 | if (!gtk_css_parser_consume_number (self: parser, number: &dvalue)) |
506 | return 0; |
507 | hsla->hue = dvalue; |
508 | return 1; |
509 | |
510 | case 1: |
511 | if (!gtk_css_parser_consume_percentage (self: parser, number: &dvalue)) |
512 | return 0; |
513 | hsla->saturation = CLAMP (dvalue, 0.0, 100.0) / 100.0; |
514 | return 1; |
515 | |
516 | case 2: |
517 | if (!gtk_css_parser_consume_percentage (self: parser, number: &dvalue)) |
518 | return 0; |
519 | hsla->lightness = CLAMP (dvalue, 0.0, 100.0) / 100.0; |
520 | return 1; |
521 | |
522 | case 3: |
523 | if (!gtk_css_parser_consume_number (self: parser, number: &dvalue)) |
524 | return 0; |
525 | |
526 | hsla->alpha = CLAMP (dvalue, 0.0, 1.0) / 1.0; |
527 | return 1; |
528 | |
529 | default: |
530 | g_assert_not_reached (); |
531 | return 0; |
532 | } |
533 | } |
534 | |
535 | static gboolean |
536 | rgba_init_chars (GdkRGBA *rgba, |
537 | const char s[8]) |
538 | { |
539 | guint i; |
540 | |
541 | for (i = 0; i < 8; i++) |
542 | { |
543 | if (!g_ascii_isxdigit (s[i])) |
544 | return FALSE; |
545 | } |
546 | |
547 | rgba->red = (g_ascii_xdigit_value (c: s[0]) * 16 + g_ascii_xdigit_value (c: s[1])) / 255.0; |
548 | rgba->green = (g_ascii_xdigit_value (c: s[2]) * 16 + g_ascii_xdigit_value (c: s[3])) / 255.0; |
549 | rgba->blue = (g_ascii_xdigit_value (c: s[4]) * 16 + g_ascii_xdigit_value (c: s[5])) / 255.0; |
550 | rgba->alpha = (g_ascii_xdigit_value (c: s[6]) * 16 + g_ascii_xdigit_value (c: s[7])) / 255.0; |
551 | |
552 | return TRUE; |
553 | } |
554 | |
555 | gboolean |
556 | gdk_rgba_parser_parse (GtkCssParser *parser, |
557 | GdkRGBA *rgba) |
558 | { |
559 | const GtkCssToken *token; |
560 | |
561 | token = gtk_css_parser_get_token (self: parser); |
562 | if (gtk_css_token_is_function (token, ident: "rgb" )) |
563 | { |
564 | if (!gtk_css_parser_consume_function (self: parser, min_args: 3, max_args: 3, parse_func: parse_color_channel, data: rgba)) |
565 | return FALSE; |
566 | |
567 | rgba->alpha = 1.0; |
568 | return TRUE; |
569 | } |
570 | else if (gtk_css_token_is_function (token, ident: "rgba" )) |
571 | { |
572 | return gtk_css_parser_consume_function (self: parser, min_args: 4, max_args: 4, parse_func: parse_color_channel, data: rgba); |
573 | } |
574 | else if (gtk_css_token_is_function (token, ident: "hsl" ) || gtk_css_token_is_function (token, ident: "hsla" )) |
575 | { |
576 | GdkHSLA hsla; |
577 | |
578 | hsla.alpha = 1.0; |
579 | |
580 | if (!gtk_css_parser_consume_function (self: parser, min_args: 3, max_args: 4, parse_func: parse_hsla_color_channel, data: &hsla)) |
581 | return FALSE; |
582 | |
583 | _gdk_rgba_init_from_hsla (rgba, hsla: &hsla); |
584 | return TRUE; |
585 | } |
586 | else if (gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_ID) || |
587 | gtk_css_token_is (token, GTK_CSS_TOKEN_HASH_UNRESTRICTED)) |
588 | { |
589 | const char *s = token->string.string; |
590 | |
591 | switch (strlen (s: s)) |
592 | { |
593 | case 3: |
594 | if (!rgba_init_chars (rgba, s: (char[8]) {s[0], s[0], s[1], s[1], s[2], s[2], 'F', 'F' })) |
595 | { |
596 | gtk_css_parser_error_value (self: parser, format: "Hash code is not a valid hex color." ); |
597 | return FALSE; |
598 | } |
599 | break; |
600 | |
601 | case 4: |
602 | if (!rgba_init_chars (rgba, s: (char[8]) {s[0], s[0], s[1], s[1], s[2], s[2], s[3], s[3] })) |
603 | { |
604 | gtk_css_parser_error_value (self: parser, format: "Hash code is not a valid hex color." ); |
605 | return FALSE; |
606 | } |
607 | break; |
608 | |
609 | case 6: |
610 | if (!rgba_init_chars (rgba, s: (char[8]) {s[0], s[1], s[2], s[3], s[4], s[5], 'F', 'F' })) |
611 | { |
612 | gtk_css_parser_error_value (self: parser, format: "Hash code is not a valid hex color." ); |
613 | return FALSE; |
614 | } |
615 | break; |
616 | |
617 | case 8: |
618 | if (!rgba_init_chars (rgba, s)) |
619 | { |
620 | gtk_css_parser_error_value (self: parser, format: "Hash code is not a valid hex color." ); |
621 | return FALSE; |
622 | } |
623 | break; |
624 | |
625 | default: |
626 | gtk_css_parser_error_value (self: parser, format: "Hash code is not a valid hex color." ); |
627 | return FALSE; |
628 | break; |
629 | } |
630 | |
631 | gtk_css_parser_consume_token (self: parser); |
632 | return TRUE; |
633 | } |
634 | else if (gtk_css_token_is (token, GTK_CSS_TOKEN_IDENT)) |
635 | { |
636 | if (gtk_css_token_is_ident (token, ident: "transparent" )) |
637 | { |
638 | *rgba = (GdkRGBA) { 0, 0, 0, 0 }; |
639 | } |
640 | else if (gdk_rgba_parse (rgba, spec: token->string.string)) |
641 | { |
642 | /* everything's fine */ |
643 | } |
644 | else |
645 | { |
646 | gtk_css_parser_error_syntax (self: parser, format: "\"%s\" is not a valid color name." , token->string.string); |
647 | return FALSE; |
648 | } |
649 | |
650 | gtk_css_parser_consume_token (self: parser); |
651 | return TRUE; |
652 | } |
653 | else |
654 | { |
655 | gtk_css_parser_error_syntax (self: parser, format: "Expected a valid color." ); |
656 | return FALSE; |
657 | } |
658 | } |
659 | |