]> Pileus Git - ~andy/gtk/blobdiff - gdk/gdkrgba.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gdk / gdkrgba.c
index b355631e0e5694940a158bf493135bda8e4bc357..1151c1245d54aea0bd781eafb7fe0814eaf8abcc 100644 (file)
@@ -12,9 +12,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 /*
 #include "config.h"
 #include "gdkrgba.h"
 #include <string.h>
+#include <errno.h>
+#include <math.h>
+
+#include "fallback-c89.c"
 
 /**
  * SECTION:rgba_colors
  * @Short_description: RGBA colors
  * @Title: RGBA Colors
+ *
+ * The #GdkRGBA struct is a convenient way to pass rgba colors around.
+ * It's based on cairo's way to deal with colors and mirrors its behavior.
+ * All values are in the range from 0.0 to 1.0 inclusive. So the color
+ * (0.0, 0.0, 0.0, 0.0) represents transparent black and
+ * (1.0, 1.0, 1.0, 1.0) is opaque white. Other values will be clamped
+ * to this range when drawing.
  */
 
 G_DEFINE_BOXED_TYPE (GdkRGBA, gdk_rgba,
                      gdk_rgba_copy, gdk_rgba_free)
 
+/**
+ * GdkRGBA:
+ * @red: The intensity of the red channel from 0.0 to 1.0 inclusive
+ * @green: The intensity of the green channel from 0.0 to 1.0 inclusive
+ * @blue: The intensity of the blue channel from 0.0 to 1.0 inclusive
+ * @alpha: The opacity of the color from 0.0 for completely translucent to
+ *   1.0 for opaque
+ *
+ * The GdkRGBA structure is used to represent a (possibly translucent)
+ * color, in a way that is compatible with cairos notion of color.
+ */
+
 /**
  * gdk_rgba_copy:
  * @rgba: a #GdkRGBA
  *
- * Makes a copy of a #GdkRGBA structure, the result must be freed
- * through gdk_rgba_free().
+ * Makes a copy of a #GdkRGBA structure.
+ *
+ * The result must be freed through gdk_rgba_free().
+ *
+ * Returns: A newly allocated #GdkRGBA, with the same contents as @rgba
  *
- * Returns: A newly allocated #GdkRGBA
- **/
+ * Since: 3.0
+ */
 GdkRGBA *
-gdk_rgba_copy (GdkRGBA *rgba)
+gdk_rgba_copy (const GdkRGBA *rgba)
 {
-  GdkRGBA *copy;
-
-  copy = g_slice_new (GdkRGBA);
-  copy->red = rgba->red;
-  copy->green = rgba->green;
-  copy->blue = rgba->blue;
-  copy->alpha = rgba->alpha;
-
-  return copy;
+  return g_slice_dup (GdkRGBA, rgba);
 }
 
 /**
@@ -65,17 +81,61 @@ gdk_rgba_copy (GdkRGBA *rgba)
  * @rgba: a #GdkRGBA
  *
  * Frees a #GdkRGBA struct created with gdk_rgba_copy()
- **/
+ *
+ * Since: 3.0
+ */
 void
 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 gboolean
+parse_rgb_value (const gchar  *str,
+                 gchar       **endp,
+                 gdouble      *number)
+{
+  const char *p;
+
+  *number = g_ascii_strtod (str, endp);
+  if (errno == ERANGE || *endp == str ||
+      isinf (*number) || isnan (*number))
+    return FALSE;
+
+  p = *endp;
+
+  SKIP_WHITESPACES (p);
+
+  if (*p == '%')
+    {
+      *endp = (char *)(p + 1);
+      *number = CLAMP(*number / 100., 0., 1.);
+    }
+  else
+    {
+      *number = CLAMP(*number / 255., 0., 1.);
+    }
+
+  return TRUE;
+}
+
 /**
  * gdk_rgba_parse:
- * @spec: the string specifying the color
  * @rgba: the #GdkRGBA struct to fill in
+ * @spec: the string specifying the color
  *
  * Parses a textual representation of a color, filling in
  * the <structfield>red</structfield>, <structfield>green</structfield>,
@@ -88,7 +148,8 @@ gdk_rgba_free (GdkRGBA *rgba)
  * A standard name (Taken from the X11 rgb.txt file).
  * </listitem>
  * <listitem>
- * A hex value in the form '#rgb' '#rrggbb' '#rrrgggbbb' or '#rrrrggggbbbb'
+ * A hex value in the form '&num;rgb' '&num;rrggbb' '&num;rrrgggbbb'
+ * or '&num;rrrrggggbbbb'
  * </listitem>
  * <listitem>
  * A RGB color in the form 'rgb(r,g,b)' (In this case the color will
@@ -100,20 +161,22 @@ 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
- **/
+ *
+ * Since: 3.0
+ */
 gboolean
