[gnome-shell] Move rendering into st-theme-node-drawing.c



commit 5060081db5fb0a5848dd04d7723ed3f5d02c1668
Author: Colin Walters <walters verbum org>
Date:   Mon Feb 8 13:40:25 2010 -0500

    Move rendering into st-theme-node-drawing.c
    
    The idea behind this move is that we have a lot more control over
    rendering if StWidget isn't a big pile of actors, and things are
    more efficient.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=607500

 src/Makefile-st.am             |    8 +-
 src/st/st-button.c             |    1 -
 src/st/st-shadow-texture.c     |  273 ----------
 src/st/st-shadow-texture.h     |   30 --
 src/st/st-texture-frame.c      |  621 -----------------------
 src/st/st-texture-frame.h      |   94 ----
 src/st/st-theme-node-drawing.c | 1097 ++++++++++++++++++++++++++++++++++++++++
 src/st/st-theme-node-private.h |   86 ++++
 src/st/st-theme-node.c         |  109 +---
 src/st/st-theme-node.h         |    5 +
 src/st/st-widget.c             |  645 +-----------------------
 11 files changed, 1231 insertions(+), 1738 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index 706c588..6b06688 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -83,13 +83,11 @@ st_source_h =					\
     st/st-scrollable.h				\
     st/st-scroll-bar.h				\
     st/st-scroll-view.h				\
-    st/st-shadow-texture.h                      \
     st/st-shadow.h                              \
     st/st-subtexture.h				\
     st/st-table.h				\
     st/st-table-child.h				\
     st/st-texture-cache.h			\
-    st/st-texture-frame.h			\
     st/st-theme.h				\
     st/st-theme-context.h			\
     st/st-theme-node.h				\
@@ -109,7 +107,8 @@ BUILT_SOURCES += st.h
 st_source_private_h =				\
     st/st-private.h				\
     st/st-table-private.h			\
-    st/st-theme-private.h
+    st/st-theme-private.h			\
+    st/st-theme-node-private.h
 
 # please, keep this sorted alphabetically
 st_source_c =					\
@@ -130,16 +129,15 @@ st_source_c =					\
     st/st-scrollable.c				\
     st/st-scroll-bar.c				\
     st/st-scroll-view.c				\
-    st/st-shadow-texture.c                      \
     st/st-shadow.c                              \
     st/st-subtexture.c				\
     st/st-table.c				\
     st/st-table-child.c				\
     st/st-texture-cache.c			\
-    st/st-texture-frame.c			\
     st/st-theme.c				\
     st/st-theme-context.c			\
     st/st-theme-node.c				\
+    st/st-theme-node-drawing.c		\
     st/st-tooltip.c				\
     st/st-widget.c				\
     $(NULL)
diff --git a/src/st/st-button.c b/src/st/st-button.c
index 2b8d757..7fbc101 100644
--- a/src/st/st-button.c
+++ b/src/st/st-button.c
@@ -45,7 +45,6 @@
 #include "st-button.h"
 
 #include "st-marshal.h"
-#include "st-texture-frame.h"
 #include "st-texture-cache.h"
 #include "st-private.h"
 
diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c
new file mode 100644
index 0000000..374a55c
--- /dev/null
+++ b/src/st/st-theme-node-drawing.c
@@ -0,0 +1,1097 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* Drawing for StWidget.
+
+   Copyright (C) 2009,2010 Red Hat, Inc.
+
+   Contains code derived from:
+   rectangle.c: Rounded rectangle.
+   Copyright (C) 2008 litl, LLC.
+   st-shadow-texture.c: a class for creating soft shadow texture
+   Copyright (C) 2009 Florian Müllner <fmuellner src gnome org>
+   st-texture-frame.h: Expandible texture actor
+   Copyright 2007 OpenedHand
+   Copyright 2009 Intel Corporation.
+
+   The St is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The St is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the St; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "st-theme-private.h"
+#include "st-theme-context.h"
+#include "st-texture-cache.h"
+#include "st-theme-node-private.h"
+
+/*****
+ * Shadows
+ *****/
+
+static gdouble *
+calculate_gaussian_kernel (gdouble   sigma,
+                           guint     n_values)
+{
+  gdouble *ret, sum;
+  gdouble exp_divisor;
+  gint half, i;
+
+  g_return_val_if_fail ((int) sigma > 0, NULL);
+
+  half = n_values / 2;
+
+  ret = g_malloc (n_values * sizeof (gdouble));
+  sum = 0.0;
+
+  exp_divisor = 2 * sigma * sigma;
+
+  /* n_values of 1D Gauss function */
+  for (i = 0; i < n_values; i++)
+    {
+      ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
+      sum += ret[i];
+    }
+
+  /* normalize */
+  for (i = 0; i < n_values; i++)
+    ret[i] /= sum;
+
+  return ret;
+}
+
+static CoglHandle
+create_shadow_material (StThemeNode  *node,
+                        CoglHandle    src_texture)
+{
+  CoglColor   color;
+  CoglHandle  material;
+  CoglHandle  texture;
+  StShadow   *shadow_spec;
+  guchar     *pixels_in, *pixels_out;
+  gint        width_in, height_in, rowstride_in;
+  gint        width_out, height_out, rowstride_out;
+  float       sigma;
+
+  shadow_spec = st_theme_node_get_shadow (node);
+  if (!shadow_spec)
+    return COGL_INVALID_HANDLE;
+
+  /* we use an approximation of the sigma - blur radius relationship used
+     in Firefox for doing SVG blurs; see
+     http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/src/gfxBlur.cpp#280
+  */
+  sigma = shadow_spec->blur / 1.9;
+
+  width_in  = cogl_texture_get_width  (src_texture);
+  height_in = cogl_texture_get_height (src_texture);
+  rowstride_in = (width_in + 3) & ~3;
+
+  pixels_in  = g_malloc0 (rowstride_in * height_in);
+
+  cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8,
+                         rowstride_in, pixels_in);
+
+  if ((guint) shadow_spec->blur == 0)
+    {
+      width_out  = width_in;
+      height_out = height_in;
+      rowstride_out = rowstride_in;
+      pixels_out = g_memdup (pixels_in, rowstride_out * height_out);
+    }
+  else
+    {
+      gdouble *kernel;
+      guchar  *line;
+      gint     n_values, half;
+      gint     x_in, y_in, x_out, y_out, i;
+
+      n_values = (gint) 5 * sigma;
+      half = n_values / 2;
+
+      width_out  = width_in  + 2 * half;
+      height_out = height_in + 2 * half;
+      rowstride_out = (width_out + 3) & ~3;
+
+      pixels_out = g_malloc0 (rowstride_out * height_out);
+      line       = g_malloc0 (rowstride_out);
+
+      kernel = calculate_gaussian_kernel (sigma, n_values);
+
+      /* vertical blur */
+      for (x_in = 0; x_in < width_in; x_in++)
+        for (y_out = 0; y_out < height_out; y_out++)
+          {
+            guchar *pixel_in, *pixel_out;
+            gint i0, i1;
+
+            y_in = y_out - half;
+
+            /* We read from the source at 'y = y_in + i - half'; clamp the
+             * full i range [0, n_values) so that y is in [0, height_in).
+             */
+            i0 = MAX (half - y_in, 0);
+            i1 = MIN (height_in + half - y_in, n_values);
+
+            pixel_in  =  pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
+            pixel_out =  pixels_out + y_out * rowstride_out + (x_in + half);
+
+            for (i = i0; i < i1; i++)
+              {
+                *pixel_out += *pixel_in * kernel[i];
+                pixel_in += rowstride_in;
+              }
+          }
+
+      /* horizontal blur */
+      for (y_out = 0; y_out < height_out; y_out++)
+        {
+          memcpy (line, pixels_out + y_out * rowstride_out, rowstride_out);
+
+          for (x_out = 0; x_out < width_out; x_out++)
+            {
+              gint i0, i1;
+              guchar *pixel_out, *pixel_in;
+
+              /* We read from the source at 'x = x_out + i - half'; clamp the
+               * full i range [0, n_values) so that x is in [0, width_out).
+               */
+              i0 = MAX (half - x_out, 0);
+              i1 = MIN (width_out + half - x_out, n_values);
+
+              pixel_in  = line + x_out + i0 - half;
+              pixel_out = pixels_out + rowstride_out * y_out + x_out;
+
+              *pixel_out = 0;
+              for (i = i0; i < i1; i++)
+                {
+                  *pixel_out += *pixel_in * kernel[i];
+                  pixel_in++;
+                }
+            }
+        }
+      g_free (kernel);
+      g_free (line);
+    }
+
+  texture = cogl_texture_new_from_data (width_out,
+                                        height_out,
+                                        COGL_TEXTURE_NONE,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        rowstride_out,
+                                        pixels_out);
+
+  g_free (pixels_in);
+  g_free (pixels_out);
+
+  material = cogl_material_new ();
+
+  cogl_color_set_from_4ub (&color,
+                           shadow_spec->color.red, shadow_spec->color.green,
+                           shadow_spec->color.blue, shadow_spec->color.alpha);
+  cogl_color_premultiply (&color);
+
+  cogl_material_set_layer_combine_constant (material, 0, &color);
+  cogl_material_set_layer (material, 0, texture);
+
+  /* We ignore the material color, which encodes the overall opacity of the
+   * actor, so setting an ancestor of the shadow to partially opaque won't
+   * work. The easiest way to fix this would be to not set the combine or constant color
+   * here, then in paint set the material color to the shadow_spec_color *
+   * paint_opacity.*/
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA = MODULATE (CONSTANT, TEXTURE[A])",
+                                   NULL);
+
+
+  cogl_handle_unref (texture);
+
+  return material;
+}
+
+
+/****
+ * Rounded corners
+ ****/
+
+typedef struct {
+  ClutterColor   color;
+  ClutterColor   border_color_1;
+  ClutterColor   border_color_2;
+  guint          radius;
+  guint          border_width_1;
+  guint          border_width_2;
+} StCornerSpec;
+
+static CoglHandle
+create_corner_texture (StCornerSpec *corner)
+{
+  CoglHandle texture;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  guint rowstride;
+  guint8 *data;
+  guint size;
+  guint max_border_width;
+
+  max_border_width = MAX(corner->border_width_2, corner->border_width_1);
+  size = 2 * MAX(max_border_width, corner->radius);
+  rowstride = size * 4;
+  data = g_new0 (guint8, size * rowstride);
+
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 size, size,
+                                                 rowstride);
+  cr = cairo_create (surface);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_scale (cr, size, size);
+
+  /* TODO support nonuniform border widths */
+
+  if (corner->border_width_1 < corner->radius)
+    {
+      double internal_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+
+      if (corner->border_width_1 != 0)
+        {
+          cairo_set_source_rgba (cr,
+                                 corner->border_color_1.red / 255.,
+                                 corner->border_color_1.green / 255.,
+                                 corner->border_color_1.blue / 255.,
+                                 corner->border_color_1.alpha / 255.);
+
+          cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+          cairo_fill (cr);
+        }
+
+      cairo_set_source_rgba (cr,
+                             corner->color.red / 255.,
+                             corner->color.green / 255.,
+                             corner->color.blue / 255.,
+                             corner->color.alpha / 255.);
+      cairo_arc (cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+      cairo_fill (cr);
+    }
+  else
+    {
+      double radius;
+
+      radius = (gdouble)corner->radius / corner->border_width_1;
+
+      cairo_set_source_rgba (cr,
+                             corner->border_color_1.red / 255.,
+                             corner->border_color_1.green / 255.,
+                             corner->border_color_1.blue / 255.,
+                             corner->border_color_1.alpha / 255.);
+
+      cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
+      cairo_line_to (cr, 1.0 - radius, 0.0);
+      cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI);
+      cairo_line_to (cr, 1.0, 1.0 - radius);
+      cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
+      cairo_line_to (cr, radius, 1.0);
+      cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
+      cairo_fill (cr);
+    }
+  cairo_destroy (cr);
+
+  cairo_surface_destroy (surface);
+
+  texture = cogl_texture_new_from_data (size, size,
+                                        COGL_TEXTURE_NONE,
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                        COGL_PIXEL_FORMAT_BGRA_8888_PRE,
+#else
+                                        COGL_PIXEL_FORMAT_ARGB_8888_PRE,
+#endif
+                                        COGL_PIXEL_FORMAT_ANY,
+                                        rowstride,
+                                        data);
+  g_free (data);
+  g_assert (texture != COGL_INVALID_HANDLE);
+
+  return texture;
+}
+
+static char *
+corner_to_string (StCornerSpec *corner)
+{
+  return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u",
+                          corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha,
+                          corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha,
+                          corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha,
+                          corner->radius,
+                          corner->border_width_1,
+                          corner->border_width_2);
+}
+
+typedef struct {
+  StThemeNode *node;
+  StCornerSpec *corner;
+} LoadCornerData;
+
+static CoglHandle
+load_corner (StTextureCache  *cache,
+             const char      *key,
+             void            *datap,
+             GError         **error)
+{
+  LoadCornerData *data = datap;
+
+  return create_corner_texture (data->corner);
+}
+
+/* To match the CSS specification, we want the border to look like it was
+ * drawn over the background. But actually drawing the border over the
+ * background will produce slightly bad antialiasing at the edges, so
+ * compute the effective border color instead.
+ */
+#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
+#define MULT(c,a) NORM(c*a)
+
+static void
+premultiply (ClutterColor *color)
+{
+  guint t;
+  color->red = MULT (color->red, color->alpha);
+  color->green = MULT (color->green, color->alpha);
+  color->blue = MULT (color->blue, color->alpha);
+}
+
+static void
+unpremultiply (ClutterColor *color)
+{
+  if (color->alpha != 0)
+    {
+      color->red = (color->red * 255 + 127) / color->alpha;
+      color->green = (color->green * 255 + 127) / color->alpha;
+      color->blue = (color->blue * 255 + 127) / color->alpha;
+    }
+}
+
+static void
+over (const ClutterColor *source,
+      const ClutterColor *destination,
+      ClutterColor       *result)
+{
+  guint t;
+  ClutterColor src = *source;
+  ClutterColor dst = *destination;
+
+  premultiply (&src);
+  premultiply (&dst);
+
+  result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
+  result->red   = src.red +   NORM ((255 - src.alpha) * dst.red);
+  result->green = src.green + NORM ((255 - src.alpha) * dst.green);
+  result->blue  = src.blue +  NORM ((255 - src.alpha) * dst.blue);
+
+  unpremultiply (result);
+}
+
+static CoglHandle
+st_theme_node_lookup_corner (StThemeNode    *node,
+                             StCorner        corner_id)
+{
+  CoglHandle texture;
+  char *key;
+  StTextureCache *cache;
+  StCornerSpec corner;
+  LoadCornerData data;
+
+  if (node->border_radius[corner_id] == 0)
+    return COGL_INVALID_HANDLE;
+
+  cache = st_texture_cache_get_default ();
+
+  corner.radius = node->border_radius[corner_id];
+  corner.color = node->background_color;
+
+  switch (corner_id)
+    {
+      case ST_CORNER_TOPLEFT:
+        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
+        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
+        over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_TOPRIGHT:
+        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
+        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_BOTTOMRIGHT:
+        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_BOTTOMLEFT:
+        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
+        over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+        break;
+    }
+
+  key = corner_to_string (&corner);
+
+  data.node = node;
+  data.corner = &corner;
+  texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_NONE, load_corner, &data, NULL);
+
+  g_free (key);
+
+  return texture;
+}
+
+static void
+get_background_position (StThemeNode             *self,
+                         const ClutterActorBox   *allocation,
+                         ClutterActorBox         *result)
+{
+  gfloat w, h;
+
+  result->x1 = result->y1 = 0;
+  result->x2 = allocation->x2 - allocation->x1;
+  result->y2 = allocation->y2 - allocation->y1;
+
+  w = cogl_texture_get_width (self->background_texture);
+  h = cogl_texture_get_height (self->background_texture);
+
+  /* scale the background into the allocated bounds */
+  if (w > result->x2 || h > result->y2)
+    {
+      gint new_h, new_w, offset;
+      gint box_w, box_h;
+
+      box_w = (int) result->x2;
+      box_h = (int) result->y2;
+
+      /* scale to fit */
+      new_h = (int)((h / w) * ((gfloat) box_w));
+      new_w = (int)((w / h) * ((gfloat) box_h));
+
+      if (new_h > box_h)
+        {
+          /* center for new width */
+          offset = ((box_w) - new_w) * 0.5;
+          result->x1 = offset;
+          result->x2 = offset + new_w;
+
+          result->y2 = box_h;
+        }
+      else
+        {
+          /* center for new height */
+          offset = ((box_h) - new_h) * 0.5;
+          result->y1 = offset;
+          result->y2 = offset + new_h;
+
+          result->x2 = box_w;
+        }
+    }
+  else
+    {
+      /* center the background on the widget */
+      result->x1 = (int)(((allocation->x2 - allocation->x1) / 2) - (w / 2));
+      result->y1 = (int)(((allocation->y2 - allocation->y1) / 2) - (h / 2));
+      result->x2 = result->x1 + w;
+      result->y2 = result->y1 + h;
+    }
+}
+
+/* Use of this function marks code which doesn't support
+ * non-uniform widths and/or colors.
+ */
+static gboolean
+get_arbitrary_border (StThemeNode   *node,
+                      int           *width,
+                      ClutterColor  *color)
+{
+  int w;
+
+  w = st_theme_node_get_border_width (node, ST_SIDE_TOP);
+  if (w > 0)
+    {
+      if (width)
+        *width = w;
+      if (color)
+        st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
+      return TRUE;
+    }
+
+  if (width)
+    *width = 0;
+  return FALSE;
+}
+
+static CoglHandle
+st_theme_node_render_gradient (StThemeNode *node)
+{
+  CoglHandle texture;
+  int radius[4], i;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  cairo_pattern_t *pattern;
+  gboolean round_border = FALSE;
+  ClutterColor border_color;
+  int border_width;
+  guint rowstride;
+  guchar *data;
+
+  rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, node->alloc_width);
+  data = g_new0 (guchar, node->alloc_height * rowstride);
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 node->alloc_width,
+                                                 node->alloc_height,
+                                                 rowstride);
+  cr = cairo_create (surface);
+
+  /* TODO - support non-uniform border colors and widths */
+  get_arbitrary_border (node, &border_width, &border_color);
+
+  for (i = 0; i < 4; i++)
+    {
+      radius[i] = st_theme_node_get_border_radius (node, i);
+      if (radius[i] > 0)
+        round_border = TRUE;
+    }
+
+  if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
+    pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
+  else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL)
+    pattern = cairo_pattern_create_linear (0, 0, node->alloc_width, 0);
+  else
+    {
+      gdouble cx, cy;
+
+      cx = node->alloc_width / 2.;
+      cy = node->alloc_height / 2.;
+      pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
+    }
+
+  cairo_pattern_add_color_stop_rgba (pattern, 0,
+                                     node->background_color.red / 255.,
+                                     node->background_color.green / 255.,
+                                     node->background_color.blue / 255.,
+                                     node->background_color.alpha / 255.);
+  cairo_pattern_add_color_stop_rgba (pattern, 1,
+                                     node->background_gradient_end.red / 255.,
+                                     node->background_gradient_end.green / 255.,
+                                     node->background_gradient_end.blue / 255.,
+                                     node->background_gradient_end.alpha / 255.);
+
+  if (round_border)
+    {
+      if (radius[ST_CORNER_TOPLEFT] > 0)
+        cairo_arc (cr,
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
+      else
+        cairo_move_to (cr, 0, 0);
+      cairo_line_to (cr, node->alloc_width - radius[ST_CORNER_TOPRIGHT], 0);
+      if (radius[ST_CORNER_TOPRIGHT] > 0)
+        cairo_arc (cr,
+                   node->alloc_width - radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
+      cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]);
+      if (radius[ST_CORNER_BOTTOMRIGHT])
+        cairo_arc (cr,
+                   node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
+                   node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
+                   radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
+      cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height);
+      if (radius[ST_CORNER_BOTTOMLEFT])
+        cairo_arc (cr,
+                   radius[ST_CORNER_BOTTOMLEFT],
+                   node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
+                   radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
+      cairo_close_path (cr);
+    }
+  else
+    cairo_rectangle (cr, 0, 0, node->alloc_width, node->alloc_height);
+
+  if (border_width > 0)
+    {
+      cairo_path_t *path;
+
+      path = cairo_copy_path (cr);
+      cairo_set_source_rgba (cr,
+                             border_color.red / 255.,
+                             border_color.green / 255.,
+                             border_color.blue / 255.,
+                             border_color.alpha / 255.);
+      cairo_fill (cr);
+
+      cairo_translate (cr, border_width, border_width);
+      cairo_scale (cr,
+                   (gdouble)(node->alloc_width - 2 * border_width) / node->alloc_width,
+                   (gdouble)(node->alloc_height - 2 * border_width) / node->alloc_height);
+      cairo_append_path (cr, path);
+      cairo_path_destroy (path);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_fill (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  texture = cogl_texture_new_from_data (node->alloc_width, node->alloc_height,
+                                        COGL_TEXTURE_NONE,
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                        COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+                                        COGL_PIXEL_FORMAT_ARGB_8888_PRE,
+#else
+                                        COGL_PIXEL_FORMAT_ANY,
+#error unknown endianness type
+#endif
+                                        COGL_PIXEL_FORMAT_ANY,
+                                        rowstride,
+                                        data);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+  g_free (data);
+
+  return texture;
+}
+
+void
+_st_theme_node_free_drawing_state (StThemeNode  *node)
+{
+  if (node->background_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->background_texture);
+  if (node->shadow_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->shadow_material);
+  if (node->border_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->border_texture);
+
+  _st_theme_node_init_drawing_state (node);
+}
+
+void
+_st_theme_node_init_drawing_state (StThemeNode *node)
+{
+  node->background_texture = COGL_INVALID_HANDLE;
+  node->shadow_material = COGL_INVALID_HANDLE;
+  node->border_texture = COGL_INVALID_HANDLE;
+}
+
+static void
+st_theme_node_render_resources (StThemeNode   *node,
+                                float          width,
+                                float          height)
+{
+  StTextureCache *texture_cache;
+  StBorderImage *border_image;
+  const char *background_image;
+
+  texture_cache = st_texture_cache_get_default ();
+
+  /* FIXME - need to separate this into things that need to be recomputed on
+   * geometry change versus things that can be cached regardless, such as
+   * a background image.
+   */
+  _st_theme_node_free_drawing_state (node);
+
+  node->alloc_width = width;
+  node->alloc_height = height;
+
+  _st_theme_node_ensure_background (node);
+  _st_theme_node_ensure_geometry (node);
+
+  /* Load referenced images from disk and draw anything we need with cairo now */
+
+  border_image = st_theme_node_get_border_image (node);
+  if (border_image)
+    {
+      const char *filename;
+
+      filename = st_border_image_get_filename (border_image);
+
+      node->border_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
+    }
+  else if (node->background_gradient_type != ST_GRADIENT_NONE)
+    {
+      node->border_texture = st_theme_node_render_gradient (node);
+    }
+
+  background_image = st_theme_node_get_background_image (node);
+  if (background_image != NULL)
+    {
+      StShadow *shadow_spec;
+
+      node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image);
+
+      shadow_spec = st_theme_node_get_shadow (node);
+      if (shadow_spec)
+        {
+          node->shadow_material = create_shadow_material (node, node->background_texture);
+        }
+    }
+
+  node->corner_texture = st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT);
+}
+
+static void
+paint_texture_with_opacity (CoglHandle       texture,
+                            ClutterActorBox *box,
+                            guint8           paint_opacity)
+{
+  if (paint_opacity == 255)
+    {
+      /* Minor: optimization use the default material if we can */
+      cogl_set_source_texture (texture);
+      cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
+      return;
+    }
+
+  CoglHandle material;
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, texture);
+  cogl_material_set_color4ub (material,
+                              paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+  cogl_set_source (material);
+  cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
+
+  cogl_handle_unref (material);
+}
+
+static void
+st_theme_node_paint_borders (StThemeNode           *node,
+                             const ClutterActorBox *box,
+                             guint8                 paint_opacity)
+
+{
+  float width, height;
+  int border_width;
+  int border_radius;
+  int max_width_radius;
+  ClutterColor border_color;
+  CoglHandle material;
+
+  width = box->x2 - box->x1;
+  height = box->y2 - box->y1;
+
+  get_arbitrary_border (node, &border_width, &border_color);
+  border_radius = node->border_radius[ST_CORNER_TOPLEFT];
+
+  max_width_radius = MAX(border_width, border_radius);
+
+  /* borders */
+  if (border_width > 0)
+    {
+      ClutterColor effective_border;
+
+      over (&border_color, &node->background_color, &effective_border);
+
+      cogl_set_source_color4ub (effective_border.red,
+                                effective_border.green,
+                                effective_border.blue,
+                                paint_opacity * effective_border.alpha / 255);
+
+      if (border_radius > 0) /* skip corners */
+        {
+          /* NORTH */
+          cogl_rectangle (max_width_radius, 0,
+                          width - max_width_radius, border_width);
+
+          /* EAST */
+          cogl_rectangle (width - border_width, max_width_radius,
+                          width, height - max_width_radius);
+
+          /* SOUTH */
+          cogl_rectangle (max_width_radius, height - border_width,
+                          width - max_width_radius, height);
+
+          /* WEST */
+          cogl_rectangle (0, max_width_radius,
+                          border_width, height - max_width_radius);
+        }
+      else /* include corners */
+        {
+          /* NORTH */
+          cogl_rectangle (0, 0,
+                          width, border_width);
+
+          /* EAST */
+          cogl_rectangle (width - border_width, border_width,
+                          width, height - border_width);
+
+          /* SOUTH */
+          cogl_rectangle (0, height - border_width,
+                          width, height);
+
+          /* WEST */
+          cogl_rectangle (0, border_width,
+                          border_width, height - border_width);
+        }
+    }
+
+  /* corners */
+  if (node->corner_texture != COGL_INVALID_HANDLE)
+    {
+      material = cogl_material_new ();
+      cogl_material_set_layer (material, 0, node->corner_texture);
+      cogl_material_set_color4ub (material,
+                                  paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+      cogl_set_source (material);
+
+      cogl_rectangle_with_texture_coords (0, 0, max_width_radius, max_width_radius, 0, 0, 0.5, 0.5);
+      cogl_rectangle_with_texture_coords (width - max_width_radius, 0, width, max_width_radius, 0.5, 0, 1, 0.5);
+      cogl_rectangle_with_texture_coords (width - max_width_radius, height - max_width_radius, width, height, 0.5, 0.5, 1, 1);
+      cogl_rectangle_with_texture_coords (0, height - max_width_radius, max_width_radius, height, 0, 0.5, 0.5, 1);
+
+      cogl_handle_unref (material);
+    }
+
+  /* background color */
+  cogl_set_source_color4ub (node->background_color.red,
+                            node->background_color.green,
+                            node->background_color.blue,
+                            paint_opacity * node->background_color.alpha / 255);
+
+  if (border_radius > border_width)
+    {
+      /* Once we've drawn the borders and corners, if the corners are bigger
+       * the the border width, the remaining area is shaped like
+       *
+       *  ########
+       * ##########
+       * ##########
+       *  ########
+       *
+       * We draw it in 3 pieces - first the top and bottom, then the main
+       * rectangle
+       */
+      cogl_rectangle (border_radius, border_width,
+                      width - border_radius, border_radius);
+      cogl_rectangle (border_radius, height - border_radius,
+                      width - border_radius, height - border_width);
+    }
+
+  cogl_rectangle (border_width, max_width_radius,
+                  width - border_width, height - max_width_radius);
+}
+
+static void
+st_theme_node_paint_sliced_border_image (StThemeNode           *node,
+                                         const ClutterActorBox *box,
+                                         guint8                 paint_opacity)
+{
+  gfloat ex, ey;
+  gfloat tx1, ty1, tx2, ty2;
+  gint border_left, border_right, border_top, border_bottom;
+  float img_width, img_height;
+  StBorderImage *border_image;
+  CoglHandle material;
+
+  border_image = st_theme_node_get_border_image (node);
+  g_assert (border_image != NULL);
+
+  st_border_image_get_borders (border_image,
+                               &border_left, &border_right, &border_top, &border_bottom);
+
+  img_width = cogl_texture_get_width (node->border_texture);
+  img_height = cogl_texture_get_height (node->border_texture);
+
+  tx1 = border_left / img_width;
+  tx2 = (img_width - border_right) / img_width;
+  ty1 = border_top / img_height;
+  ty2 = (img_height - border_bottom) / img_height;
+
+  ex = node->alloc_width - border_right;
+  if (ex < 0)
+    ex = border_right;           /* FIXME ? */
+
+  ey = node->alloc_height - border_bottom;
+  if (ey < 0)
+    ey = border_bottom;          /* FIXME ? */
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, node->border_texture);
+  cogl_material_set_color4ub (material,
+                              paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+  cogl_set_source (material);
+
+  {
+    GLfloat rectangles[] =
+    {
+      /* top left corner */
+      0, 0, border_left, border_top,
+      0.0, 0.0,
+      tx1, ty1,
+
+      /* top middle */
+      border_left, 0, ex, border_top,
+      tx1, 0.0,
+      tx2, ty1,
+
+      /* top right */
+      ex, 0, node->alloc_width, border_top,
+      tx2, 0.0,
+      1.0, ty1,
+
+      /* mid left */
+      0, border_top, border_left, ey,
+      0.0, ty1,
+      tx1, ty2,
+
+      /* center */
+      border_left, border_top, ex, ey,
+      tx1, ty1,
+      tx2, ty2,
+
+      /* mid right */
+      ex, border_top, node->alloc_width, ey,
+      tx2, ty1,
+      1.0, ty2,
+
+      /* bottom left */
+      0, ey, border_left, node->alloc_height,
+      0.0, ty2,
+      tx1, 1.0,
+
+      /* bottom center */
+      border_left, ey, ex, node->alloc_height,
+      tx1, ty2,
+      tx2, 1.0,
+
+      /* bottom right */
+      ex, ey, node->alloc_width, node->alloc_height,
+      tx2, ty2,
+      1.0, 1.0
+    };
+
+    cogl_rectangles_with_texture_coords (rectangles, 9);
+  }
+
+  cogl_handle_unref (material);
+}
+
+void
+st_theme_node_paint (StThemeNode           *node,
+                     const ClutterActorBox *box,
+                     guint8                 paint_opacity)
+{
+  float width, height;
+  ClutterActorBox allocation;
+
+  /* Some things take an ActorBox, some things just width/height */
+  width = box->x2 - box->x1;
+  height = box->y2 - box->y1;
+  allocation.x1 = allocation.y1 = 0;
+  allocation.x2 = width;
+  allocation.y2 = height;
+
+  if (node->alloc_width != width || node->alloc_height != height)
+    st_theme_node_render_resources (node, width, height);
+
+  /* Rough notes about the relationship of borders and backgrounds in CSS3;
+   * see http://www.w3.org/TR/css3-background/ for more accurate details.
+   *
+   * - Things are drawn in 4 layers, from the bottom:
+   *     Background color
+   *     Background image
+   *     Border color or border image
+   *     Content
+   * - The background color, gradient and image extend to and are clipped by
+   *   the edge of the border area, so will be rounded if the border is
+   *   rounded. (CSS3 background-clip property modifies this)
+   * - The border image replaces what would normally be drawn by the border
+   * - The border image is not clipped by a rounded border-radius
+   * - The border radius rounds the background even if the border is
+   *   zero width or a border image is being used.
+   *
+   * Deviations from the above as implemented here:
+   *  - Nonuniform border widths combined with a non-zero border radius result
+   *    in the border radius being ignored
+   *  - The combination of border image and a non-zero border radius is
+   *    not supported; the background color will be drawn with square
+   *    corners.
+   *  - The combination of border image and a background gradient is not
+   *    supported; the background will be drawn as a solid color
+   *  - The background image is drawn above the border color or image,
+   *    not below it.
+   *  - We don't clip the background image to the (rounded) border area.
+   *
+   * The first three allow us to always draw with no more than a single
+   * border_image and a single background image above it.
+   */
+
+  if (node->border_texture != COGL_INVALID_HANDLE)
+    {
+      /* Gradients and border images are mutually exclusive at this time */
+      if (node->background_gradient_type != ST_GRADIENT_NONE)
+        paint_texture_with_opacity (node->border_texture, &allocation, paint_opacity);
+      else
+        st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity);
+    }
+  else
+    st_theme_node_paint_borders (node, box, paint_opacity);
+
+  if (node->background_texture != COGL_INVALID_HANDLE)
+    {
+      ClutterActorBox background_box;
+
+      get_background_position (node, &allocation, &background_box);
+
+      /* CSS based drop shadows
+       *
+       * Drop shadows in ST are modelled after the CSS3 box-shadow property;
+       * see http://www.css3.info/preview/box-shadow/ for a detailed description.
+       *
+       * While the syntax of the property is mostly identical - we do not support
+       * multiple shadows and allow for a more liberal placement of the color
+       * parameter - its interpretation defers significantly in that the shadow's
+       * shape is not determined by the bounding box, but by the CSS background
+       * image (we could exend this in the future to take other CSS properties
+       * like boder and background color into account).
+       */
+      if (node->shadow_material != COGL_INVALID_HANDLE)
+        {
+          StShadow *shadow_spec;
+          ClutterActorBox shadow_box;
+
+          shadow_spec = node->shadow;
+
+          shadow_box.x1 = background_box.x1 + shadow_spec->xoffset - shadow_spec->blur;
+          shadow_box.y1 = background_box.y1 + shadow_spec->yoffset - shadow_spec->blur;
+          shadow_box.x2 = background_box.x2 + shadow_spec->xoffset + shadow_spec->blur;
+          shadow_box.y2 = background_box.y2 + shadow_spec->yoffset + shadow_spec->blur;
+
+          cogl_material_set_color4ub (node->shadow_material,
+                                      paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+          cogl_set_source (node->shadow_material);
+          cogl_rectangle_with_texture_coords (shadow_box.x1, shadow_box.y1, shadow_box.x2, shadow_box.y2,
+                                              0, 0, 1, 1);
+        }
+
+      paint_texture_with_opacity (node->background_texture, &background_box, paint_opacity);
+    }
+}
diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h
new file mode 100644
index 0000000..6074d5a
--- /dev/null
+++ b/src/st/st-theme-node-private.h
@@ -0,0 +1,86 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_THEME_NODE_PRIVATE_H__
+#define __ST_THEME_NODE_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+struct _StThemeNode {
+  GObject parent;
+
+  StThemeContext *context;
+  StThemeNode *parent_node;
+  StTheme *theme;
+
+  PangoFontDescription *font_desc;
+
+  ClutterColor background_color;
+  /* If gradient is set, then background_color is the gradient start */
+  StGradientType background_gradient_type;
+  ClutterColor background_gradient_end;
+
+  ClutterColor foreground_color;
+  ClutterColor border_color[4];
+
+  int border_width[4];
+  int border_radius[4];
+  guint padding[4];
+
+  int width;
+  int height;
+  int min_width;
+  int min_height;
+  int max_width;
+  int max_height;
+
+  char *background_image;
+  StBorderImage *border_image;
+  StShadow *shadow;
+
+  GType element_type;
+  char *element_id;
+  char *element_class;
+  char *pseudo_class;
+  char *inline_style;
+
+  CRDeclaration **properties;
+  int n_properties;
+
+  /* We hold onto these separately so we can destroy them on finalize */
+  CRDeclaration *inline_properties;
+
+  guint properties_computed : 1;
+  guint geometry_computed : 1;
+  guint background_computed : 1;
+  guint foreground_computed : 1;
+  guint border_image_computed : 1;
+  guint shadow_computed : 1;
+  guint link_type : 2;
+
+  /* Graphics state */
+  float alloc_width;
+  float alloc_height;
+
+  CoglHandle shadow_material;
+  CoglHandle background_texture;
+  CoglHandle border_texture;
+  CoglHandle corner_texture;
+};
+
+struct _StThemeNodeClass {
+  GObjectClass parent_class;
+
+};
+
+void _st_theme_node_ensure_background (StThemeNode *node);
+void _st_theme_node_ensure_geometry (StThemeNode *node);
+
+void _st_theme_node_init_drawing_state (StThemeNode *node);
+void _st_theme_node_free_drawing_state (StThemeNode *node);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_NODE_PRIVATE_H__ */
diff --git a/src/st/st-theme-node.c b/src/st/st-theme-node.c
index 5492a94..1b8b4d0 100644
--- a/src/st/st-theme-node.c
+++ b/src/st/st-theme-node.c
@@ -5,70 +5,12 @@
 
 #include "st-theme-private.h"
 #include "st-theme-context.h"
