diff --git a/clutter/clutter-color.c b/clutter/clutter-color.c index 7d0ab2981..2d20734be 100644 --- a/clutter/clutter-color.c +++ b/clutter/clutter-color.c @@ -441,6 +441,99 @@ clutter_color_from_pixel (ClutterColor *color, color->alpha = pixel & 0xff; } +static inline void +skip_whitespace (gchar **str) +{ + while (g_ascii_isspace (**str)) + *str += 1; +} + +static inline void +parse_rgb_value (gchar *str, + guint8 *color, + gchar **endp) +{ + gdouble number; + gchar *p; + + skip_whitespace (&str); + + number = g_ascii_strtod (str, endp); + + p = *endp; + + skip_whitespace (&p); + + if (*p == '%') + { + *endp = (gchar *) (p + 1); + + *color = CLAMP (number / 100.0, 0.0, 1.0) * 255; + } + else + *color = CLAMP (number, 0, 255); +} + +static gboolean +parse_rgba (ClutterColor *color, + gchar *str, + gboolean has_alpha) +{ + skip_whitespace (&str); + + if (*str != '(') + return FALSE; + + str += 1; + + /* red */ + parse_rgb_value (str, &color->red, &str); + skip_whitespace (&str); + if (*str != ',') + return FALSE; + + str += 1; + + /* green */ + parse_rgb_value (str, &color->green, &str); + skip_whitespace (&str); + if (*str != ',') + return FALSE; + + str += 1; + + /* blue */ + parse_rgb_value (str, &color->blue, &str); + skip_whitespace (&str); + + /* alpha (optional); since the alpha channel value can only + * be between 0 and 1 we don't use the parse_rgb_value() + * function + */ + if (has_alpha) + { + gdouble number; + + if (*str != ',') + return FALSE; + + str += 1; + + skip_whitespace (&str); + number = g_ascii_strtod (str, &str); + + color->alpha = CLAMP (number * 255.0, 0, 255); + } + else + color->alpha = 255; + + skip_whitespace (&str); + if (*str != ')') + return FALSE; + + return TRUE; +} + /** * clutter_color_from_string: * @color: (out caller-allocates): return location for a #ClutterColor @@ -453,11 +546,28 @@ clutter_color_from_pixel (ClutterColor *color, * * The @color is not allocated. * - * The color may be defined by any of the formats understood by - * pango_color_from_string(); these include literal color names, like - * Red or DarkSlateGray, or - * hexadecimal specifications like #3050b2 or - * #333. + * The format of @str can be either one of: + * + * a standard name (taken from the X11 rgb.txt + * file"); + * an hexadecimal value in the form: '#rgb', '#rrggbb', + * '#rgba' or '#rrggbbaa'; + * a RGB color in the form 'rgb(r, g, b)'; + * a RGBA color in the form 'rgba(r, g, b, + * a)'; + * + * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue and + * alpha color values. + * + * In the last two cases, the 'r', 'g', and 'b' values are either integers + * between 0 and 255, or percentage values in the range between 0% and 100%; + * the percentages require the '%' character. The 'a' value, if specified, + * can only be a floating point value between 0.0 and 1.0. + * + * Whitespace is ignored. + * + * If the alpha component is not specified then it is assumed to be set to + * be fully opaque. * * Return value: %TRUE if parsing succeeded. * @@ -472,6 +582,19 @@ clutter_color_from_string (ClutterColor *color, g_return_val_if_fail (color != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); + if (strncmp (str, "rgb", 3) == 0) + { + gchar *s = (gchar *) str; + gboolean res; + + if (strncmp (str, "rgba", 4) == 0) + res = parse_rgba (color, s + 4, TRUE); + else + res = parse_rgba (color, s + 3, FALSE); + + return res; + } + /* if the string contains a color encoded using the hexadecimal * notations (#rrggbbaa or #rgba) we attempt a rough pass at * parsing the color ourselves, as we need the alpha channel that @@ -537,7 +660,7 @@ clutter_color_from_string (ClutterColor *color, } } } - + /* Fall back to pango for named colors */ if (pango_color_parse (&pango_color, str)) { diff --git a/tests/conform/test-color.c b/tests/conform/test-color.c index 71b080736..e9681e1a6 100644 --- a/tests/conform/test-color.c +++ b/tests/conform/test-color.c @@ -78,7 +78,7 @@ test_color_from_string (TestConformSimpleFixture *fixture, { ClutterColor color; - clutter_color_from_string (&color, "#ff0000ff"); + g_assert (clutter_color_from_string (&color, "#ff0000ff")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0xff, 0, 0, 0xff }\n", @@ -92,7 +92,7 @@ test_color_from_string (TestConformSimpleFixture *fixture, g_assert (color.blue == 0); g_assert (color.alpha == 0xff); - clutter_color_from_string (&color, "#0f0f"); + g_assert (clutter_color_from_string (&color, "#0f0f")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0xff, 0, 0xff }\n", @@ -106,7 +106,7 @@ test_color_from_string (TestConformSimpleFixture *fixture, g_assert (color.blue == 0); g_assert (color.alpha == 0xff); - clutter_color_from_string (&color, "#0000ff"); + g_assert (clutter_color_from_string (&color, "#0000ff")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0, 0xff, 0xff }\n", @@ -120,7 +120,7 @@ test_color_from_string (TestConformSimpleFixture *fixture, g_assert (color.blue == 0xff); g_assert (color.alpha == 0xff); - clutter_color_from_string (&color, "#abc"); + g_assert (clutter_color_from_string (&color, "#abc")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0xaa, 0xbb, 0xcc, 0xff }\n", @@ -134,7 +134,7 @@ test_color_from_string (TestConformSimpleFixture *fixture, g_assert (color.blue == 0xcc); g_assert (color.alpha == 0xff); - clutter_color_from_string (&color, "#123abc"); + g_assert (clutter_color_from_string (&color, "#123abc")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0x12, 0x3a, 0xbc, 0xff }\n", @@ -147,6 +147,50 @@ test_color_from_string (TestConformSimpleFixture *fixture, g_assert (color.green == 0x3a); g_assert (color.blue == 0xbc); g_assert (color.alpha == 0xff); + + g_assert (clutter_color_from_string (&color, "rgb(255, 128, 64)")); + if (g_test_verbose ()) + { + g_print ("color = { %x, %x, %x, %x }, expected = { 255, 128, 64, 255 }\n", + color.red, + color.green, + color.blue, + color.alpha); + } + g_assert_cmpint (color.red, ==, 255); + g_assert_cmpint (color.green, ==, 128); + g_assert_cmpint (color.blue, ==, 64); + g_assert_cmpint (color.alpha, ==, 255); + + g_assert (clutter_color_from_string (&color, "rgba ( 30%, 0, 25%, 0.5 ) ")); + if (g_test_verbose ()) + { + g_print ("color = { %x, %x, %x, %x }, expected = { %.1f, 0, %.1f, 128 }\n", + color.red, + color.green, + color.blue, + color.alpha, + CLAMP (255.0 / 100.0 * 30.0, 0, 255), + CLAMP (255.0 / 100.0 * 25.0, 0, 255)); + } + g_assert_cmpint (color.red, ==, (255.0 / 100.0 * 30.0)); + g_assert_cmpint (color.green, ==, 0); + g_assert_cmpint (color.blue, ==, (255.0 / 100.0 * 25.0)); + g_assert_cmpint (color.alpha, ==, 127); + + g_assert (clutter_color_from_string (&color, "rgb( 50%, -50%, 150% )")); + if (g_test_verbose ()) + { + g_print ("color = { %x, %x, %x, %x }, expected = { 127, 0, 255, 255 }\n", + color.red, + color.green, + color.blue, + color.alpha); + } + g_assert_cmpint (color.red, ==, 127); + g_assert_cmpint (color.green, ==, 0); + g_assert_cmpint (color.blue, ==, 255); + g_assert_cmpint (color.alpha, ==, 255); } void