[gtk+] Switch to CSS interpretation of rgb() and rgba() colors



commit 5daab5266113a6aadd96fdcf504f3aa19c48fe39
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Tue Nov 2 15:30:44 2010 -0400

    Switch to CSS interpretation of rgb() and rgba() colors
    
    CSS3 defines a somewhat odd syntax for rgba() colors - the rgb values
    are integers from 0 to 255 or percentages and the a value is a
    float from 0 to 1. To avoid increasing the total amount of confusion
    in the world, make gdk_rgb_to_string() and gdk_rgb_parse() follow
    this syntax rather than using floats for r, g, and b.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=633762

 gdk/gdkrgba.c         |   91 +++++++++++++++++++++++++++++++++++++------------
 gdk/tests/gdk-color.c |   29 +++++++++-------
 2 files changed, 85 insertions(+), 35 deletions(-)
---
diff --git a/gdk/gdkrgba.c b/gdk/gdkrgba.c
index b355631..e50f47f 100644
--- a/gdk/gdkrgba.c
+++ b/gdk/gdkrgba.c
@@ -72,6 +72,43 @@ gdk_rgba_free (GdkRGBA *rgba)
   g_slice_free (GdkRGBA, rgba);
 }
 
+#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
+
+/* Parses a single color component from a rgb() or rgba() specification
+ * according to CSS3 rules. Compared to exact CSS3 parsing we are liberal
+ * in what we accept as follows:
+ *
+ *  - For non-percentage values, we accept floats in the range 0-255
+ *    not just [0-9]+ integers
+ *  - For percentage values we accept any float, not just
+ *     [ 0-9]+ | [0-9]* '.' [0-9]+
+ *  - We accept mixed percentages and non-percentages in a single
+ *    rgb() or rgba() specification.
+ */
+static double
+parse_rgb_value (const char  *str,
+		 char       **endp)
+{
+  double number;
+  const char *p;
+
+  number = g_ascii_strtod (str, endp);
+
+  p = *endp;
+
+  SKIP_WHITESPACES (p);
+
+  if (*p == '%')
+    {
+      *endp = (char *)(p + 1);
+      return CLAMP(number / 100., 0., 1.);
+    }
+  else
+    {
+      return CLAMP(number / 255., 0., 1.);
+    }
+}
+
 /**
  * gdk_rgba_parse:
  * @spec: the string specifying the color
@@ -100,8 +137,9 @@ gdk_rgba_free (GdkRGBA *rgba)
  * </itemizedlist>
  *
  * Where 'r', 'g', 'b' and 'a' are respectively the red, green, blue and
- * alpha color values, parsed in the last 2 cases as double numbers in
- * the range [0..1], any other value out of that range will be clamped.
+ * alpha color values. In the last two cases, r g and b are either integers
+ * in the range 0 to 255 or precentage values in the range 0% to 100%, and
+ * a is a floating point value in the range 0 to 1.
  *
  * Returns: %TRUE if the parsing succeeded
  **/