-#include "st-theme-node.h"
+#include "st-theme-node-private.h"
 
 static void st_theme_node_init               (StThemeNode          *node);
 static void st_theme_node_class_init         (StThemeNodeClass     *klass);
 static void st_theme_node_finalize           (GObject                 *object);
 
-struct _StThemeNode {
-  GObject parent;
-
-  StThemeContext *context;
-  StThemeNode *parent_node;
-  StTheme *theme;
-
-  PangoFontDescription *font_desc;
-
-  ClutterColor background_color;
-  /* If gradient is set, then background_color is the gradient start */
-  StGradientType background_gradient_type;
-  ClutterColor background_gradient_end;
-
-  ClutterColor foreground_color;
-  ClutterColor border_color[4];
-
-  int border_width[4];
-  int border_radius[4];
-  guint padding[4];
-
-  int width;
-  int height;
-  int min_width;
-  int min_height;
-  int max_width;
-  int max_height;
-
-  char *background_image;
-  StBorderImage *border_image;
-  StShadow *shadow;
-
-  GType element_type;
-  char *element_id;
-  char *element_class;
-  char *pseudo_class;
-  char *inline_style;
-
-  CRDeclaration **properties;
-  int n_properties;
-
-  /* We hold onto these separately so we can destroy them on finalize */
-  CRDeclaration *inline_properties;
-
-  guint properties_computed : 1;
-  guint geometry_computed : 1;
-  guint background_computed : 1;
-  guint foreground_computed : 1;
-  guint border_image_computed : 1;
-  guint shadow_computed : 1;
-  guint link_type : 2;
-};
-
-struct _StThemeNodeClass {
-  GObjectClass parent_class;
-
-};
-
 static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff };
 static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 };
 
