]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcssshadowvalue.c
filechooserbutton: Duh, remove all the timeouts after tests
[~andy/gtk] / gtk / gtkcssshadowvalue.c
index 370e1dba978bf2c4ad6709e4b3701af807aa683f..618288807af8d3b7e9571d013c632880a9f7f67a 100644 (file)
 
 #include "gtkcssshadowvalueprivate.h"
 
+#include "gtkcairoblurprivate.h"
+#include "gtkcsscolorvalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
+#include "gtkcssrgbavalueprivate.h"
 #include "gtkstylecontextprivate.h"
 #include "gtkthemingengineprivate.h"
 #include "gtkpango.h"
 
-typedef struct _GtkShadowElement GtkShadowElement;
-
-struct _GtkShadowElement {
-  gint16 hoffset;
-  gint16 voffset;
-  gint16 radius;
-  gint16 spread;
+struct _GtkCssValue {
+  GTK_CSS_VALUE_BASE
+  guint inset :1;
 
-  gboolean inset;
+  GtkCssValue *hoffset;
+  GtkCssValue *voffset;
+  GtkCssValue *radius;
+  GtkCssValue *spread;
 
-  GdkRGBA color;
-  GtkSymbolicColor *symbolic_color;
+  GtkCssValue *color;
 };
 
-static void
-shadow_element_print (GtkShadowElement *element,
-                      GString          *str)
-{
-  gchar *color_str;
-
-  if (element->inset)
-    g_string_append (str, "inset ");
-
-  g_string_append_printf (str, "%d %d ",
-                          (gint) element->hoffset,
-                          (gint) element->voffset);
-
-  if (element->radius != 0)
-    g_string_append_printf (str, "%d ", (gint) element->radius);
-
-  if (element->spread != 0)
-    g_string_append_printf (str, "%d ", (gint) element->spread);
-
-  if (element->symbolic_color != NULL)
-    color_str = gtk_symbolic_color_to_string (element->symbolic_color);
-  else
-    color_str = gdk_rgba_to_string (&element->color);
-
-  g_string_append (str, color_str);
-  g_free (color_str);
-}
+static GtkCssValue *    gtk_css_shadow_value_new (GtkCssValue *hoffset,
+                                                  GtkCssValue *voffset,
+                                                  GtkCssValue *radius,
+                                                  GtkCssValue *spread,
+                                                  gboolean     inset,
+                                                  GtkCssValue *color);
 
 static void
-shadow_element_free (GtkShadowElement *element)
+gtk_css_value_shadow_free (GtkCssValue *shadow)
 {
-  if (element->symbolic_color != NULL)
-    gtk_symbolic_color_unref (element->symbolic_color);
+  _gtk_css_value_unref (shadow->hoffset);
+  _gtk_css_value_unref (shadow->voffset);
+  _gtk_css_value_unref (shadow->radius);
+  _gtk_css_value_unref (shadow->spread);
+  _gtk_css_value_unref (shadow->color);
 
-  g_slice_free (GtkShadowElement, element);
+  g_slice_free (GtkCssValue, shadow);
 }
 
-static GtkShadowElement *
-shadow_element_new (gdouble hoffset,
-                    gdouble voffset,
-                    gdouble radius,
-                    gdouble spread,
-                    gboolean inset,
-                    GdkRGBA *color,
-                    GtkSymbolicColor *symbolic_color)
+static GtkCssValue *
+gtk_css_value_shadow_compute (GtkCssValue             *shadow,
+                              guint                    property_id,
+                              GtkStyleProviderPrivate *provider,
+                              GtkCssComputedValues    *values,
+                              GtkCssComputedValues    *parent_values,
+                              GtkCssDependencies      *dependencies)
 {
-  GtkShadowElement *retval;
-
-  retval = g_slice_new0 (GtkShadowElement);
+  GtkCssValue *hoffset, *voffset, *radius, *spread, *color;
+  GtkCssDependencies child_deps;
 
-  retval->hoffset = hoffset;
-  retval->voffset = voffset;
-  retval->radius = radius;
-  retval->spread = spread;
-  retval->inset = inset;
+  child_deps = 0;
+  hoffset = _gtk_css_value_compute (shadow->hoffset, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
 
-  if (symbolic_color != NULL)
-    retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color);
+  child_deps = 0;
+  voffset = _gtk_css_value_compute (shadow->voffset, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
 
-  if (color != NULL)
-    retval->color = *color;
+  child_deps = 0;
+  radius = _gtk_css_value_compute (shadow->radius, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
 
-  return retval;
-}                  
+  child_deps = 0;
+  spread = _gtk_css_value_compute (shadow->spread, property_id, provider, values, parent_values, &child_deps),
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
 
-/****************
- * GtkCssValue *
- ****************/
+  child_deps = 0;
+  color = _gtk_css_value_compute (shadow->color, property_id, provider, values, parent_values, &child_deps);
+  *dependencies = _gtk_css_dependencies_union (*dependencies, child_deps);
 
-struct _GtkCssValue {
-  GTK_CSS_VALUE_BASE
-  GList *elements;
-};
-
-static void
-gtk_css_value_shadow_free (GtkCssValue *shadow)
-{
-  g_list_free_full (shadow->elements,
-                    (GDestroyNotify) shadow_element_free);
-  g_slice_free (GtkCssValue, shadow);
+  return gtk_css_shadow_value_new (hoffset, voffset, radius, spread, shadow->inset, color);
 }
 
 static gboolean
 gtk_css_value_shadow_equal (const GtkCssValue *shadow1,
                             const GtkCssValue *shadow2)
 {
-  /* FIXME */
-  return shadow1 == shadow2;
+  return shadow1->inset == shadow2->inset
+      && _gtk_css_value_equal (shadow1->hoffset, shadow2->hoffset)
+      && _gtk_css_value_equal (shadow1->voffset, shadow2->voffset)
+      && _gtk_css_value_equal (shadow1->radius, shadow2->radius)
+      && _gtk_css_value_equal (shadow1->spread, shadow2->spread)
+      && _gtk_css_value_equal (shadow1->color, shadow2->color);
+}
+
+static GtkCssValue *
+gtk_css_value_shadow_transition (GtkCssValue *start,
+                                 GtkCssValue *end,
+                                 guint        property_id,
+                                 double       progress)
+{
+  if (start->inset != end->inset)
+    return NULL;
+
+  return gtk_css_shadow_value_new (_gtk_css_value_transition (start->hoffset, end->hoffset, property_id, progress),
+                                   _gtk_css_value_transition (start->voffset, end->voffset, property_id, progress),
+                                   _gtk_css_value_transition (start->radius, end->radius, property_id, progress),
+                                   _gtk_css_value_transition (start->spread, end->spread, property_id, progress),
+                                   start->inset,
+                                   _gtk_css_value_transition (start->color, end->color, property_id, progress));
 }
 
 static void
 gtk_css_value_shadow_print (const GtkCssValue *shadow,
                             GString           *string)
 {
-  gint length;
-  GList *l;
-
-  length = g_list_length (shadow->elements);
+  _gtk_css_value_print (shadow->hoffset, string);
+  g_string_append_c (string, ' ');
+  _gtk_css_value_print (shadow->voffset, string);
+  g_string_append_c (string, ' ');
+  if (_gtk_css_number_value_get (shadow->radius, 100) != 0 ||
+      _gtk_css_number_value_get (shadow->spread, 100) != 0)
+    {
+      _gtk_css_value_print (shadow->radius, string);
+      g_string_append_c (string, ' ');
+    }
 
-  if (length == 0)
+  if (_gtk_css_number_value_get (shadow->spread, 100) != 0)
     {
-      g_string_append (string, "none");
-      return;
+      _gtk_css_value_print (shadow->spread, string);
+      g_string_append_c (string, ' ');
     }
 
-  shadow_element_print (shadow->elements->data, string);
+  _gtk_css_value_print (shadow->color, string);
 
-  if (length == 1)
-    return;
+  if (shadow->inset)
+    g_string_append (string, " inset");
 
-  for (l = g_list_next (shadow->elements); l != NULL; l = l->next)
-    {
-      g_string_append (string, ", ");
-      shadow_element_print (l->data, string);
-    }
 }
 
 static const GtkCssValueClass GTK_CSS_VALUE_SHADOW = {
   gtk_css_value_shadow_free,
+  gtk_css_value_shadow_compute,
   gtk_css_value_shadow_equal,
+  gtk_css_value_shadow_transition,
   gtk_css_value_shadow_print
 };
 
-static GtkCssValue none_singleton = { &GTK_CSS_VALUE_SHADOW, 1, NULL };
+static GtkCssValue *
+gtk_css_shadow_value_new (GtkCssValue *hoffset,
+                          GtkCssValue *voffset,
+                          GtkCssValue *radius,
+                          GtkCssValue *spread,
+                          gboolean     inset,
+                          GtkCssValue *color)
+{
+  GtkCssValue *retval;
+
+  retval = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
+
+  retval->hoffset = hoffset;
+  retval->voffset = voffset;
+  retval->radius = radius;
+  retval->spread = spread;
+  retval->inset = inset;
+  retval->color = color;
+
+  return retval;
+}                  
 
 GtkCssValue *
-_gtk_css_shadow_value_new_none (void)
+_gtk_css_shadow_value_new_for_transition (GtkCssValue *target)
+{
+  GdkRGBA transparent = { 0, 0, 0, 0 };
+
+  g_return_val_if_fail (target->class == &GTK_CSS_VALUE_SHADOW, NULL);
+
+  return gtk_css_shadow_value_new (_gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   _gtk_css_number_value_new (0, GTK_CSS_PX),
+                                   target->inset,
+                                   _gtk_css_rgba_value_new_from_rgba (&transparent));
+}
+
+static gboolean
+value_is_done_parsing (GtkCssParser *parser)
 {
-  return _gtk_css_value_ref (&none_singleton);
+  return _gtk_css_parser_is_eof (parser) ||
+         _gtk_css_parser_begins_with (parser, ',') ||
+         _gtk_css_parser_begins_with (parser, ';') ||
+         _gtk_css_parser_begins_with (parser, '}');
 }
 
 GtkCssValue *
 _gtk_css_shadow_value_parse (GtkCssParser *parser)
 {
-  gboolean have_inset, have_color, have_lengths;
-  gdouble hoffset, voffset, blur, spread;
-  GtkSymbolicColor *color;
-  GtkShadowElement *element;
-  GtkCssValue *shadow;
+  enum {
+    HOFFSET,
+    VOFFSET,
+    RADIUS,
+    SPREAD,
+    COLOR,
+    N_VALUES
+  };
+  GtkCssValue *values[N_VALUES] = { NULL, };
+  gboolean inset;
   guint i;
 
-  if (_gtk_css_parser_try (parser, "none", TRUE))
-    return _gtk_css_shadow_value_new_none ();
-
-  shadow = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
+  inset = _gtk_css_parser_try (parser, "inset", TRUE);
 
   do
+  {
+    if (values[HOFFSET] == NULL &&
+         _gtk_css_parser_has_number (parser))
+      {
+        values[HOFFSET] = _gtk_css_number_value_parse (parser,
+                                                       GTK_CSS_PARSE_LENGTH
+                                                       | GTK_CSS_NUMBER_AS_PIXELS);
+        if (values[HOFFSET] == NULL)
+          goto fail;
+
+        values[VOFFSET] = _gtk_css_number_value_parse (parser,
+                                                       GTK_CSS_PARSE_LENGTH
+                                                       | GTK_CSS_NUMBER_AS_PIXELS);
+        if (values[VOFFSET] == NULL)
+          goto fail;
+
+        if (_gtk_css_parser_has_number (parser))
+          {
+            values[RADIUS] = _gtk_css_number_value_parse (parser,
+                                                          GTK_CSS_PARSE_LENGTH
+                                                          | GTK_CSS_POSITIVE_ONLY
+                                                          | GTK_CSS_NUMBER_AS_PIXELS);
+            if (values[RADIUS] == NULL)
+              goto fail;
+          }
+        else
+          values[RADIUS] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
+                                                        
+        if (_gtk_css_parser_has_number (parser))
+          {
+            values[SPREAD] = _gtk_css_number_value_parse (parser,
+                                                          GTK_CSS_PARSE_LENGTH
+                                                          | GTK_CSS_NUMBER_AS_PIXELS);
+            if (values[SPREAD] == NULL)
+              goto fail;
+          }
+        else
+          values[SPREAD] = _gtk_css_number_value_new (0.0, GTK_CSS_PX);
+      }
+    else if (!inset && _gtk_css_parser_try (parser, "inset", TRUE))
+      {
+        if (values[HOFFSET] == NULL)
+          goto fail;
+        inset = TRUE;
+        break;
+      }
+    else if (values[COLOR] == NULL)
+      {
+        values[COLOR] = _gtk_css_color_value_parse (parser);
+
+        if (values[COLOR] == NULL)
+          goto fail;
+      }
+    else
+      {
+        /* We parsed everything and there's still stuff left?
+         * Pretend we didn't notice and let the normal code produce
+         * a 'junk at end of value' error */
+        goto fail;
+      }
+  }
+  while (values[HOFFSET] == NULL || !value_is_done_parsing (parser));
+
+  if (values[COLOR] == NULL)
+    values[COLOR] = _gtk_css_color_value_new_current_color ();
+
+  return gtk_css_shadow_value_new (values[HOFFSET], values[VOFFSET],
+                                   values[RADIUS], values[SPREAD],
+                                   inset, values[COLOR]);
+
+fail:
+  for (i = 0; i < N_VALUES; i++)
     {
-      have_inset = have_lengths = have_color = FALSE;
-
-      for (i = 0; i < 3; i++)
-        {
-          if (!have_inset && 
-              _gtk_css_parser_try (parser, "inset", TRUE))
-            {
-              have_inset = TRUE;
-              continue;
-            }
-            
-          if (!have_lengths &&
-              _gtk_css_parser_try_double (parser, &hoffset))
-            {
-              have_lengths = TRUE;
-
-              if (!_gtk_css_parser_try_double (parser, &voffset))
-                {
-                  _gtk_css_parser_error (parser, "Horizontal and vertical offsets are required");
-                  _gtk_css_value_unref (shadow);
-                  return NULL;
-                }
-
-              if (!_gtk_css_parser_try_double (parser, &blur))
-                blur = 0;
-
-              if (!_gtk_css_parser_try_double (parser, &spread))
-                spread = 0;
-
-              continue;
-            }
-
-          if (!have_color)
-            {
-              have_color = TRUE;
-
-              /* XXX: the color is optional and UA-defined if it's missing,
-               * but it doesn't really make sense for us...
-               */
-              color = _gtk_css_parser_read_symbolic_color (parser);
-
-              if (color == NULL)
-                {
-                  _gtk_css_value_unref (shadow);
-                  return NULL;
-                }
-            }
-        }
-
-      if (!have_color || !have_lengths)
-        {
-          _gtk_css_parser_error (parser, "Must specify at least color and offsets");
-          _gtk_css_value_unref (shadow);
-          return NULL;
-        }
-
-      element = shadow_element_new (hoffset, voffset,
-                                    blur, spread, have_inset,
-                                    NULL, color);
-
-      shadow->elements = g_list_append (shadow->elements, element);
-
-      gtk_symbolic_color_unref (color);
+      if (values[i])
+        _gtk_css_value_unref (values[i]);
+    }
+
+  return NULL;
+}
 
+static const cairo_user_data_key_t shadow_key;
+
+static cairo_t *
+gtk_css_shadow_value_start_drawing (const GtkCssValue *shadow,
+                                    cairo_t           *cr)
+{
+  cairo_rectangle_int_t clip_rect;
+  cairo_surface_t *surface;
+  cairo_t *blur_cr;
+  gdouble radius;
+
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  if (radius == 0.0)
+    return cr;
+
+  gdk_cairo_get_clip_rectangle (cr, &clip_rect);
+
+  /* Create a larger surface to center the blur. */
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                        clip_rect.width + 2 * radius,
+                                        clip_rect.height + 2 * radius);
+  cairo_surface_set_device_offset (surface, radius - clip_rect.x, radius - clip_rect.y);
+  blur_cr = cairo_create (surface);
+  cairo_set_user_data (blur_cr, &shadow_key, cairo_reference (cr), (cairo_destroy_func_t) cairo_destroy);
+
+  if (cairo_has_current_point (cr))
+    {
+      double x, y;
+      
+      cairo_get_current_point (cr, &x, &y);
+      cairo_move_to (blur_cr, x, y);
     }
-  while (_gtk_css_parser_try (parser, ",", TRUE));
 
-  return shadow;
+  return blur_cr;
 }
 
-GtkCssValue *
-_gtk_css_shadow_value_compute (GtkCssValue     *shadow,
-                               GtkStyleContext *context)
+static cairo_t *
+gtk_css_shadow_value_finish_drawing (const GtkCssValue *shadow,
+                                     cairo_t           *cr)
 {
-  GtkCssValue *resolved_shadow;
-  GtkShadowElement *element, *resolved_element;
-  GdkRGBA color;
-  GList *l;
+  gdouble radius;
+  cairo_t *original_cr;
+  cairo_surface_t *surface;
 
-  resolved_shadow = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_SHADOW);
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  if (radius == 0.0)
+    return cr;
 
-  for (l = shadow->elements; l != NULL; l = l->next)
-    {
-      element = l->data;
-
-      if (!_gtk_style_context_resolve_color (context,
-                                             element->symbolic_color,
-                                             &color))
-        {
-          _gtk_css_value_unref (resolved_shadow);
-          return NULL;
-        }
-
-      resolved_element =
-        shadow_element_new (element->hoffset, element->voffset,
-                            element->radius, element->spread, element->inset,
-                            &color, NULL);
-
-      resolved_shadow->elements =
-        g_list_append (resolved_shadow->elements, resolved_element);
-    }
+  surface = cairo_get_target (cr);
+  original_cr = cairo_get_user_data (cr, &shadow_key);
+
+  /* Blur the surface. */
+  _gtk_cairo_blur_surface (surface, radius);
+
+  cairo_set_source_surface (original_cr, surface, 0, 0);
+  cairo_paint (original_cr);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
 
-  return resolved_shadow;
+  return original_cr;
 }
 
 void
@@ -297,52 +371,54 @@ _gtk_css_shadow_value_paint_layout (const GtkCssValue *shadow,
                                     cairo_t           *cr,
                                     PangoLayout       *layout)
 {
-  GList *l;
-  GtkShadowElement *element;
+  g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
   if (!cairo_has_current_point (cr))
     cairo_move_to (cr, 0, 0);
 
-  /* render shadows starting from the last one,
-   * and the others on top.
-   */
-  for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
-    {
-      element = l->data;
+  cairo_save (cr);
 
-      cairo_save (cr);
+  cairo_rel_move_to (cr, 
+                     _gtk_css_number_value_get (shadow->hoffset, 0),
+                     _gtk_css_number_value_get (shadow->voffset, 0));
 
-      cairo_rel_move_to (cr, element->hoffset, element->voffset);
-      gdk_cairo_set_source_rgba (cr, &element->color);
-      _gtk_pango_fill_layout (cr, layout);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
 
-      cairo_rel_move_to (cr, -element->hoffset, -element->voffset);
-      cairo_restore (cr);
-  }
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
+  _gtk_pango_fill_layout (cr, layout);
+
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
+  cairo_rel_move_to (cr,
+                     - _gtk_css_number_value_get (shadow->hoffset, 0),
+                     - _gtk_css_number_value_get (shadow->voffset, 0));
+  cairo_restore (cr);
 }
 
 void
 _gtk_css_shadow_value_paint_icon (const GtkCssValue *shadow,
                                  cairo_t           *cr)
 {
-  GList *l;
-  GtkShadowElement *element;
   cairo_pattern_t *pattern;
 
-  for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
-    {
-      element = l->data;
+  g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
-      cairo_save (cr);
-      pattern = cairo_pattern_reference (cairo_get_source (cr));
-      gdk_cairo_set_source_rgba (cr, &element->color);
+  cairo_save (cr);
+  pattern = cairo_pattern_reference (cairo_get_source (cr));
 
-      cairo_translate (cr, element->hoffset, element->voffset);
-      cairo_mask (cr, pattern);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
 
-      cairo_restore (cr);
-      cairo_pattern_destroy (pattern);
-    }
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
+
+  cairo_translate (cr,
+                   _gtk_css_number_value_get (shadow->hoffset, 0),
+                   _gtk_css_number_value_get (shadow->voffset, 0));
+  cairo_mask (cr, pattern);
+
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
+  cairo_restore (cr);
+  cairo_pattern_destroy (pattern);
 }
 
 void
@@ -351,22 +427,22 @@ _gtk_css_shadow_value_paint_spinner (const GtkCssValue *shadow,
                                      gdouble            radius,
                                      gdouble            progress)
 {
-  GtkShadowElement *element;
-  GList *l;
+  g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
-  for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
-    {
-      element = l->data;
+  cairo_save (cr);
 
-      cairo_save (cr);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
 
-      cairo_translate (cr, element->hoffset, element->voffset);
-      _gtk_theming_engine_paint_spinner (cr,
-                                         radius, progress,
-                                         &element->color);
+  cairo_translate (cr,
+                   _gtk_css_number_value_get (shadow->hoffset, 0),
+                   _gtk_css_number_value_get (shadow->voffset, 0));
+  _gtk_theming_engine_paint_spinner (cr,
+                                     radius, progress,
+                                     _gtk_css_rgba_value_get_rgba (shadow->color));
 
-      cairo_restore (cr);
-    }
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
+
+  cairo_restore (cr);
 }
 
 void
@@ -374,38 +450,37 @@ _gtk_css_shadow_value_paint_box (const GtkCssValue   *shadow,
                                  cairo_t             *cr,
                                  const GtkRoundedBox *padding_box)
 {
-  GtkShadowElement *element;
-  GtkRoundedBox box;
-  GList *l;
+  GtkRoundedBox box, clip_box;
+  double spread, radius;
+
+  g_return_if_fail (shadow->class == &GTK_CSS_VALUE_SHADOW);
 
   cairo_save (cr);
-  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
 
   _gtk_rounded_box_path (padding_box, cr);
   cairo_clip (cr);
 
-  /* render shadows starting from the last one,
-   * and the others on top.
-   */
-  for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
-    {
-      element = l->data;
+  box = *padding_box;
+  _gtk_rounded_box_move (&box,
+                         _gtk_css_number_value_get (shadow->hoffset, 0),
+                         _gtk_css_number_value_get (shadow->voffset, 0));
+  spread = _gtk_css_number_value_get (shadow->spread, 0);
+  _gtk_rounded_box_shrink (&box, spread, spread, spread, spread);
 
-      if (!element->inset)
-        continue;
+  clip_box = *padding_box;
+  radius = _gtk_css_number_value_get (shadow->radius, 0);
+  _gtk_rounded_box_shrink (&clip_box, -radius, -radius, -radius, -radius);
 
-      box = *padding_box;
-      _gtk_rounded_box_move (&box, element->hoffset, element->voffset);
-      _gtk_rounded_box_shrink (&box,
-                               element->spread, element->spread,
-                               element->spread, element->spread);
+  cr = gtk_css_shadow_value_start_drawing (shadow, cr);
 
-      _gtk_rounded_box_path (&box, cr);
-      _gtk_rounded_box_clip_path (padding_box, cr);
+  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+  _gtk_rounded_box_path (&box, cr);
+  _gtk_rounded_box_clip_path (&clip_box, cr);
 
-      gdk_cairo_set_source_rgba (cr, &element->color);
-      cairo_fill (cr);
-  }
+  gdk_cairo_set_source_rgba (cr, _gtk_css_rgba_value_get_rgba (shadow->color));
+  cairo_fill (cr);
+
+  cr = gtk_css_shadow_value_finish_drawing (shadow, cr);
 
   cairo_restore (cr);
 }