[gtk+/gtk-style-context] GtkGradient: Handle symbolic gradients.



commit 1a0df49f0c4f9941c057570e4c1ab2dac9e8acc9
Author: Carlos Garnacho <carlosg gnome org>
Date:   Tue Oct 12 21:48:23 2010 +0200

    GtkGradient: Handle symbolic gradients.
    
    The css parser has been modified to parse correctly radial gradients:
    
    background-image: -gtk-gradient (radial,
                                     center center, 0,
                                     center center, 0.8,
                                     from (#000), to (#fff));
    
    The theming engine has been modified to correctly animate these,
    as well as transitions between different pattern types.

 gtk/gtkcssprovider.c   |  232 +++++++++++++++++++++++++++---------------------
 gtk/gtksymboliccolor.c |   39 ++++++++-
 gtk/gtksymboliccolor.h |    6 ++
 gtk/gtkthemingengine.c |  150 +++++++++++++++++++++----------
 4 files changed, 272 insertions(+), 155 deletions(-)
---
diff --git a/gtk/gtkcssprovider.c b/gtk/gtkcssprovider.c
index 598b1ff..1150b08 100644
--- a/gtk/gtkcssprovider.c
+++ b/gtk/gtkcssprovider.c
@@ -1354,6 +1354,9 @@ gradient_parse_str (const gchar  *str,
                     gchar       **end_ptr)
 {
   GtkGradient *gradient = NULL;
+  gdouble coords[6];
+  gchar *end;
+  guint i;
 
   if (g_str_has_prefix (str, "-gtk-gradient"))
     {
@@ -1390,15 +1393,67 @@ gradient_parse_str (const gchar  *str,
 
       SKIP_SPACES (str);
 
-      if (type == CAIRO_PATTERN_TYPE_LINEAR)
+      /* Parse start/stop position parameters */
+      for (i = 0; i < 2; i++)
         {
-          gdouble coords[4];
-          gchar *end;
-          guint i;
+          if (*str != ',')
+            {
+              *end_ptr = (gchar *) str;
+              return NULL;
+            }
+
+          str++;
+          SKIP_SPACES (str);
+
+          if (strncmp (str, "left", 4) == 0)
+            {
+              coords[i * 3] = 0;
+              str += strlen ("left");
+            }
+          else if (strncmp (str, "right", 5) == 0)
+            {
+              coords[i * 3] = 1;
+              str += strlen ("right");
+            }
+          else if (strncmp (str, "center", 6) == 0)
+            {
+              coords[i * 3] = 0.5;
+              str += strlen ("center");
+            }
+          else
+            {
+              coords[i * 3] = g_ascii_strtod (str, &end);
+              str = end;
+            }
+
+          SKIP_SPACES (str);
+
+          if (strncmp (str, "top", 3) == 0)
+            {
+              coords[(i * 3) + 1] = 0;
+              str += strlen ("top");
+            }
+          else if (strncmp (str, "bottom", 6) == 0)
+            {
+              coords[(i * 3) + 1] = 1;
+              str += strlen ("bottom");
+            }
+          else if (strncmp (str, "center", 6) == 0)
+            {
+              coords[(i * 3) + 1] = 0.5;
+              str += strlen ("center");
+            }
+          else
+            {
+              coords[(i * 3) + 1] = g_ascii_strtod (str, &end);
+              str = end;
+            }
+
+          SKIP_SPACES (str);
 
-          /* Parse start/stop position parameters */
-          for (i = 0; i < 2; i++)
+          if (type == CAIRO_PATTERN_TYPE_RADIAL)
             {
+              /* Parse radius */
               if (*str != ',')
                 {
                   *end_ptr = (gchar *) str;
@@ -1408,109 +1463,63 @@ gradient_parse_str (const gchar  *str,
               str++;
               SKIP_SPACES (str);
 
-              if (g_str_has_prefix (str, "left"))
-                {
-                  coords[i * 2] = 0;
-                  str += strlen ("left");
-                }
-              else if (g_str_has_prefix (str, "right"))
-                {
-                  coords[i * 2] = 1;
-                  str += strlen ("right");
-                }
-              else
-                {
-                  coords[i * 2] = g_ascii_strtod (str, &end);
-                  str = end;
-                }
+              coords[(i * 3) + 2] = g_ascii_strtod (str, &end);
+              str = end;
 
               SKIP_SPACES (str);
+            }
+        }
 
-              if (g_str_has_prefix (str, "top"))
-                {
-                  coords[(i * 2) + 1] = 0;
-                  str += strlen ("top");
-                }
-              else if (g_str_has_prefix (str, "bottom"))
-                {
-                  coords[(i * 2) + 1] = 1;
-                  str += strlen ("bottom");
-                }
-              else
-                {
-                  coords[(i * 2) + 1] = g_ascii_strtod (str, &end);
-                  str = end;
-                }
+      if (type == CAIRO_PATTERN_TYPE_LINEAR)
+        gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[3], coords[4]);
+      else
+        gradient = gtk_gradient_new_radial (coords[0], coords[1], coords[2],
+                                            coords[3], coords[4], coords[5]);
 
-              SKIP_SPACES (str);
+      while (*str == ',')
+        {
+          GtkSymbolicColor *color;
+          gdouble position;
+
+          if (*str != ',')
+            {
+              *end_ptr = (gchar *) str;
+              return gradient;
             }
 
-          gradient = gtk_gradient_new_linear (coords[0], coords[1], coords[2], coords[3]);
+          str++;
+          SKIP_SPACES (str);
 
-          while (*str == ',')
+          if (g_str_has_prefix (str, "from"))
             {
-              GtkSymbolicColor *color;
-              gdouble position;
+              position = 0;
+              str += strlen ("from");
+              SKIP_SPACES (str);
 
-              if (*str != ',')
+              if (*str != '(')
                 {
                   *end_ptr = (gchar *) str;
                   return gradient;
                 }
-
-              str++;
+            }
+          else if (g_str_has_prefix (str, "to"))
+            {
+              position = 1;
+              str += strlen ("to");
               SKIP_SPACES (str);
 
-              if (g_str_has_prefix (str, "from"))
+              if (*str != '(')
                 {
-                  position = 0;
-                  str += strlen ("from");
-                  SKIP_SPACES (str);
-
-                  if (*str != '(')
-                    {
-                      *end_ptr = (gchar *) str;
-                      return gradient;
-                    }
-                }
-              else if (g_str_has_prefix (str, "to"))
-                {
-                  position = 1;
-                  str += strlen ("to");
-                  SKIP_SPACES (str);
-
-                  if (*str != '(')
-                    {
-                      *end_ptr = (gchar *) str;
-                      return gradient;
-                    }
+                  *end_ptr = (gchar *) str;
+                  return gradient;
                 }
-              else if (g_str_has_prefix (str, "color-stop"))
-                {
-                  str += strlen ("color-stop");
-                  SKIP_SPACES (str);
-
-                  if (*str != '(')
-                    {
-                      *end_ptr = (gchar *) str;
-                      return gradient;
-                    }
-
-                  str++;
-                  SKIP_SPACES (str);
-
-                  position = g_strtod (str, &end);
-
-                  str = end;
-                  SKIP_SPACES (str);
+            }
+          else if (g_str_has_prefix (str, "color-stop"))
+            {
+              str += strlen ("color-stop");
+              SKIP_SPACES (str);
 
-                  if (*str != ',')
-                    {
-                      *end_ptr = (gchar *) str;
-                      return gradient;
-                    }
-                }
-              else
+              if (*str != '(')
                 {
                   *end_ptr = (gchar *) str;
                   return gradient;
@@ -1519,26 +1528,30 @@ gradient_parse_str (const gchar  *str,
               str++;
               SKIP_SPACES (str);
 
-              color = symbolic_color_parse_str (str, &end);
+              position = g_strtod (str, &end);
 
               str = end;
               SKIP_SPACES (str);
 
-              if (*str != ')')
+              if (*str != ',')
                 {
                   *end_ptr = (gchar *) str;
                   return gradient;
                 }
+            }
+          else
+            {
+              *end_ptr = (gchar *) str;
+              return gradient;
+            }
 
-              str++;
-              SKIP_SPACES (str);
+          str++;
+          SKIP_SPACES (str);
 
-              if (color)
-                {
-                  gtk_gradient_add_color_stop (gradient, position, color);
-                  gtk_symbolic_color_unref (color);
-                }
-            }
+          color = symbolic_color_parse_str (str, &end);
+
+          str = end;
+          SKIP_SPACES (str);
 
           if (*str != ')')
             {
@@ -1547,7 +1560,22 @@ gradient_parse_str (const gchar  *str,
             }
 
           str++;
+          SKIP_SPACES (str);
+
+          if (color)
+            {
+              gtk_gradient_add_color_stop (gradient, position, color);
+              gtk_symbolic_color_unref (color);
+            }
         }
+
+      if (*str != ')')
+        {
+          *end_ptr = (gchar *) str;
+          return gradient;
+        }
+
+      str++;
     }
 
   *end_ptr = (gchar *) str;
@@ -2328,8 +2356,6 @@ gtk_css_provider_load_from_path (GtkCssProvider  *css_provider,
                                  const gchar     *path,
                                  GError         **error)
 {
-  GtkCssProviderPrivate *priv;
-
   g_return_val_if_fail (GTK_IS_CSS_PROVIDER (css_provider), FALSE);
   g_return_val_if_fail (path != NULL, FALSE);
 
diff --git a/gtk/gtksymboliccolor.c b/gtk/gtksymboliccolor.c
index 310e211..657ac82 100644
--- a/gtk/gtksymboliccolor.c
+++ b/gtk/gtksymboliccolor.c
@@ -74,6 +74,8 @@ struct GtkGradient
   gdouble y0;
   gdouble x1;
   gdouble y1;
+  gdouble radius0;
+  gdouble radius1;
 
   GArray *stops;
 
@@ -269,6 +271,33 @@ gtk_gradient_new_linear (gdouble x0,
   gradient->y0 = y0;
   gradient->x1 = x1;
   gradient->y1 = y1;
+  gradient->radius0 = 0;
+  gradient->radius1 = 0;
+
+  gradient->ref_count = 1;
+
+  return gradient;
+}
+
+GtkGradient *
+gtk_gradient_new_radial (gdouble x0,
+			 gdouble y0,
+			 gdouble radius0,
+			 gdouble x1,
+			 gdouble y1,
+			 gdouble radius1)
+{
+  GtkGradient *gradient;
+
+  gradient = g_slice_new (GtkGradient);
+  gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
+
+  gradient->x0 = x0;
+  gradient->y0 = y0;
+  gradient->x1 = x1;
+  gradient->y1 = y1;
+  gradient->radius0 = radius0;
+  gradient->radius1 = radius1;
 
   gradient->ref_count = 1;
 
@@ -336,8 +365,14 @@ gtk_gradient_resolve (GtkGradient      *gradient,
   g_return_val_if_fail (GTK_IS_STYLE_SET (style_set), FALSE);
   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
 
-  pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
-                                         gradient->x1, gradient->y1);
+  if (gradient->radius0 == 0 && gradient->radius1 == 0)
+    pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
+                                           gradient->x1, gradient->y1);
+  else
+    pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
+                                           gradient->radius0,
+                                           gradient->x1, gradient->y1,
+                                           gradient->radius1);
 
   for (i = 0; i < gradient->stops->len; i++)
     {
diff --git a/gtk/gtksymboliccolor.h b/gtk/gtksymboliccolor.h
index aedd2aa..de403a3 100644
--- a/gtk/gtksymboliccolor.h
+++ b/gtk/gtksymboliccolor.h
@@ -49,6 +49,12 @@ GtkGradient * gtk_gradient_new_linear (gdouble x0,
                                        gdouble y0,
                                        gdouble x1,
                                        gdouble y1);
+GtkGradient * gtk_gradient_new_radial (gdouble x0,
+				       gdouble y0,
+				       gdouble radius0,
+				       gdouble x1,
+				       gdouble y1,
+				       gdouble radius1);
 
 void gtk_gradient_add_color_stop (GtkGradient      *gradient,
                                   gdouble           offset,
diff --git a/gtk/gtkthemingengine.c b/gtk/gtkthemingengine.c
index ee7e641..95536ab 100644
--- a/gtk/gtkthemingengine.c
+++ b/gtk/gtkthemingengine.c
@@ -632,8 +632,6 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine,
   gint exterior_size, interior_size, pad, thickness;
   gdouble radius;
 
-  /* FIXME: set clipping */
-
   flags = gtk_theming_engine_get_state (engine);
   path = gtk_theming_engine_get_path (engine);
   radius = MIN (width, height) / 2 - 0.5;
@@ -897,7 +895,7 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
   cairo_pattern_t *pattern;
   GtkStateFlags flags;
   gboolean prelight;
-  gdouble progress;
+  gdouble progress, alpha = 1;
 
   flags = gtk_theming_engine_get_state (engine);
   cairo_save (cr);
@@ -919,6 +917,9 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
 
   prelight = gtk_theming_engine_is_state_set (engine, GTK_STATE_PRELIGHT, &progress);
 
+  cairo_translate (cr, x, y);
+  cairo_scale (cr, width, height);
+
   if (prelight || progress > 0 )
     {
       cairo_pattern_t *other_pattern;
@@ -942,53 +943,89 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
 
       if (pattern && other_pattern)
         {
-          gdouble offset0, red0, green0, blue0, alpha0;
-          gdouble offset1, red1, green1, blue1, alpha1;
-          gdouble x00, x01, y00, y01, x10, x11, y10, y11;
+          cairo_pattern_type_t type, other_type;
           gint n0, n1;
-          guint i;
-
-          cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01);
-          cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11);
-
-          new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
-                                                     y00 + (y10 - y00) * progress,
-                                                     x01 + (x11 - x01) * progress,
-                                                     y01 + (y11 - y01) * progress);
-          cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST);
 
           cairo_pattern_get_color_stop_count (pattern, &n0);
           cairo_pattern_get_color_stop_count (other_pattern, &n1);
-          i = 0;
+          type = cairo_pattern_get_type (pattern);
+          other_type = cairo_pattern_get_type (other_pattern);
 
-          /* Blend both gradients into one */
-          while (i < n0 && i < n1)
+          if (type == other_type && n0 == n1)
             {
-              cairo_pattern_get_color_stop_rgba (pattern, i,
-                                                 &offset0,
-                                                 &red0, &green0, &blue0,
-                                                 &alpha0);
-              cairo_pattern_get_color_stop_rgba (other_pattern, i,
-                                                 &offset1,
-                                                 &red1, &green1, &blue1,
-                                                 &alpha1);
-
-              cairo_pattern_add_color_stop_rgba (new_pattern,
-                                                 offset0 + ((offset1 - offset0) * progress),
-                                                 red0 + ((red1 - red0) * progress),
-                                                 green0 + ((green1 - green0) * progress),
-                                                 blue0 + ((blue1 - blue0) * progress),
-                                                 alpha0 + ((alpha1 - alpha0) * progress));
-              i++;
+              gdouble offset0, red0, green0, blue0, alpha0;
+              gdouble offset1, red1, green1, blue1, alpha1;
+              gdouble x00, x01, y00, y01, x10, x11, y10, y11;
+              gdouble r00, r01, r10, r11;
+              guint i;
+
+              if (type == CAIRO_PATTERN_TYPE_LINEAR)
+                {
+                  cairo_pattern_get_linear_points (pattern, &x00, &y00, &x01, &y01);
+                  cairo_pattern_get_linear_points (other_pattern, &x10, &y10, &x11, &y11);
+
+                  new_pattern = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
+                                                             y00 + (y10 - y00) * progress,
+                                                             x01 + (x11 - x01) * progress,
+                                                             y01 + (y11 - y01) * progress);
+                }
+              else
+                {
+                  cairo_pattern_get_radial_circles (pattern, &x00, &y00, &r00, &x01, &y01, &r01);
+                  cairo_pattern_get_radial_circles (other_pattern, &x10, &y10, &r10, &x11, &y11, &r11);
+
+                  new_pattern = cairo_pattern_create_radial (x00 + (x10 - x00) * progress,
+                                                             y00 + (y10 - y00) * progress,
+                                                             r00 + (r10 - r00) * progress,
+                                                             x01 + (x11 - x01) * progress,
+                                                             y01 + (y11 - y01) * progress,
+                                                             r01 + (r11 - r01) * progress);
+                }
+
+              cairo_pattern_set_filter (new_pattern, CAIRO_FILTER_FAST);
+              i = 0;
+
+              /* Blend both gradients into one */
+              while (i < n0 && i < n1)
+                {
+                  cairo_pattern_get_color_stop_rgba (pattern, i,
+                                                     &offset0,
+                                                     &red0, &green0, &blue0,
+                                                     &alpha0);
+                  cairo_pattern_get_color_stop_rgba (other_pattern, i,
+                                                     &offset1,
+                                                     &red1, &green1, &blue1,
+                                                     &alpha1);
+
+                  cairo_pattern_add_color_stop_rgba (new_pattern,
+                                                     offset0 + ((offset1 - offset0) * progress),
+                                                     red0 + ((red1 - red0) * progress),
+                                                     green0 + ((green1 - green0) * progress),
+                                                     blue0 + ((blue1 - blue0) * progress),
+                                                     alpha0 + ((alpha1 - alpha0) * progress));
+                  i++;
+                }
+            }
+          else
+            {
+              /* Different pattern types, or different color
+               * stop counts, alpha blend both patterns.
+               */
+              cairo_rectangle (cr, 0, 0, 1, 1);
+              cairo_set_source (cr, other_pattern);
+              cairo_fill (cr);
+
+              /* Set alpha for posterior drawing
+               * of the target pattern
+               */
+              alpha = 1 - progress;
             }
-
-          /* FIXME: Handle remaining color stops in both source patterns */
         }
       else if (pattern || other_pattern)
         {
           cairo_pattern_t *p;
           GdkColor *c;
-          gdouble x0, y0, x1, y1;
+          gdouble x0, y0, x1, y1, r0, r1;
           gdouble red0, green0, blue0;
           gdouble red1, green1, blue1;
           gint n, i;
@@ -1006,8 +1043,17 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
               c = gtk_theming_engine_has_class (engine, "entry") ? base_color : bg_color;
             }
 
-          cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
-          new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1);
+          if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
+            {
+              cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
+              new_pattern = cairo_pattern_create_linear (x0, y0, x1, y1);
+            }
+          else
+            {
+              cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1);
+              new_pattern = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1);
+            }
+
           cairo_pattern_get_color_stop_count (p, &n);
 
           red0 = c->red / 65535.;
@@ -1067,14 +1113,11 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
         gdk_color_free (other_base);
     }
 
+  cairo_rectangle (cr, 0, 0, 1, 1);
+
   if (pattern)
     {
-      cairo_translate (cr, x, y);
-      cairo_scale (cr, width, height);
-
-      cairo_rectangle (cr, 0, 0, 1, 1);
       cairo_set_source (cr, pattern);
-
       cairo_pattern_destroy (pattern);
     }
   else
@@ -1083,8 +1126,6 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
         gdk_cairo_set_source_color (cr, base_color);
       else
         gdk_cairo_set_source_color (cr, bg_color);
-
-      cairo_rectangle (cr, x, y, width, height);
     }
 
   if (gtk_theming_engine_has_class (engine, "tooltip"))
@@ -1095,7 +1136,18 @@ gtk_theming_engine_render_background (GtkThemingEngine *engine,
       cairo_stroke (cr);
     }
   else
-    cairo_fill (cr);
+    {
+      if (alpha == 1)
+        cairo_fill (cr);
+      else
+        {
+          cairo_pattern_t *mask;
+
+          mask = cairo_pattern_create_rgba (1, 1, 1, alpha);
+          cairo_mask (cr, mask);
+          cairo_pattern_destroy (mask);
+        }
+    }
 
   cairo_restore (cr);
 
@@ -1544,8 +1596,6 @@ gtk_theming_engine_render_layout (GtkThemingEngine *engine,
   cairo_save (cr);
   flags = gtk_theming_engine_get_state (engine);
 
-  /* FIXME: Set clipping */
-
   gtk_theming_engine_get (engine, flags,
                           "foreground-color", &fg_color,
                           NULL);



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