@@ -77,6 +19,7 @@ G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT)
 static void
 st_theme_node_init (StThemeNode *node)
 {
+  _st_theme_node_init_drawing_state (node);
 }
 
 static void
@@ -131,6 +74,8 @@ st_theme_node_finalize (GObject *object)
   if (node->background_image)
     g_free (node->background_image);
 
+  _st_theme_node_free_drawing_state (node);
+
   G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
 }
 
@@ -259,7 +204,7 @@ st_theme_node_get_pseudo_class (StThemeNode *node)
   return node->pseudo_class;
 }
 
-static void
+void
 ensure_properties (StThemeNode *node)
 {
   if (!node->properties_computed)
@@ -1108,8 +1053,8 @@ do_size_property (StThemeNode   *node,
   get_length_from_term_int (node, decl->value, FALSE, node_value);
 }
 
-static void
-ensure_geometry (StThemeNode *node)
+void
+_st_theme_node_ensure_geometry (StThemeNode *node)
 {
   int i, j;
 
@@ -1192,7 +1137,7 @@ st_theme_node_get_border_width (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->border_width[side];
 }
@@ -1204,7 +1149,7 @@ st_theme_node_get_border_radius (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->border_radius[corner];
 }
@@ -1214,7 +1159,7 @@ st_theme_node_get_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->width;
 }
 
@@ -1223,7 +1168,7 @@ st_theme_node_get_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->height;
 }
 