@@ -113,8 +151,6 @@ gdk_rgba_parse (const gchar *spec,
   gdouble r, g, b, a;
   gchar *str = (gchar *) spec;
 
-#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
-
   if (strncmp (str, "rgba", 4) == 0)
     {
       has_alpha = TRUE;
@@ -157,7 +193,7 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse red */
   SKIP_WHITESPACES (str);
-  r = g_ascii_strtod (str, &str);
+  r = parse_rgb_value (str, &str);
   SKIP_WHITESPACES (str);
 
   if (*str != ',')
@@ -167,7 +203,7 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse green */
   SKIP_WHITESPACES (str);
-  g = g_ascii_strtod (str, &str);
+  g = parse_rgb_value (str, &str);
   SKIP_WHITESPACES (str);
 
   if (*str != ',')
@@ -177,7 +213,7 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse blue */
   SKIP_WHITESPACES (str);
-  b = g_ascii_strtod (str, &str);
+  b = parse_rgb_value (str, &str);
   SKIP_WHITESPACES (str);
 
   if (has_alpha)
@@ -195,8 +231,6 @@ gdk_rgba_parse (const gchar *spec,
   if (*str != ')')
     return FALSE;
 
-#undef SKIP_WHITESPACES
-
   if (rgba)
     {
       rgba->red = CLAMP (r, 0, 1);
@@ -208,6 +242,8 @@ gdk_rgba_parse (const gchar *spec,
   return TRUE;
 }
 
+#undef SKIP_WHITESPACES
+
 /**
  * gdk_rgba_hash:
  * @p: a #GdkRGBA pointer.
@@ -259,25 +295,36 @@ gdk_rgba_equal (gconstpointer p1,
  * gdk_rgba_to_string:
  * @rgba: a #GdkRGBA
  *
- * Returns a textual specification of @rgba in the form
- * <literal>rgba (r, g, b, a)</literal>, where 'r', 'g',
- * 'b' and 'a' represent the red, green, blue and alpha
- * values respectively.
+ * Returns a textual specification of @rgba in the form <literal>rgb
+ * (r, g, b)</literal> or <literal>rgba (r, g, b, a)</literal>,
+ * where 'r', 'g', 'b' and 'a' represent the red, green, blue and alpha values
+ * respectively. r, g, and b are integers in the range 0 to 255, and a
+ * is a floating point value in the range 0 to 1.
+ *
+ * (These string forms are string forms those supported by the CSS3 colors module)
  *
  * Returns: A newly allocated text string
  **/
 gchar *
 gdk_rgba_to_string (const GdkRGBA *rgba)
 {
-  gchar red[G_ASCII_DTOSTR_BUF_SIZE];
-  gchar green[G_ASCII_DTOSTR_BUF_SIZE];
-  gchar blue[G_ASCII_DTOSTR_BUF_SIZE];
-  gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
+  if (rgba->alpha > 0.999)
+    {
+      return g_strdup_printf ("rgb(%d,%d,%d)",
+			      (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
+			      (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
+			      (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.));
+    }
+  else
+    {
+      gchar alpha[G_ASCII_DTOSTR_BUF_SIZE];
 
-  g_ascii_dtostr (red, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->red, 0, 1));
-  g_ascii_dtostr (green, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->green, 0, 1));
-  g_ascii_dtostr (blue, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->blue, 0, 1));
-  g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
+      g_ascii_dtostr (alpha, G_ASCII_DTOSTR_BUF_SIZE, CLAMP (rgba->alpha, 0, 1));
 
-  return g_strdup_printf ("rgba(%s,%s,%s,%s)", red, green, blue, alpha);
+      return g_strdup_printf ("rgba(%d,%d,%d,%s)",
+			      (int)(0.5 + CLAMP (rgba->red, 0., 1.) * 255.),
+			      (int)(0.5 + CLAMP (rgba->green, 0., 1.) * 255.),
+			      (int)(0.5 + CLAMP (rgba->blue, 0., 1.) * 255.),
+			      alpha);
+    }
 }
diff --git a/gdk/tests/gdk-color.c b/gdk/tests/gdk-color.c
index b0736b8..2dbe2ad 100644
--- a/gdk/tests/gdk-color.c
+++ b/gdk/tests/gdk-color.c
@@ -14,23 +14,23 @@ test_color_parse (void)
   res = gdk_rgba_parse ("", &color);
   g_assert (!res);
 
-  expected.red = 0.4;
-  expected.green = 0.3;
-  expected.blue = 0.2;
+  expected.red = 100/255.;
+  expected.green = 90/255.;
+  expected.blue = 80/255.;
   expected.alpha = 0.1;
-  res = gdk_rgba_parse ("rgba(0.4,0.3,0.2,0.1)", &color);
-  g_assert (res);
-  g_assert (gdk_rgba_equal (&color, &expected));
-
-  res = gdk_rgba_parse ("rgba ( 0.4 ,  0.3  ,   0.2 ,  0.1     )", &color);
+  res = gdk_rgba_parse ("rgba(100,90,80,0.1)", &color);
   g_assert (res);
   g_assert (gdk_rgba_equal (&color, &expected));
 
   expected.red = 0.4;
   expected.green = 0.3;
   expected.blue = 0.2;
-  expected.alpha = 1.0;
-  res = gdk_rgba_parse ("rgb(0.4,0.3,0.2)", &color);
+  expected.alpha = 0.1;
+  res = gdk_rgba_parse ("rgba(40%,30%,20%,0.1)", &color);
+  g_assert (res);
+  g_assert (gdk_rgba_equal (&color, &expected));
+
+  res = gdk_rgba_parse ("rgba(  40 % ,  30 %  ,   20 % ,  0.1    )", &color);
   g_assert (res);
   g_assert (gdk_rgba_equal (&color, &expected));
 
@@ -61,10 +61,13 @@ test_color_to_string (void)
   gchar *res_en;
   gchar *orig;
 
+  /* Using /255. values for the r, g, b components should
+   * make sure they round-trip exactly without rounding
+   * from the double => integer => double conversions */
   rgba.red = 1.0;
-  rgba.green = 0.5;
-  rgba.blue = 0.1;
-  rgba.alpha = 1.0;
+  rgba.green = 128/255.;
+  rgba.blue = 64/255.;
+  rgba.alpha = 0.5;
 
   orig = g_strdup (setlocale (LC_ALL, NULL));
   res = gdk_rgba_to_string (&rgba);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]