color: Support CSS color definitions

The CSS Color Module 3, available at:

  http://www.w3.org/TR/css3-color/

allows defining colors as:

  rgb ( r, g, b )
  rgba ( r, g, b, a)

along with the usual hexadecimal and named notations.

The r, g, and b channels can be:

  • integers between 0 and 255
  • percentages, between 0% and 100%

The alpha channel, if included using the rgba() modifier, can be a
floating point value between 0.0 and 1.0.

The ClutterColor parser should support this notation.
This commit is contained in:
Emmanuele Bassi 2010-11-22 14:22:56 +00:00
parent ee0b8c439e
commit ab6da347f6
2 changed files with 178 additions and 11 deletions

View File

@ -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
* <literal>Red</literal> or <literal>DarkSlateGray</literal>, or
* hexadecimal specifications like <literal>&num;3050b2</literal> or
* <literal>&num;333</literal>.
* The format of @str can be either one of:
* <itemizedlist>
* <listitem><para>a standard name (taken from the X11 rgb.txt
* file");</para></listitem>
* <listitem><para>an hexadecimal value in the form: '#rgb', '#rrggbb',
* '#rgba' or '#rrggbbaa';</para></listitem>
* <listitem><para>a RGB color in the form 'rgb(r, g, b)';</para></listitem>
* <listitem><para>a RGBA color in the form 'rgba(r, g, b,
* a)';</para></listitem>
*
* 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))
{

View File

@ -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