@@ -1232,7 +1177,7 @@ st_theme_node_get_min_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->min_width;
 }
 
@@ -1241,7 +1186,7 @@ st_theme_node_get_min_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->min_height;
 }
 
@@ -1250,7 +1195,7 @@ st_theme_node_get_max_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->max_width;
 }
 
@@ -1259,7 +1204,7 @@ st_theme_node_get_max_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->max_height;
 }
 
@@ -1281,8 +1226,8 @@ get_background_color_from_term (StThemeNode  *node,
   return result;
 }
 
-static void
-ensure_background (StThemeNode *node)
+void
+_st_theme_node_ensure_background (StThemeNode *node)
 {
   int i;
 
@@ -1429,7 +1374,7 @@ st_theme_node_get_background_color (StThemeNode  *node,
 {
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   *color = node->background_color;
 }
@@ -1439,7 +1384,7 @@ st_theme_node_get_background_image (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   return node->background_image;
 }
@@ -1500,7 +1445,7 @@ st_theme_node_get_background_gradient (StThemeNode    *node,
 {
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   *type = node->background_gradient_type;
   if (*type != ST_GRADIENT_NONE)
@@ -1518,7 +1463,7 @@ st_theme_node_get_border_color (StThemeNode  *node,
   g_return_if_fail (ST_IS_THEME_NODE (node));
   g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   *color = node->border_color[side];
 }
@@ -1530,7 +1475,7 @@ st_theme_node_get_padding (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->padding[side];
 }
@@ -2353,7 +2298,7 @@ st_theme_node_adjust_preferred_width (StThemeNode  *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   width_inc = get_width_inc (node);
 
@@ -2420,7 +2365,7 @@ st_theme_node_adjust_preferred_height (StThemeNode  *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   height_inc = get_height_inc (node);
 
@@ -2461,7 +2406,7 @@ st_theme_node_get_content_box (StThemeNode           *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   avail_width = allocation->x2 - allocation->x1;
   avail_height = allocation->y2 - allocation->y1;
@@ -2501,8 +2446,8 @@ st_theme_node_geometry_equal (StThemeNode *node,
 {
   StSide side;
 
-  ensure_geometry (node);
-  ensure_geometry (other);
+  _st_theme_node_ensure_geometry (node);
+  _st_theme_node_ensure_geometry (other);
 
   for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
     {
diff --git a/src/st/st-theme-node.h b/src/st/st-theme-node.h
index 87c6884..2bc5e7a 100644
--- a/src/st/st-theme-node.h
+++ b/src/st/st-theme-node.h
@@ -171,6 +171,11 @@ void st_theme_node_get_content_box         (StThemeNode        *node,
 gboolean st_theme_node_geometry_equal (StThemeNode *node,
                                        StThemeNode *other);
 
+void st_theme_node_paint (StThemeNode            *node,
+                          const ClutterActorBox  *box,
+                          guint8                  paint_opacity);
+
+
 G_END_DECLS
 
 #endif /* __ST_THEME_NODE_H__ */
diff --git a/src/st/st-widget.c b/src/st/st-widget.c
index 92e2d9a..dba5a04 100644
--- a/src/st/st-widget.c
+++ b/src/st/st-widget.c
@@ -38,9 +38,7 @@
 
 #include "st-marshal.h"
 #include "st-private.h"
-#include "st-shadow-texture.h"
 #include "st-texture-cache.h"
-#include "st-texture-frame.h"
 #include "st-theme-context.h"
 #include "st-tooltip.h"
 
@@ -59,7 +57,6 @@ struct _StWidgetPrivate
 
   ClutterActor *border_image;
   ClutterActor *background_image;
-  ClutterActor *background_image_shadow;
   ClutterColor  bg_color;
 
   guint         border_width;
@@ -68,6 +65,7 @@ struct _StWidgetPrivate
   StGradientType bg_gradient_type;
   ClutterColor  bg_gradient_end;
 
+  ClutterActorBox background_allocation;
   gdouble       shadow_xoffset;
   gdouble       shadow_yoffset;
 
@@ -125,7 +123,6 @@ G_DEFINE_ABSTRACT_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
 
 static void st_widget_recompute_style (StWidget    *widget,
                                        StThemeNode *old_theme_node);
-static void st_widget_redraw_gradient (StWidget  *widget);
 
 static void
 st_widget_set_property (GObject      *gobject,
@@ -254,12 +251,6 @@ st_widget_dispose (GObject *gobject)
       priv->border_image = NULL;
     }
 
-  if (priv->background_image_shadow)
-    {
-      clutter_actor_unparent (priv->background_image_shadow);
-      priv->background_image_shadow = NULL;
-    }
-
   if (priv->theme_node)
     {
       g_object_unref (priv->theme_node);
@@ -295,18 +286,20 @@ st_widget_finalize (GObject *gobject)
   G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
 }
 
+
 static void
 st_widget_allocate (ClutterActor          *actor,
                     const ClutterActorBox *box,
                     ClutterAllocationFlags flags)
 {
-  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
+  StWidget *self = ST_WIDGET (actor);
+  StWidgetPrivate *priv = self->priv;
   StThemeNode *theme_node;
   ClutterActorClass *klass;
   ClutterGeometry area;
   ClutterVertex in_v, out_v;
 
-  theme_node = st_widget_get_theme_node ((StWidget*) actor);
+  theme_node = st_widget_get_theme_node (self);
 
   klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
   klass->allocate (actor, box, flags);
@@ -327,249 +320,20 @@ st_widget_allocate (ClutterActor          *actor,
 
       st_tooltip_set_tip_area (priv->tooltip, &area);
     }
-
-
-
-  if (priv->border_image && priv->bg_gradient_type == ST_GRADIENT_NONE)
-    {
-      ClutterActorBox frame_box;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
-                              &frame_box,
-                              flags);
-    }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      guint width,  old_width,
-            height, old_height;
-      ClutterActorBox frame_box;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      width = (guint)(0.5 + frame_box.x2);
-      height = (guint)(0.5 + frame_box.y2);
-
-      clutter_cairo_texture_get_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image),
-                                              &old_width, &old_height);
-
-      if (width > 0 && height > 0 &&
-          (old_width != width || old_height != height))
-        {
-
-          clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image),
-                                                  width, height);
-          st_widget_redraw_gradient ((StWidget*) actor);
-        }
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
-                              &frame_box,
-                              flags);
-    }
-
-  if (priv->background_image)
-    {
-      ClutterActorBox frame_box;
-      gfloat w, h;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h);
-
-      /* scale the background into the allocated bounds */
-      if (w > frame_box.x2 || h > frame_box.y2)
-        {
-          gint new_h, new_w, offset;
-          gint box_w, box_h;
-
-          box_w = (int) frame_box.x2;
-          box_h = (int) frame_box.y2;
-
-          /* scale to fit */
-          new_h = (int)((h / w) * ((gfloat) box_w));
-          new_w = (int)((w / h) * ((gfloat) box_h));
-
-          if (new_h > box_h)
-            {
-              /* center for new width */
-              offset = ((box_w) - new_w) * 0.5;
-              frame_box.x1 = offset;
-              frame_box.x2 = offset + new_w;
-
-              frame_box.y2 = box_h;
-            }
-          else
-            {
-              /* center for new height */
-              offset = ((box_h) - new_h) * 0.5;
-              frame_box.y1 = offset;
-              frame_box.y2 = offset + new_h;
-
-              frame_box.x2 = box_w;
-            }
-
-        }
-      else
-        {
-          /* center the background on the widget */
-          frame_box.x1 = (int)(((box->x2 - box->x1) / 2) - (w / 2));
-          frame_box.y1 = (int)(((box->y2 - box->y1) / 2) - (h / 2));
-          frame_box.x2 = frame_box.x1 + w;
-          frame_box.y2 = frame_box.y1 + h;
-        }
-
-        if (priv->background_image_shadow)
-          {
-            StShadowTexture *shadow;
-            ClutterActorBox  shadow_box;
-
-            shadow_box.x1 = frame_box.x1 + priv->shadow_xoffset;
-            shadow_box.y1 = frame_box.y1 + priv->shadow_yoffset;
-            shadow_box.x2 = frame_box.x2 + priv->shadow_xoffset;
-            shadow_box.y2 = frame_box.y2 + priv->shadow_yoffset;
-
-            /* The shadow texture is larger than the original image due
-               to blurring, so we let it adjust its size.
-               When the original image has been scaled, this will change
-               the effective blur radius - we ignore this for now. */
-            shadow = ST_SHADOW_TEXTURE (priv->background_image_shadow);
-            st_shadow_texture_adjust_allocation (shadow, &shadow_box);
-
-            clutter_actor_allocate (priv->background_image_shadow,
-                                    &shadow_box, flags);
-          }
-
-
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
-                              &frame_box,
-                              flags);
-    }
 }
 
 static void
-st_widget_real_draw_background (StWidget *self)
+st_widget_paint (ClutterActor *actor)
 {
-  StWidgetPrivate *priv = self->priv;
-  ClutterActor *actor = CLUTTER_ACTOR (self);
-  ClutterActorBox allocation = { 0, };
-  gfloat w, h;
-  guint8 opacity;
-
-  clutter_actor_get_allocation_box (actor, &allocation);
-  w = allocation.x2 - allocation.x1;
-  h = allocation.y2 - allocation.y1;
-
-  opacity = clutter_actor_get_paint_opacity (actor);
-
-  /* Default implementation just draws the background
-   * colour and the image on top
-   */
-  if (priv->draw_bg_color)
-    {
-      ClutterColor bg_color = priv->bg_color;
-
-      bg_color.alpha = opacity * bg_color.alpha / 255;
-
-      cogl_set_source_color4ub (bg_color.red,
-                                bg_color.green,
-                                bg_color.blue,
-                                bg_color.alpha);
-      cogl_rectangle (0, 0, w, h);
-    }
-
-  if (priv->draw_border_internal)
-    {
-      StThemeNode *node = st_widget_get_theme_node (self);
-      int side;
-      double border_top, border_right, border_bottom, border_left;
-
-      border_top = st_theme_node_get_border_width (node, ST_SIDE_TOP);
-      border_right = st_theme_node_get_border_width (node, ST_SIDE_RIGHT);
-      border_bottom = st_theme_node_get_border_width (node, ST_SIDE_BOTTOM);
-      border_left = st_theme_node_get_border_width (node, ST_SIDE_LEFT);
-
-      for (side = 0; side < 4; side++)
-        {
-          ClutterColor color;
-
-          switch (side)
-          {
-            case ST_SIDE_TOP:
-              if (border_top <= 0)
-                continue;
-              break;
-            case ST_SIDE_RIGHT:
-              if (border_right <= 0)
-                continue;
-              break;
-            case ST_SIDE_BOTTOM:
-              if (border_bottom <= 0)
-                continue;
-              break;
-            case ST_SIDE_LEFT:
-              if (border_left <= 0)
-                continue;
-              break;
-          }
-
-          st_theme_node_get_border_color (node, side, &color);
-
-          color.alpha = (color.alpha * opacity) / 0xff;
-
-          cogl_set_source_color4ub (color.red,
-                                    color.green,
-                                    color.blue,
-                                    color.alpha);
-
-          /* Note top and bottom extend to the ends, left/right
-           * are constrained by them.  See comment above about CSS
-           * conformance.
-           */
-          switch (side)
-          {
-            case ST_SIDE_TOP:
-              cogl_rectangle (0, 0,
-                              w, border_top);
-              break;
-            case ST_SIDE_RIGHT:
-              cogl_rectangle (w - border_right, border_top,
-                              w, h - border_bottom);
-              break;
-            case ST_SIDE_BOTTOM:
-              cogl_rectangle (0, h - border_bottom,
-                              w, h);
-              break;
-            case ST_SIDE_LEFT:
-              cogl_rectangle (0, border_top,
-                              border_left, h - border_bottom);
-              break;
-            }
-        }
-    }
-
-  if (priv->border_image)
-    clutter_actor_paint (priv->border_image);
-}
+  StWidget *self = ST_WIDGET (actor);
+  StThemeNode *theme_node;
+  ClutterActorBox allocation;
 
-static void
-st_widget_paint (ClutterActor *self)
-{
-  StWidgetPrivate *priv = ST_WIDGET (self)->priv;
+  theme_node = st_widget_get_theme_node (self);
 
-  st_widget_real_draw_background (ST_WIDGET (self));
+  clutter_actor_get_allocation_box (actor, &allocation);
 
-  if (priv->background_image != NULL)
-    {
-      if (priv->background_image_shadow)
-        clutter_actor_paint (priv->background_image_shadow);
-      clutter_actor_paint (priv->background_image);
-    }
+  st_theme_node_paint (theme_node, &allocation, clutter_actor_get_paint_opacity (actor));
 }
 
 static void
@@ -599,9 +363,6 @@ st_widget_map (ClutterActor *actor)
 
   st_widget_ensure_style ((StWidget*) actor);
 
-  if (priv->background_image_shadow)
-    clutter_actor_map (priv->background_image_shadow);
-
   if (priv->border_image)
     clutter_actor_map (priv->border_image);
 
@@ -619,9 +380,6 @@ st_widget_unmap (ClutterActor *actor)
 
   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
 
-  if (priv->background_image_shadow)
-    clutter_actor_unmap (priv->background_image_shadow);
-
   if (priv->border_image)
     clutter_actor_unmap (priv->border_image);
 
@@ -632,125 +390,6 @@ st_widget_unmap (ClutterActor *actor)
     clutter_actor_unmap ((ClutterActor *) priv->tooltip);
 }
 
-static void
-st_widget_redraw_gradient (StWidget  *widget)
-{
-  ClutterCairoTexture *texture;
-  ClutterColor *start, *end;
-  StWidgetPrivate *priv;
-  guint width, height;
-  guint radius[4], i;
-  cairo_t *cr;
-  cairo_pattern_t *pattern;
-  gboolean round_border = FALSE;
-
-  if (widget->priv->bg_gradient_type == ST_GRADIENT_NONE)
-    return;
-
-  texture = CLUTTER_CAIRO_TEXTURE (widget->priv->border_image);
-  priv  = widget->priv;
-  start = &widget->priv->bg_color;
-  end   = &widget->priv->bg_gradient_end;
-
-  for (i = 0; i < 4; i++)
-    {
-      radius[i] = st_theme_node_get_border_radius (priv->theme_node, i);
-      if (radius[i] > 0)
-        round_border = TRUE;
-    }
-
-  clutter_cairo_texture_get_surface_size (texture, &width, &height);
-  clutter_cairo_texture_clear (texture);
-  cr = clutter_cairo_texture_create (texture);
-
-  if (priv->bg_gradient_type == ST_GRADIENT_VERTICAL)
-    pattern = cairo_pattern_create_linear (0, 0, 0, height);
-  else if (priv->bg_gradient_type == ST_GRADIENT_HORIZONTAL)
-    pattern = cairo_pattern_create_linear (0, 0, width, 0);
-  else
-    {
-      gdouble cx, cy;
-
-      cx = width / 2.;
-      cy = height / 2.;
-      pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
-    }
-
-  cairo_pattern_add_color_stop_rgba (pattern, 0,
-                                     start->red / 255.,
-                                     start->green / 255.,
-                                     start->blue / 255.,
-                                     start->alpha / 255.);
-  cairo_pattern_add_color_stop_rgba (pattern, 1,
-                                     end->red / 255.,
-                                     end->green / 255.,
-                                     end->blue / 255.,
-                                     end->alpha / 255.);
-
-  if (round_border)
-    {
-      if (radius[ST_CORNER_TOPLEFT] > 0)
-        cairo_arc (cr,
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
-      else
-        cairo_move_to (cr, 0, 0);
-      cairo_line_to (cr, width - radius[ST_CORNER_TOPRIGHT], 0);
-      if (radius[ST_CORNER_TOPRIGHT] > 0)
-        cairo_arc (cr,
-                   width - radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
-      cairo_line_to (cr, width, height - radius[ST_CORNER_BOTTOMRIGHT]);
-      if (radius[ST_CORNER_BOTTOMRIGHT])
-        cairo_arc (cr,
-                   width - radius[ST_CORNER_BOTTOMRIGHT],
-                   height - radius[ST_CORNER_BOTTOMRIGHT],
-                   radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
-      cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], height);
-      if (radius[ST_CORNER_BOTTOMLEFT])
-        cairo_arc (cr,
-                   radius[ST_CORNER_BOTTOMLEFT],
-                   height - radius[ST_CORNER_BOTTOMLEFT],
-                   radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
-      cairo_close_path (cr);
-    }
-  else
-    cairo_rectangle (cr, 0, 0, width, height);
-
-  if (priv->border_width > 0)
-    {
-      guint8 opacity;
-      gdouble effective_alpha;
-      cairo_path_t *path;
-
-      path = cairo_copy_path (cr);
-      opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget));
-      effective_alpha = priv->border_color.alpha * opacity / (255. * 255.);
-
-      cairo_set_source_rgba (cr,
-                             priv->border_color.red / 255.,
-                             priv->border_color.green / 255.,
-                             priv->border_color.blue / 255.,
-                             effective_alpha);
-      cairo_fill (cr);
-
-      cairo_translate (cr, priv->border_width, priv->border_width);
-      cairo_scale (cr,
-                   (gdouble)(width - 2 * priv->border_width) / width,
-                   (gdouble)(height - 2 * priv->border_width) / height);
-      cairo_append_path (cr, path);
-      cairo_path_destroy (path);
-    }
-
-  cairo_set_source (cr, pattern);
-  cairo_fill (cr);
-
-  cairo_pattern_destroy (pattern);
-  cairo_destroy (cr);
-}
-
 static void notify_children_of_style_change (ClutterContainer *container);
 
 static void
@@ -776,270 +415,12 @@ static void
 st_widget_real_style_changed (StWidget *self)
 {
   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
-  StThemeNode *theme_node;
-  StBorderImage *border_image;
-  StShadow *shadow;
-  StTextureCache *texture_cache;
-  ClutterTexture *texture;
-  const char *bg_file = NULL;
-  gboolean relayout_needed = FALSE;
-  gboolean has_changed = FALSE;
-  ClutterColor color;
-  guint border_radius = 0;
-  StGradientType gradient;
-  ClutterColor gradient_end;
-  StSide side;
-  StCorner corner;
-  gboolean uniform_border_width;
 
   /* application has request this widget is not stylable */
   if (!priv->is_stylable)
     return;
 
-  theme_node = st_widget_get_theme_node (self);
-
-  st_theme_node_get_background_gradient (theme_node, &gradient, &color, &gradient_end);
-
-  if (gradient == ST_GRADIENT_NONE)
-    {
-      st_theme_node_get_background_color (theme_node, &color);
-      if (gradient != priv->bg_gradient_type ||
-          !clutter_color_equal (&color, &priv->bg_color))
-        {
-          priv->bg_gradient_type = gradient;
-          priv->bg_color = color;
-          priv->draw_bg_color = color.alpha != 0;
-          has_changed = TRUE;
-        }
-    }
-  else if (gradient != priv->bg_gradient_type ||
-           !clutter_color_equal (&color, &priv->bg_color) ||
-           !clutter_color_equal (&gradient_end, &priv->bg_gradient_end))
-    {
-      priv->bg_gradient_type = gradient;
-      priv->bg_color = color;
-      priv->bg_gradient_end = gradient_end;
-      priv->draw_bg_color = TRUE;
-      has_changed = TRUE;
-    }
-
-  if (priv->background_image_shadow)
-    {
-      clutter_actor_unparent (priv->background_image_shadow);
-      priv->background_image_shadow = NULL;
-    }
-
-  if (priv->border_image)
-    {
-      clutter_actor_unparent (priv->border_image);
-      priv->border_image = NULL;
-    }
-
-  if (priv->background_image)
-    {
-      clutter_actor_unparent (priv->background_image);
-      priv->background_image = NULL;
-    }
-
-  texture_cache = st_texture_cache_get_default ();
-
-
-  /* Rough notes about the relationship of borders and backgrounds in CSS3;
-   * see http://www.w3.org/TR/css3-background/ for more accurate details.
-   *
-   * - Things are drawn in 4 layers, from the bottom:
-   *     Background color
-   *     Background image
-   *     Border color or border image
-   *     Content
-   * - The background color, gradient and image extend to and are clipped by
-   *   the edge of the border area, so will be rounded if the border is
-   *   rounded. (CSS3 background-clip property modifies this)
-   * - The border image replaces what would normally be drawn by the border
-   * - The border image is not clipped by a rounded border-radius
-   * - The border radius rounds the background even if the border is
-   *   zero width or a border image is being used.
-   *
-   * Deviations from the above as implemented here:
-   *  - Nonuniform border widths combined with a non-zero border radius result
-   *    in the border radius being ignored
-   *  - The combination of border image and a non-zero border radius is
-   *    not supported; the background color will be drawn with square
-   *    corners.
-   *  - The combination of border image and a background gradient is not
-   *    supported; the background will be drawn as a solid color
-   *  - The background image is drawn above the border color or image,
-   *    not below it.
-   *  - We don't clip the background image to the (rounded) border area.
-   *
-   * The first three allow us to always draw with no more than a single
-   * border_image and a single background image above it.
-   */
-
-  /* Check whether all border widths are the same.  Also, acquire the
-   * first nonzero border width as well as the border color.
-   */
-  uniform_border_width = TRUE;
-  priv->border_width = st_theme_node_get_border_width (theme_node,
-                                                       ST_SIDE_TOP);
-  if (priv->border_width > 0.5)
-    priv->border_width = (int)(0.5 + priv->border_width);
-  for (side = 0; side < 4; side++)
-    {
-      double width = st_theme_node_get_border_width (theme_node, side);
-      if (width > 0.5)
-        width = (int)(0.5 + width);
-      if (width > 0)
-        {
-          priv->border_width = width;
-          st_theme_node_get_border_color (theme_node,
-                                          side, &priv->border_color);
-        }
-      if ((int)width != priv->border_width)
-        {
-          uniform_border_width = FALSE;
-          break;
-        }
-    }
-
-  /* Pick the first nonzero border radius, but only if we have a uniform border. */
-  if (uniform_border_width)
-    {
-      for (corner = 0; corner < 4; corner++)
-        {
-          double radius = st_theme_node_get_border_radius (theme_node, corner);
-          if (radius > 0.5)
-            {
-              border_radius = (int)(0.5 + radius);
-              break;
-            }
-        }
-    }
-
-  border_image = st_theme_node_get_border_image (theme_node);
-  if (border_image)
-    {
-      const char *filename;
-      gint border_left, border_right, border_top, border_bottom;
-      gint width, height;
-
-      filename = st_border_image_get_filename (border_image);
-
-      /* `border-image' takes precedence over `background-image'.
-       * Firefox lets the background-image shine thru when border-image has
-       * alpha an channel, maybe that would be an option for the future. */
-      texture = (ClutterTexture*) st_texture_cache_load_file_simple (texture_cache,
-                                                                     filename);
-
-      clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
-                                     &width, &height);
-
-      st_border_image_get_borders (border_image,
-                                   &border_left, &border_right, &border_top, &border_bottom);
-
-      priv->border_image = st_texture_frame_new (texture,
-                                                 border_top,
-                                                 border_right,
-                                                 border_bottom,
-                                                 border_left);
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      priv->draw_border_internal = FALSE;
-      priv->draw_bg_color = FALSE;
-      texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL);
-      priv->border_image = CLUTTER_ACTOR (texture);
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (border_radius > 0)
-    {
-      priv->draw_border_internal = FALSE;
-      priv->draw_bg_color = FALSE;
-      priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
-					 "color", &priv->bg_color,
-					 "border-width", priv->border_width,
-					 "border-color", &priv->border_color,
-					 "corner-radius", border_radius,
-					 NULL);
-
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->border_width > 0 && priv->border_color.alpha != 0)
-    {
-      priv->draw_bg_color = TRUE;
-      priv->draw_border_internal = TRUE;
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->draw_border_internal)
-    {
-      priv->draw_border_internal = FALSE;
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-
-  bg_file = st_theme_node_get_background_image (theme_node);
-  if (bg_file != NULL)
-    {
-      priv->background_image = st_texture_cache_load_file_simple (texture_cache, bg_file);
-      clutter_actor_set_parent (priv->background_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-
-  /* CSS based drop shadows
-   *
-   * Drop shadows in ST are modelled after the CSS3 box-shadow property;
-   * see http://www.css3.info/preview/box-shadow/ for a detailed description.
-   *
-   * While the syntax of the property is mostly identical - we do not support
-   * multiple shadows and allow for a more liberal placement of the color
-   * parameter - its interpretation defers significantly in that the shadow's
-   * shape is not determined by the bounding box, but by the CSS background
-   * image (we could exend this in the future to take other CSS properties
-   * like boder and background color into account).
-   */
-  shadow = st_theme_node_get_shadow (theme_node);
-  if (shadow != NULL)
-    {
-      priv->shadow_xoffset = shadow->xoffset;
-      priv->shadow_yoffset = shadow->yoffset;
-
-      if (priv->background_image)
-        {
-          priv->background_image_shadow =
-              st_shadow_texture_new (priv->background_image,
-                                     &shadow->color,
-                                     shadow->blur);
-
-          clutter_actor_set_parent (priv->background_image_shadow,
-                                    CLUTTER_ACTOR (self));
-          has_changed = TRUE;
-          relayout_needed = TRUE;
-        }
-    }
-
-  /* If there are any properties above that need to cause a relayout they
-   * should set this flag.
-   */
-  if (has_changed)
-    {
-      if (relayout_needed)
-        clutter_actor_queue_relayout ((ClutterActor *) self);
-      else
-        clutter_actor_queue_redraw ((ClutterActor *) self);
-    }
+  clutter_actor_queue_redraw ((ClutterActor *) self);
 
   if (CLUTTER_IS_CONTAINER (self))
     notify_children_of_style_change ((ClutterContainer *)self);



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