-gdk_rgba_parse (const gchar *spec,
-                GdkRGBA     *rgba)
+gdk_rgba_parse (GdkRGBA     *rgba,
+                const gchar *spec)
 {
   gboolean has_alpha;
   gdouble r, g, b, a;
   gchar *str = (gchar *) spec;
-
-#define SKIP_WHITESPACES(s) while (*(s) == ' ') (s)++;
+  gchar *p;
 
   if (strncmp (str, "rgba", 4) == 0)
     {
@@ -131,7 +194,8 @@ gdk_rgba_parse (const gchar *spec,
       PangoColor pango_color;
 
       /* Resort on PangoColor for rgb.txt color
-       * map and '#' prefixed colors */
+       * map and '#' prefixed colors
+       */
       if (pango_color_parse (&pango_color, str))
         {
           if (rgba)
@@ -157,7 +221,8 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse red */
   SKIP_WHITESPACES (str);
-  r = g_ascii_strtod (str, &str);
+  if (!parse_rgb_value (str, &str, &r))
+    return FALSE;
   SKIP_WHITESPACES (str);
 
   if (*str != ',')
@@ -167,7 +232,8 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse green */
   SKIP_WHITESPACES (str);
-  g = g_ascii_strtod (str, &str);
+  if (!parse_rgb_value (str, &str, &g))
+    return FALSE;
   SKIP_WHITESPACES (str);
 
   if (*str != ',')
@@ -177,7 +243,8 @@ gdk_rgba_parse (const gchar *spec,
 
   /* Parse blue */
   SKIP_WHITESPACES (str);
-  b = g_ascii_strtod (str, &str);
+  if (!parse_rgb_value (str, &str, &b))
+    return FALSE;
   SKIP_WHITESPACES (str);
 
   if (has_alpha)
@@ -188,14 +255,23 @@ gdk_rgba_parse (const gchar *spec,
       str++;
 
       SKIP_WHITESPACES (str);
-      a = g_ascii_strtod (str, &str);
+      a = g_ascii_strtod (str, &p);
+      if (errno == ERANGE || p == str ||
+          isinf (a) || isnan (a))
+        return FALSE;
+      str = p;
       SKIP_WHITESPACES (str);
     }
 
   if (*str != ')')
     return FALSE;
 
-#undef SKIP_WHITESPACES
+  str++;
+
+  SKIP_WHITESPACES (str);
+
+  if (*str != '\0')
+    return FALSE;
 
   if (rgba)
     {
@@ -208,35 +284,41 @@ gdk_rgba_parse (const gchar *spec,
   return TRUE;
 }
 
+#undef SKIP_WHITESPACES
+
 /**
  * gdk_rgba_hash:
- * @p: a #GdkRGBA pointer.
+ * @p: (type GdkRGBA): a #GdkRGBA pointer
  *
  * A hash function suitable for using for a hash
- * table that stores #GdkRGBA<!-- -->s.
+ * table that stores #GdkRGBAs.
  *
- * Return value: The hash function applied to @p
- **/
+ * Return value: The hash value for @p
+ *
+ * Since: 3.0
+ */
 guint
 gdk_rgba_hash (gconstpointer p)
 {
   const GdkRGBA *rgba = p;
 
   return ((guint) (rgba->red * 65535) +
-         ((guint) (rgba->green * 65535) << 11) +
-         ((guint) (rgba->blue * 65535) << 22) +
-         ((guint) (rgba->alpha * 65535) >> 6));
+          ((guint) (rgba->green * 65535) << 11) +
+          ((guint) (rgba->blue * 65535) << 22) +
+          ((guint) (rgba->alpha * 65535) >> 6));
 }
 
 /**
  * gdk_rgba_equal:
- * @p1: a #GdkRGBA pointer.
- * @p2: another #GdkRGBA pointer.
+ * @p1: (type GdkRGBA): a #GdkRGBA pointer
+ * @p2: (type GdkRGBA): another #GdkRGBA pointer
  *
  * Compares two RGBA colors.
  *
  * Return value: %TRUE if the two colors compare equal
- **/
+ *
+ * Since: 3.0
+ */
 gboolean
 gdk_rgba_equal (gconstpointer p1,
                 gconstpointer p2)
@@ -260,24 +342,45 @@ gdk_rgba_equal (gconstpointer p1,
  * @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.
+ * <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
+ * represented as integers in the range 0 to 255, and a
+ * is represented as floating point value in the range 0 to 1.
+ *
+ * These string forms are string forms those supported by
+ * the CSS3 colors module, and can be parsed by gdk_rgba_parse().
+ *
+ * Note that this string representation may loose some
+ * precision, since r, g and b are represented as 8-bit
+ * integers. If this is a concern, you should use a
+ * different representation.
  *
  * Returns: A newly allocated text string
- **/
+ *
+ * Since: 3.0
+ */
 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);
+    }
 }