]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkthemingengine.c
themingengine: Remove unnecessary optimization
[~andy/gtk] / gtk / gtkthemingengine.c
index a48787c7334321099e8a53a2bfd86a33590dd0b4..6c5d5f0318ab48afd724ec813a8a65694bebefbc 100644 (file)
 #include <gtk/gtkintl.h>
 
 #include "gtkprivate.h"
-#include "gtk9slice.h"
+#include "gtkborderimageprivate.h"
 #include "gtkpango.h"
+#include "gtkshadowprivate.h"
+#include "gtkcsstypesprivate.h"
+#include "gtkthemingengineprivate.h"
 
 /**
  * SECTION:gtkthemingengine
@@ -175,6 +178,11 @@ static void gtk_theming_engine_render_activity  (GtkThemingEngine *engine,
 static GdkPixbuf * gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
                                                           const GtkIconSource *source,
                                                           GtkIconSize          size);
+static void gtk_theming_engine_render_icon (GtkThemingEngine *engine,
+                                            cairo_t *cr,
+                                           GdkPixbuf *pixbuf,
+                                            gdouble x,
+                                            gdouble y);
 
 G_DEFINE_TYPE (GtkThemingEngine, gtk_theming_engine, G_TYPE_OBJECT)
 
@@ -213,6 +221,7 @@ gtk_theming_engine_class_init (GtkThemingEngineClass *klass)
   object_class->set_property = gtk_theming_engine_impl_set_property;
   object_class->get_property = gtk_theming_engine_impl_get_property;
 
+  klass->render_icon = gtk_theming_engine_render_icon;
   klass->render_check = gtk_theming_engine_render_check;
   klass->render_option = gtk_theming_engine_render_option;
   klass->render_arrow = gtk_theming_engine_render_arrow;
@@ -629,7 +638,7 @@ gtk_theming_engine_state_is_running (GtkThemingEngine *engine,
  *
  * Since: 3.0
  **/
-G_CONST_RETURN GtkWidgetPath *
+const GtkWidgetPath *
 gtk_theming_engine_get_path (GtkThemingEngine *engine)
 {
   GtkThemingEnginePrivate *priv;
@@ -1052,26 +1061,26 @@ gtk_theming_engine_render_check (GtkThemingEngine *engine,
                                  gdouble           width,
                                  gdouble           height)
 {
-  GdkRGBA *fg_color, *bg_color, *border_color;
+  GdkRGBA fg_color, bg_color;
   GtkStateFlags flags;
   gint exterior_size, interior_size, thickness, pad;
   GtkBorderStyle border_style;
-  GtkBorder *border;
+  GtkBorder border;
   gint border_width;
 
   flags = gtk_theming_engine_get_state (engine);
   cairo_save (cr);
 
+  gtk_theming_engine_get_color (engine, flags, &fg_color);
+  gtk_theming_engine_get_background_color (engine, flags, &bg_color);
+  gtk_theming_engine_get_border (engine, flags, &border);
+
   gtk_theming_engine_get (engine, flags,
-                          "color", &fg_color,
-                          "background-color", &bg_color,
-                          "border-color", &border_color,
                           "border-style", &border_style,
-                          "border-width", &border,
                           NULL);
 
-  border_width = MIN (MIN (border->top, border->bottom),
-                      MIN (border->left, border->right));
+  border_width = MIN (MIN (border.top, border.bottom),
+                      MIN (border.left, border.right));
   exterior_size = MIN (width, height);
 
   if (exterior_size % 2 == 0) /* Ensure odd */
@@ -1093,21 +1102,20 @@ gtk_theming_engine_render_check (GtkThemingEngine *engine,
 
   if (border_style == GTK_BORDER_STYLE_SOLID)
     {
+      GdkRGBA border_color;
+
       cairo_set_line_width (cr, border_width);
+      gtk_theming_engine_get_border_color (engine, flags, &border_color);
 
       cairo_rectangle (cr, x + 0.5, y + 0.5, exterior_size - 1, exterior_size - 1);
-      gdk_cairo_set_source_rgba (cr, bg_color);
+      gdk_cairo_set_source_rgba (cr, &bg_color);
       cairo_fill_preserve (cr);
 
-      if (border_color)
-        gdk_cairo_set_source_rgba (cr, border_color);
-      else
-        gdk_cairo_set_source_rgba (cr, fg_color);
-
+      gdk_cairo_set_source_rgba (cr, &border_color);
       cairo_stroke (cr);
     }
 
-  gdk_cairo_set_source_rgba (cr, fg_color);
+  gdk_cairo_set_source_rgba (cr, &fg_color);
 
   if (flags & GTK_STATE_FLAG_INCONSISTENT)
     {
@@ -1164,11 +1172,6 @@ gtk_theming_engine_render_check (GtkThemingEngine *engine,
     }
 
   cairo_restore (cr);
-
-  gdk_rgba_free (fg_color);
-  gdk_rgba_free (bg_color);
-  gdk_rgba_free (border_color);
-  gtk_border_free (border);
 }
 
 static void
@@ -1180,26 +1183,26 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine,
                                   gdouble           height)
 {
   GtkStateFlags flags;
-  GdkRGBA *fg_color, *bg_color, *border_color;
+  GdkRGBA fg_color, bg_color;
   gint exterior_size, interior_size, pad, thickness, border_width;
   GtkBorderStyle border_style;
-  GtkBorder *border;
+  GtkBorder border;
 
   flags = gtk_theming_engine_get_state (engine);
 
   cairo_save (cr);
 
+  gtk_theming_engine_get_color (engine, flags, &fg_color);
+  gtk_theming_engine_get_background_color (engine, flags, &bg_color);
+  gtk_theming_engine_get_border (engine, flags, &border);
+
   gtk_theming_engine_get (engine, flags,
-                          "color", &fg_color,
-                          "background-color", &bg_color,
-                          "border-color", &border_color,
                           "border-style", &border_style,
-                          "border-width", &border,
                           NULL);
 
   exterior_size = MIN (width, height);
-  border_width = MIN (MIN (border->top, border->bottom),
-                      MIN (border->left, border->right));
+  border_width = MIN (MIN (border.top, border.bottom),
+                      MIN (border.left, border.right));
 
   if (exterior_size % 2 == 0) /* Ensure odd */
     exterior_size -= 1;
@@ -1209,7 +1212,10 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine,
 
   if (border_style == GTK_BORDER_STYLE_SOLID)
     {
+      GdkRGBA border_color;
+
       cairo_set_line_width (cr, border_width);
+      gtk_theming_engine_get_border_color (engine, flags, &border_color);
 
       cairo_new_sub_path (cr);
       cairo_arc (cr,
@@ -1218,18 +1224,14 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine,
                  (exterior_size - 1) / 2.,
                  0, 2 * G_PI);
 
-      gdk_cairo_set_source_rgba (cr, bg_color);
+      gdk_cairo_set_source_rgba (cr, &bg_color);
       cairo_fill_preserve (cr);
 
-      if (border_color)
-        gdk_cairo_set_source_rgba (cr, border_color);
-      else
-        gdk_cairo_set_source_rgba (cr, fg_color);
-
+      gdk_cairo_set_source_rgba (cr, &border_color);
       cairo_stroke (cr);
     }
 
-  gdk_cairo_set_source_rgba (cr, fg_color);
+  gdk_cairo_set_source_rgba (cr, &fg_color);
 
   /* FIXME: thickness */
   thickness = 1;
@@ -1277,11 +1279,6 @@ gtk_theming_engine_render_option (GtkThemingEngine *engine,
     }
 
   cairo_restore (cr);
-
-  gdk_rgba_free (fg_color);
-  gdk_rgba_free (bg_color);
-  gdk_rgba_free (border_color);
-  gtk_border_free (border);
 }
 
 static void
@@ -1313,15 +1310,12 @@ gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
                                  gdouble           size)
 {
   GtkStateFlags flags;
-  GdkRGBA *fg_color;
+  GdkRGBA fg_color;
 
   cairo_save (cr);
 
   flags = gtk_theming_engine_get_state (engine);
-
-  gtk_theming_engine_get (engine, flags,
-                          "color", &fg_color,
-                          NULL);
+  gtk_theming_engine_get_color (engine, flags, &fg_color);
 
   if (flags & GTK_STATE_FLAG_INSENSITIVE)
     {
@@ -1331,12 +1325,10 @@ gtk_theming_engine_render_arrow (GtkThemingEngine *engine,
     }
 
   add_path_arrow (cr, angle, x, y, size);
-  gdk_cairo_set_source_rgba (cr, fg_color);
+  gdk_cairo_set_source_rgba (cr, &fg_color);
   cairo_fill (cr);
 
   cairo_restore (cr);
-
-  gdk_rgba_free (fg_color);
 }
 
 static void
@@ -1380,122 +1372,129 @@ color_shade (const GdkRGBA *color,
 }
 
 static void
-_cairo_round_rectangle_sides (cairo_t          *cr,
-                              gdouble           radius,
-                              gdouble           x,
-                              gdouble           y,
-                              gdouble           width,
-                              gdouble           height,
-                              guint             sides,
-                              GtkJunctionSides  junction)
+_cairo_ellipsis (cairo_t *cr,
+                double xc, double yc,
+                double xradius, double yradius,
+                double angle1, double angle2)
 {
-  radius = CLAMP (radius, 0, MIN (width / 2, height / 2));
+  if (xradius <= 0.0 || yradius <= 0.0)
+    {
+      /* stolen from cairo sources */
+      cairo_line_to (cr, xc, yc); /* might become a move_to */
+      cairo_line_to (cr, xc, yc);
+      return;
+    }
+
+  cairo_save (cr);
+  cairo_translate (cr, xc, yc);
+  cairo_scale (cr, xradius, yradius);
+  cairo_arc (cr, 0, 0, 1.0, angle1, angle2);
+  cairo_restore (cr);
+}
+
+static void
+_cairo_round_rectangle_sides (cairo_t                  *cr,
+                              const GtkCssBorderRadius *border_radius,
+                              gdouble                   x,
+                              gdouble                   y,
+                              gdouble                   width,
+                              gdouble                   height,
+                              guint                     sides)
+{
+  cairo_new_sub_path (cr);
 
   if (sides & SIDE_RIGHT)
     {
-      if (radius == 0 ||
-          (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
-        cairo_move_to (cr, x + width, y);
-      else
-        {
-          cairo_new_sub_path (cr);
-          cairo_arc (cr, x + width - radius, y + radius, radius, - G_PI / 4, 0);
-        }
-
-      if (radius == 0 ||
-          (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
-        cairo_line_to (cr, x + width, y + height);
-      else
-        cairo_arc (cr, x + width - radius, y + height - radius, radius, 0, G_PI / 4);
+      _cairo_ellipsis (cr, 
+                       x + width - border_radius->top_right.horizontal,
+                       y + border_radius->top_right.vertical,
+                       border_radius->top_right.horizontal,
+                       border_radius->top_right.vertical,
+                       - G_PI / 4, 0);
+      _cairo_ellipsis (cr,
+                       x + width - border_radius->bottom_right.horizontal,
+                       y + height - border_radius->bottom_right.vertical,
+                       border_radius->bottom_right.horizontal,
+                       border_radius->bottom_right.vertical,
+                       0, G_PI / 4);
     }
 
   if (sides & SIDE_BOTTOM)
     {
-      if (radius != 0 &&
-          ! (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
-        {
-          if ((sides & SIDE_RIGHT) == 0)
-            cairo_new_sub_path (cr);
-
-          cairo_arc (cr, x + width - radius, y + height - radius, radius, G_PI / 4, G_PI / 2);
-        }
-      else if ((sides & SIDE_RIGHT) == 0)
-        cairo_move_to (cr, x + width, y + height);
-
-      if (radius == 0 ||
-          (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
-        cairo_line_to (cr, x, y + height);
-      else
-        cairo_arc (cr, x + radius, y + height - radius, radius, G_PI / 2, 3 * (G_PI / 4));
+      _cairo_ellipsis (cr,
+                       x + width - border_radius->bottom_right.horizontal,
+                       y + height - border_radius->bottom_right.vertical,
+                       border_radius->bottom_right.horizontal,
+                       border_radius->bottom_right.vertical,
+                       G_PI / 4, G_PI / 2);
+      _cairo_ellipsis (cr,
+                       x + border_radius->bottom_left.horizontal,
+                       y + height - border_radius->bottom_left.vertical,
+                       border_radius->bottom_left.horizontal,
+                       border_radius->bottom_left.vertical,
+                       G_PI / 2, 3 * (G_PI / 4));
     }
   else
     cairo_move_to (cr, x, y + height);
 
   if (sides & SIDE_LEFT)
     {
-      if (radius != 0 &&
-          ! (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
-        {
-          if ((sides & SIDE_BOTTOM) == 0)
-            cairo_new_sub_path (cr);
-
-          cairo_arc (cr, x + radius, y + height - radius, radius, 3 * (G_PI / 4), G_PI);
-        }
-      else if ((sides & SIDE_BOTTOM) == 0)
-        cairo_move_to (cr, x, y + height);
-
-      if (radius == 0 ||
-          (junction & GTK_JUNCTION_CORNER_TOPLEFT))
-        cairo_line_to (cr, x, y);
-      else
-        cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI + G_PI / 4);
+      _cairo_ellipsis (cr,
+                       x + border_radius->bottom_left.horizontal,
+                       y + height - border_radius->bottom_left.vertical,
+                       border_radius->bottom_left.horizontal,
+                       border_radius->bottom_left.vertical,
+                       3 * (G_PI / 4), G_PI);
+      _cairo_ellipsis (cr,
+                       x + border_radius->top_left.horizontal,
+                       y + border_radius->top_left.vertical,
+                       border_radius->top_left.horizontal,
+                       border_radius->top_left.vertical,
+                       G_PI, G_PI + G_PI / 4);
     }
+  else
+    cairo_move_to (cr, x, y);
 
   if (sides & SIDE_TOP)
     {
-      if (radius != 0 &&
-          ! (junction & GTK_JUNCTION_CORNER_TOPLEFT))
-        {
-          if ((sides & SIDE_LEFT) == 0)
-            cairo_new_sub_path (cr);
-
-          cairo_arc (cr, x + radius, y + radius, radius, 5 * (G_PI / 4), 3 * (G_PI / 2));
-        }
-      else if ((sides & SIDE_LEFT) == 0)
-        cairo_move_to (cr, x, y);
-
-      if (radius == 0 ||
-          (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
-        cairo_line_to (cr, x + width, y);
-      else
-        cairo_arc (cr, x + width - radius, y + radius, radius, 3 * (G_PI / 2), - G_PI / 4);
+      _cairo_ellipsis (cr,
+                       x + border_radius->top_left.horizontal,
+                       y + border_radius->top_left.vertical,
+                       border_radius->top_left.horizontal,
+                       border_radius->top_left.vertical,
+                       5 * (G_PI / 4), 3 * (G_PI / 2));
+      _cairo_ellipsis (cr,
+                       x + width - border_radius->top_right.horizontal,
+                       y + border_radius->top_right.vertical,
+                       border_radius->top_right.horizontal,
+                       border_radius->top_right.vertical,
+                       3 * (G_PI / 2), - G_PI / 4);
     }
 }
 
-/* Set the appropriate matrix for
- * patterns coming from the style context
- */
 static void
-style_pattern_set_matrix (cairo_pattern_t *pattern,
-                          gdouble          width,
-                          gdouble          height)
-{
-  cairo_matrix_t matrix;
-  gint w, h;
-
-  if (cairo_pattern_get_type (pattern) == CAIRO_PATTERN_TYPE_SURFACE)
-    {
-      cairo_surface_t *surface;
-
-      cairo_pattern_get_surface (pattern, &surface);
-      w = cairo_image_surface_get_width (surface);
-      h = cairo_image_surface_get_height (surface);
-    }
-  else
-    w = h = 1;
+_cairo_uneven_frame (cairo_t                  *cr,
+                     const GtkCssBorderRadius *border_radius,
+                     gdouble                   x,
+                     gdouble                   y,
+                     gdouble                   width,
+                     gdouble                   height,
+                     GtkBorder                *border)
+{
+  cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+  cairo_set_line_width (cr, 1);
 
-  cairo_matrix_init_scale (&matrix, (gdouble) w / width, (gdouble) h / height);
-  cairo_pattern_set_matrix (pattern, &matrix);
+  _cairo_round_rectangle_sides (cr, border_radius,
+                                x, y,
+                                width, height,
+                                SIDE_ALL);
+
+  _cairo_round_rectangle_sides (cr, border_radius,
+                                x + border->left,
+                                y + border->top,
+                                width - border->left - border->right,
+                                height - border->top - border->bottom,
+                                SIDE_ALL);
 }
 
 static void
@@ -1507,16 +1506,18 @@ render_background_internal (GtkThemingEngine *engine,
                             gdouble           height,
                             GtkJunctionSides  junction)
 {
-  GdkRGBA *bg_color;
+  GdkRGBA bg_color;
   cairo_pattern_t *pattern;
   GtkStateFlags flags;
   gboolean running;
   gdouble progress, alpha = 1;
-  GtkBorder *border;
-  gint radius, border_width;
+  GtkBorder border;
+  GtkCssBorderCornerRadius *top_left_radius, *top_right_radius;
+  GtkCssBorderCornerRadius *bottom_left_radius, *bottom_right_radius;
+  GtkCssBorderRadius border_radius = { { 0, },  };
+  gint border_width;
   GtkBorderStyle border_style;
   gdouble mat_w, mat_h;
-  cairo_matrix_t identity;
 
   /* Use unmodified size for pattern scaling */
   mat_w = width;
@@ -1524,24 +1525,39 @@ render_background_internal (GtkThemingEngine *engine,
 
   flags = gtk_theming_engine_get_state (engine);
 
-  cairo_matrix_init_identity (&identity);
+  gtk_theming_engine_get_background_color (engine, flags, &bg_color);
+  gtk_theming_engine_get_border (engine, flags, &border);
 
   gtk_theming_engine_get (engine, flags,
                           "background-image", &pattern,
-                          "background-color", &bg_color,
-                          "border-radius", &radius,
-                          "border-width", &border,
+                          /* Can't use border-radius as it's an int for
+                           * backwards compat */
+                          "border-top-left-radius", &top_left_radius,
+                          "border-top-right-radius", &top_right_radius,
+                          "border-bottom-right-radius", &bottom_right_radius,
+                          "border-bottom-left-radius", &bottom_left_radius,
                           "border-style", &border_style,
                           NULL);
 
-  border_width = MIN (MIN (border->top, border->bottom),
-                      MIN (border->left, border->right));
+  if (top_left_radius)
+    border_radius.top_left = *top_left_radius;
+  g_free (top_left_radius);
+  if (top_right_radius)
+    border_radius.top_right = *top_right_radius;
+  g_free (top_right_radius);
+  if (bottom_right_radius)
+    border_radius.bottom_right = *bottom_right_radius;
+  g_free (bottom_right_radius);
+  if (bottom_left_radius)
+    border_radius.bottom_left = *bottom_left_radius;
+  g_free (bottom_left_radius);
+
+  border_width = MIN (MIN (border.top, border.bottom),
+                      MIN (border.left, border.right));
 
   if (border_width > 1 &&
       border_style == GTK_BORDER_STYLE_NONE)
     {
-      cairo_set_line_width (cr, border_width);
-
       x += (gdouble) border_width / 2;
       y += (gdouble) border_width / 2;
       width -= border_width;
@@ -1549,16 +1565,17 @@ render_background_internal (GtkThemingEngine *engine,
     }
   else
     {
-      x += border->left;
-      y += border->top;
-      width -= border->left + border->right;
-      height -= border->top + border->bottom;
+      x += border.left;
+      y += border.top;
+      width -= border.left + border.right;
+      height -= border.top + border.bottom;
     }
 
   if (width <= 0 || height <= 0)
     return;
 
   cairo_save (cr);
+  cairo_set_line_width (cr, border_width);
   cairo_translate (cr, x, y);
 
   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
@@ -1574,7 +1591,7 @@ render_background_internal (GtkThemingEngine *engine,
     {
       cairo_pattern_t *other_pattern;
       GtkStateFlags other_flags;
-      GdkRGBA *other_bg;
+      GdkRGBA other_bg;
       cairo_pattern_t *new_pattern = NULL;
 
       if (flags & GTK_STATE_FLAG_PRELIGHT)
@@ -1585,9 +1602,9 @@ render_background_internal (GtkThemingEngine *engine,
       else
         other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
 
+      gtk_theming_engine_get_background_color (engine, other_flags, &other_bg);
       gtk_theming_engine_get (engine, other_flags,
                               "background-image", &other_pattern,
-                              "background-color", &other_bg,
                               NULL);
 
       if (pattern && other_pattern)
@@ -1660,16 +1677,15 @@ render_background_internal (GtkThemingEngine *engine,
               /* Different pattern types, or different color
                * stop counts, alpha blend both patterns.
                */
-              _cairo_round_rectangle_sides (cr, (gdouble) radius,
+              _cairo_round_rectangle_sides (cr, &border_radius,
                                             0, 0, width, height,
-                                            SIDE_ALL, junction);
+                                            SIDE_ALL);
 
-              style_pattern_set_matrix (other_pattern, mat_w, mat_h);
+              cairo_scale (cr, mat_w, mat_h);
               cairo_set_source (cr, other_pattern);
+              cairo_scale (cr, 1.0 / mat_w, 1.0 / mat_h);
               cairo_fill_preserve (cr);
 
-              cairo_pattern_set_matrix (other_pattern, &identity);
-
               /* Set alpha for posterior drawing
                * of the target pattern
                */
@@ -1687,13 +1703,13 @@ render_background_internal (GtkThemingEngine *engine,
           if (pattern)
             {
               p = pattern;
-             c = other_bg;
+              c = &other_bg;
               progress = 1 - progress;
             }
           else
             {
               p = other_pattern;
-             c = bg_color;
+              c = &bg_color;
             }
 
           if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
@@ -1727,16 +1743,11 @@ render_background_internal (GtkThemingEngine *engine,
         }
       else
         {
-          const GdkRGBA *color, *other_color;
-
           /* Merge just colors */
-          color = bg_color;
-          other_color = other_bg;
-
-          new_pattern = cairo_pattern_create_rgba (CLAMP (color->red + ((other_color->red - color->red) * progress), 0, 1),
-                                                   CLAMP (color->green + ((other_color->green - color->green) * progress), 0, 1),
-                                                   CLAMP (color->blue + ((other_color->blue - color->blue) * progress), 0, 1),
-                                                   CLAMP (color->alpha + ((other_color->alpha - color->alpha) * progress), 0, 1));
+          new_pattern = cairo_pattern_create_rgba (CLAMP (bg_color.red + ((other_bg.red - bg_color.red) * progress), 0, 1),
+                                                   CLAMP (bg_color.green + ((other_bg.green - bg_color.green) * progress), 0, 1),
+                                                   CLAMP (bg_color.blue + ((other_bg.blue - bg_color.blue) * progress), 0, 1),
+                                                   CLAMP (bg_color.alpha + ((other_bg.alpha - bg_color.alpha) * progress), 0, 1));
         }
 
       if (new_pattern)
@@ -1748,21 +1759,19 @@ render_background_internal (GtkThemingEngine *engine,
 
       if (other_pattern)
         cairo_pattern_destroy (other_pattern);
-
-      if (other_bg)
-        gdk_rgba_free (other_bg);
     }
 
-  _cairo_round_rectangle_sides (cr, (gdouble) radius,
+  _cairo_round_rectangle_sides (cr, &border_radius,
                                 0, 0, width, height,
-                                SIDE_ALL, junction);
+                                SIDE_ALL);
   if (pattern)
     {
-      style_pattern_set_matrix (pattern, mat_w, mat_h);
+      cairo_scale (cr, mat_w, mat_h);
       cairo_set_source (cr, pattern);
+      cairo_scale (cr, 1.0 / mat_w, 1.0 / mat_h);
     }
   else
-    gdk_cairo_set_source_rgba (cr, bg_color);
+    gdk_cairo_set_source_rgba (cr, &bg_color);
 
   if (alpha == 1)
     {
@@ -1784,9 +1793,9 @@ render_background_internal (GtkThemingEngine *engine,
   else
     {
       cairo_save (cr);
-      _cairo_round_rectangle_sides (cr, (gdouble) radius,
+      _cairo_round_rectangle_sides (cr, &border_radius,
                                     0, 0, width, height,
-                                    SIDE_ALL, junction);
+                                    SIDE_ALL);
       cairo_clip (cr);
 
       cairo_paint_with_alpha (cr, alpha);
@@ -1795,15 +1804,9 @@ render_background_internal (GtkThemingEngine *engine,
     }
 
   if (pattern)
-    {
-      cairo_pattern_set_matrix (pattern, &identity);
-      cairo_pattern_destroy (pattern);
-    }
+    cairo_pattern_destroy (pattern);
 
   cairo_restore (cr);
-
-  gdk_rgba_free (bg_color);
-  gtk_border_free (border);
 }
 
 static void
@@ -1845,6 +1848,20 @@ _cairo_corner_triangle (cairo_t *cr,
     }
 }
 
+static void
+gtk_themeing_engine_apply_junction_to_radius (GtkCssBorderRadius *border_radius,
+                                              GtkJunctionSides    junction)
+{
+  if (junction & GTK_JUNCTION_CORNER_TOPLEFT)
+    border_radius->top_left.horizontal = border_radius->top_left.vertical = 0;
+  if (junction & GTK_JUNCTION_CORNER_TOPRIGHT)
+    border_radius->top_right.horizontal = border_radius->top_right.vertical = 0;
+  if (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT)
+    border_radius->bottom_right.horizontal = border_radius->bottom_right.vertical = 0;
+  if (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT)
+    border_radius->bottom_left.horizontal = border_radius->bottom_left.vertical = 0;
+}
+
 static void
 render_frame_internal (GtkThemingEngine *engine,
                        cairo_t          *cr,
@@ -1857,29 +1874,58 @@ render_frame_internal (GtkThemingEngine *engine,
 {
   GtkStateFlags state;
   GdkRGBA lighter;
-  GdkRGBA *border_color;
+  GdkRGBA border_color;
   GtkBorderStyle border_style;
-  gint border_width, radius;
+  gint border_width;
+  GtkCssBorderCornerRadius *top_left_radius, *top_right_radius;
+  GtkCssBorderCornerRadius *bottom_left_radius, *bottom_right_radius;
+  GtkCssBorderRadius border_radius = { { 0, },  };
   gdouble progress, d1, d2, m;
   gboolean running;
-  GtkBorder *border;
+  GtkBorder border;
+  gboolean uniform_border;
 
   state = gtk_theming_engine_get_state (engine);
+
+  gtk_theming_engine_get_border_color (engine, state, &border_color);
+  gtk_theming_engine_get_border (engine, state, &border);
+
   gtk_theming_engine_get (engine, state,
-                          "border-color", &border_color,
                           "border-style", &border_style,
-                          "border-width", &border,
-                          "border-radius", &radius,
+                          /* Can't use border-radius as it's an int for
+                           * backwards compat */
+                          "border-top-left-radius", &top_left_radius,
+                          "border-top-right-radius", &top_right_radius,
+                          "border-bottom-right-radius", &bottom_right_radius,
+                          "border-bottom-left-radius", &bottom_left_radius,
                           NULL);
 
+  if (top_left_radius)
+    border_radius.top_left = *top_left_radius;
+  g_free (top_left_radius);
+  if (top_right_radius)
+    border_radius.top_right = *top_right_radius;
+  g_free (top_right_radius);
+  if (bottom_right_radius)
+    border_radius.bottom_right = *bottom_right_radius;
+  g_free (bottom_right_radius);
+  if (bottom_left_radius)
+    border_radius.bottom_left = *bottom_left_radius;
+  g_free (bottom_left_radius);
+
+  gtk_themeing_engine_apply_junction_to_radius (&border_radius, junction);
+
   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
-  border_width = MIN (MIN (border->top, border->bottom),
-                      MIN (border->left, border->right));
+  border_width = MIN (MIN (border.top, border.bottom),
+                      MIN (border.left, border.right));
+  uniform_border = (border.top == border.bottom &&
+                    border.top == border.left &&
+                    border.top == border.right);
 
   if (running)
     {
       GtkStateFlags other_state;
-      GdkRGBA *other_color;
+      GdkRGBA other_color;
 
       if (state & GTK_STATE_FLAG_PRELIGHT)
         {
@@ -1889,21 +1935,17 @@ render_frame_internal (GtkThemingEngine *engine,
       else
         other_state = state | GTK_STATE_FLAG_PRELIGHT;
 
-      gtk_theming_engine_get (engine, other_state,
-                              "border-color", &other_color,
-                              NULL);
-
-      border_color->red = CLAMP (border_color->red + ((other_color->red - border_color->red) * progress), 0, 1);
-      border_color->green = CLAMP (border_color->green + ((other_color->green - border_color->green) * progress), 0, 1);
-      border_color->blue = CLAMP (border_color->blue + ((other_color->blue - border_color->blue) * progress), 0, 1);
-      border_color->alpha = CLAMP (border_color->alpha + ((other_color->alpha - border_color->alpha) * progress), 0, 1);
+      gtk_theming_engine_get_border_color (engine, other_state, &other_color);
 
-      gdk_rgba_free (other_color);
+      border_color.red = CLAMP (border_color.red + ((other_color.red - border_color.red) * progress), 0, 1);
+      border_color.green = CLAMP (border_color.green + ((other_color.green - border_color.green) * progress), 0, 1);
+      border_color.blue = CLAMP (border_color.blue + ((other_color.blue - border_color.blue) * progress), 0, 1);
+      border_color.alpha = CLAMP (border_color.alpha + ((other_color.alpha - border_color.alpha) * progress), 0, 1);
     }
 
   cairo_save (cr);
 
-  color_shade (border_color, 1.8, &lighter);
+  color_shade (&border_color, 1.8, &lighter);
 
   switch (border_style)
     {
@@ -1913,37 +1955,20 @@ render_frame_internal (GtkThemingEngine *engine,
       cairo_set_line_width (cr, border_width);
       cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
 
-      if (border_width > 1)
-        {
-          x += (gdouble) border_width / 2;
-          y += (gdouble) border_width / 2;
-          width -= border_width;
-          height -= border_width;
-        }
-      else if (border_width == 1)
-        {
-          x += 0.5;
-          y += 0.5;
-          width -= 1;
-          height -= 1;
-        }
+      gdk_cairo_set_source_rgba (cr, &border_color);
 
-      _cairo_round_rectangle_sides (cr, (gdouble) radius,
-                                    x, y, width, height,
-                                    SIDE_ALL & ~(hidden_side),
-                                    junction);
-      gdk_cairo_set_source_rgba (cr, border_color);
-      cairo_stroke (cr);
+      cairo_save (cr);
+      _cairo_uneven_frame (cr, &border_radius,
+                           x, y, width, height,
+                           &border);
+      cairo_fill (cr);
+      cairo_restore (cr);
 
       break;
     case GTK_BORDER_STYLE_INSET:
     case GTK_BORDER_STYLE_OUTSET:
       cairo_set_line_width (cr, border_width);
-
-      if (radius == 0)
-        cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
-      else
-        cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+      cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
 
       if (border_width > 1)
         {
@@ -1961,29 +1986,73 @@ render_frame_internal (GtkThemingEngine *engine,
       m = MIN (width, height);
       m /= 2;
 
-      if (border_style == GTK_BORDER_STYLE_INSET)
-        gdk_cairo_set_source_rgba (cr, &lighter);
-      else
-        gdk_cairo_set_source_rgba (cr, border_color);
+      if (uniform_border)
+        {
+          if (border_style == GTK_BORDER_STYLE_INSET)
+            gdk_cairo_set_source_rgba (cr, &lighter);
+          else
+            gdk_cairo_set_source_rgba (cr, &border_color);
 
-      _cairo_round_rectangle_sides (cr, (gdouble) radius,
-                                    x + d1, y + d1,
-                                    width - d2, height - d2,
-                                    (SIDE_BOTTOM | SIDE_RIGHT) & ~(hidden_side),
-                                    junction);
-      cairo_stroke (cr);
+          _cairo_round_rectangle_sides (cr, &border_radius,
+                                        x + d1, y + d1,
+                                        width - d2, height - d2,
+                                        (SIDE_BOTTOM | SIDE_RIGHT) & ~(hidden_side));
+          cairo_stroke (cr);
 
-      if (border_style == GTK_BORDER_STYLE_INSET)
-        gdk_cairo_set_source_rgba (cr, border_color);
+          if (border_style == GTK_BORDER_STYLE_INSET)
+            gdk_cairo_set_source_rgba (cr, &border_color);
+          else
+            gdk_cairo_set_source_rgba (cr, &lighter);
+
+          _cairo_round_rectangle_sides (cr, &border_radius,
+                                        x + d1, y + d1,
+                                        width - d2, height - d2,
+                                        (SIDE_TOP | SIDE_LEFT) & ~(hidden_side));
+          cairo_stroke (cr);
+        }
       else
-        gdk_cairo_set_source_rgba (cr, &lighter);
+        {
+          cairo_save (cr);
 
-      _cairo_round_rectangle_sides (cr, (gdouble) radius,
-                                    x + d1, y + d1,
-                                    width - d2, height - d2,
-                                    (SIDE_TOP | SIDE_LEFT) & ~(hidden_side),
-                                    junction);
-      cairo_stroke (cr);
+          /* Bottom/right */
+          if (border_style == GTK_BORDER_STYLE_INSET)
+            gdk_cairo_set_source_rgba (cr, &lighter);
+          else
+            gdk_cairo_set_source_rgba (cr, &border_color);
+
+          _cairo_uneven_frame (cr, &border_radius,
+                               x, y, width, height,
+                               &border);
+          cairo_fill (cr);
+
+          /* Top/left */
+          cairo_move_to (cr, x, y);
+          cairo_line_to (cr, x + width, y);
+          cairo_line_to (cr, 
+                         x + width - border.right - border_radius.top_right.horizontal / 2,
+                         y + border.top + border_radius.top_right.vertical / 2);
+          cairo_line_to (cr, 
+                         x + width - border.right - border_radius.bottom_right.horizontal / 2,
+                         y + height - border.bottom - border_radius.bottom_right.vertical / 2);
+          cairo_line_to (cr, 
+                         x + border.left + border_radius.bottom_left.horizontal / 2,
+                         y + height - border.bottom - border_radius.bottom_left.vertical / 2);
+          cairo_line_to (cr, x, y + height);
+          cairo_close_path (cr);
+
+          cairo_clip (cr);
+
+          if (border_style == GTK_BORDER_STYLE_INSET)
+            gdk_cairo_set_source_rgba (cr, &border_color);
+          else
+            gdk_cairo_set_source_rgba (cr, &lighter);
+
+          _cairo_uneven_frame (cr, &border_radius,
+                               x, y, width, height,
+                               &border);
+          cairo_fill (cr);
+          cairo_restore (cr);
+        }
 
       if (border_width > 1)
         {
@@ -1996,18 +2065,16 @@ render_frame_internal (GtkThemingEngine *engine,
           if (border_style == GTK_BORDER_STYLE_INSET)
             gdk_cairo_set_source_rgba (cr, &lighter);
           else
-            gdk_cairo_set_source_rgba (cr, border_color);
+            gdk_cairo_set_source_rgba (cr, &border_color);
 
           cairo_set_line_width (cr, 1);
 
-          if (radius == 0 ||
-              (junction & GTK_JUNCTION_CORNER_TOPRIGHT) != 0)
+          if ((junction & GTK_JUNCTION_CORNER_TOPRIGHT) != 0)
             _cairo_corner_triangle (cr,
                                     x + width - border_width, y,
                                     border_width);
 
-          if (radius == 0 ||
-              (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) != 0)
+          if ((junction & GTK_JUNCTION_CORNER_BOTTOMLEFT) != 0)
             _cairo_corner_triangle (cr,
                                     x, y + height - border_width,
                                     border_width);
@@ -2020,11 +2087,6 @@ render_frame_internal (GtkThemingEngine *engine,
     }
 
   cairo_restore (cr);
-
-  if (border_color)
-    gdk_rgba_free (border_color);
-
-  gtk_border_free (border);
 }
 
 static void
@@ -2036,22 +2098,25 @@ gtk_theming_engine_render_frame (GtkThemingEngine *engine,
                                  gdouble           height)
 {
   GtkStateFlags flags;
-  Gtk9Slice *slice;
   GtkBorderStyle border_style;
   GtkJunctionSides junction;
+  GtkBorderImage *border_image;
+  GtkBorder border;
 
   flags = gtk_theming_engine_get_state (engine);
   junction = gtk_theming_engine_get_junction_sides (engine);
+  gtk_theming_engine_get_border (engine, flags, &border);
 
   gtk_theming_engine_get (engine, flags,
-                          "border-image", &slice,
+                          "border-image", &border_image,
                           "border-style", &border_style,
                           NULL);
 
-  if (slice)
+  if (border_image != NULL)
     {
-      _gtk_9slice_render (slice, cr, x, y, width, height);
-      _gtk_9slice_unref (slice);
+      _gtk_border_image_render (border_image, &border,
+                                cr, x, y, width, height);
+      _gtk_border_image_unref (border_image);
     }
   else if (border_style != GTK_BORDER_STYLE_NONE)
     render_frame_internal (engine, cr,
@@ -2068,7 +2133,7 @@ gtk_theming_engine_render_expander (GtkThemingEngine *engine,
                                     gdouble           height)
 {
   GtkStateFlags flags;
-  GdkRGBA *outline_color, *fg_color;
+  GdkRGBA outline_color, fg_color;
   double vertical_overshoot;
   int diameter;
   double radius;
@@ -2084,12 +2149,8 @@ gtk_theming_engine_render_expander (GtkThemingEngine *engine,
   cairo_save (cr);
   flags = gtk_theming_engine_get_state (engine);
 
-  gtk_theming_engine_get (engine, flags,
-                          "color", &fg_color,
-                          NULL);
-  gtk_theming_engine_get (engine, flags,
-                          "border-color", &outline_color,
-                          NULL);
+  gtk_theming_engine_get_color (engine, flags, &fg_color);
+  gtk_theming_engine_get_border_color (engine, flags, &outline_color);
 
   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress);
   is_rtl = (gtk_theming_engine_get_direction (engine) == GTK_TEXT_DIR_RTL);
@@ -2164,17 +2225,14 @@ gtk_theming_engine_render_expander (GtkThemingEngine *engine,
 
   cairo_set_line_width (cr, line_width);
 
-  gdk_cairo_set_source_rgba (cr, fg_color);
+  gdk_cairo_set_source_rgba (cr, &fg_color);
 
   cairo_fill_preserve (cr);
 
-  gdk_cairo_set_source_rgba (cr, outline_color);
+  gdk_cairo_set_source_rgba (cr, &outline_color);
   cairo_stroke (cr);
 
   cairo_restore (cr);
-
-  gdk_rgba_free (fg_color);
-  gdk_rgba_free (outline_color);
 }
 
 static void
@@ -2186,16 +2244,14 @@ gtk_theming_engine_render_focus (GtkThemingEngine *engine,
                                  gdouble           height)
 {
   GtkStateFlags flags;
-  GdkRGBA *color;
+  GdkRGBA color;
   gint line_width;
   gint8 *dash_list;
 
   cairo_save (cr);
   flags = gtk_theming_engine_get_state (engine);
 
-  gtk_theming_engine_get (engine, flags,
-                          "color", &color,
-                          NULL);
+  gtk_theming_engine_get_color (engine, flags, &color);
 
   gtk_theming_engine_get_style (engine,
                                "focus-line-width", &line_width,
@@ -2238,12 +2294,11 @@ gtk_theming_engine_render_focus (GtkThemingEngine *engine,
                    width - line_width,
                    height - line_width);
 
-  gdk_cairo_set_source_rgba (cr, color);
+  gdk_cairo_set_source_rgba (cr, &color);
   cairo_stroke (cr);
 
   cairo_restore (cr);
 
-  gdk_rgba_free (color);
   g_free (dash_list);
 }
 
@@ -2255,7 +2310,7 @@ gtk_theming_engine_render_line (GtkThemingEngine *engine,
                                 gdouble           x1,
                                 gdouble           y1)
 {
-  GdkRGBA *bg_color, darker, lighter;
+  GdkRGBA bg_color, darker, lighter;
   GtkStateFlags flags;
   gint i, thickness, thickness_dark, thickness_light, len;
   cairo_matrix_t matrix;
@@ -2269,11 +2324,9 @@ gtk_theming_engine_render_line (GtkThemingEngine *engine,
   flags = gtk_theming_engine_get_state (engine);
   cairo_save (cr);
 
-  gtk_theming_engine_get (engine, flags,
-                          "background-color", &bg_color,
-                          NULL);
-  color_shade (bg_color, 0.7, &darker);
-  color_shade (bg_color, 1.3, &lighter);
+  gtk_theming_engine_get_background_color (engine, flags, &bg_color);
+  color_shade (&bg_color, 0.7, &darker);
+  color_shade (&bg_color, 1.3, &lighter);
 
   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
   cairo_set_line_width (cr, 1);
@@ -2321,8 +2374,31 @@ gtk_theming_engine_render_line (GtkThemingEngine *engine,
     }
 
   cairo_restore (cr);
+}
 
-  gdk_rgba_free (bg_color);
+static void
+prepare_context_for_layout (cairo_t *cr,
+                            gdouble x,
+                            gdouble y,
+                            PangoLayout *layout)
+{
+  const PangoMatrix *matrix;
+
+  matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+
+  cairo_move_to (cr, x, y);
+
+  if (matrix)
+    {
+      cairo_matrix_t cairo_matrix;
+
+      cairo_matrix_init (&cairo_matrix,
+                         matrix->xx, matrix->yx,
+                         matrix->xy, matrix->yy,
+                         matrix->x0, matrix->y0);
+
+      cairo_transform (cr, &cairo_matrix);
+    }
 }
 
 static void
@@ -2332,27 +2408,22 @@ gtk_theming_engine_render_layout (GtkThemingEngine *engine,
                                   gdouble           y,
                                   PangoLayout      *layout)
 {
-  const PangoMatrix *matrix;
-  GdkRGBA *fg_color;
+  GdkRGBA fg_color;
+  GtkShadow *text_shadow = NULL;
   GtkStateFlags flags;
   gdouble progress;
   gboolean running;
 
   cairo_save (cr);
   flags = gtk_theming_engine_get_state (engine);
-
-  gtk_theming_engine_get (engine, flags,
-                          "color", &fg_color,
-                          NULL);
-
-  matrix = pango_context_get_matrix (pango_layout_get_context (layout));
+  gtk_theming_engine_get_color (engine, flags, &fg_color);
 
   running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
 
   if (running)
     {
       GtkStateFlags other_flags;
-      GdkRGBA *other_fg;
+      GdkRGBA other_fg;
 
       if (flags & GTK_STATE_FLAG_PRELIGHT)
         {
@@ -2362,59 +2433,30 @@ gtk_theming_engine_render_layout (GtkThemingEngine *engine,
       else
         other_flags = flags | GTK_STATE_FLAG_PRELIGHT;
 
-      gtk_theming_engine_get (engine, other_flags,
-                              "color", &other_fg,
-                              NULL);
-
-      if (fg_color && other_fg)
-        {
-          fg_color->red = CLAMP (fg_color->red + ((other_fg->red - fg_color->red) * progress), 0, 1);
-          fg_color->green = CLAMP (fg_color->green + ((other_fg->green - fg_color->green) * progress), 0, 1);
-          fg_color->blue = CLAMP (fg_color->blue + ((other_fg->blue - fg_color->blue) * progress), 0, 1);
-          fg_color->alpha = CLAMP (fg_color->alpha + ((other_fg->alpha - fg_color->alpha) * progress), 0, 1);
-        }
+      gtk_theming_engine_get_color (engine, other_flags, &other_fg);
 
-      if (other_fg)
-        gdk_rgba_free (other_fg);
+      fg_color.red = CLAMP (fg_color.red + ((other_fg.red - fg_color.red) * progress), 0, 1);
+      fg_color.green = CLAMP (fg_color.green + ((other_fg.green - fg_color.green) * progress), 0, 1);
+      fg_color.blue = CLAMP (fg_color.blue + ((other_fg.blue - fg_color.blue) * progress), 0, 1);
+      fg_color.alpha = CLAMP (fg_color.alpha + ((other_fg.alpha - fg_color.alpha) * progress), 0, 1);
     }
 
-  if (matrix)
-    {
-      cairo_matrix_t cairo_matrix;
-      PangoRectangle rect;
-
-      cairo_matrix_init (&cairo_matrix,
-                         matrix->xx, matrix->yx,
-                         matrix->xy, matrix->yy,
-                         matrix->x0, matrix->y0);
-
-      pango_layout_get_extents (layout, NULL, &rect);
-      pango_matrix_transform_rectangle (matrix, &rect);
-      pango_extents_to_pixels (&rect, NULL);
-
-      cairo_matrix.x0 += x - rect.x;
-      cairo_matrix.y0 += y - rect.y;
+  gtk_theming_engine_get (engine, flags,
+                          "text-shadow", &text_shadow,
+                          NULL);
 
-      cairo_set_matrix (cr, &cairo_matrix);
-    }
-  else
-    cairo_move_to (cr, x, y);
+  prepare_context_for_layout (cr, x, y, layout);
 
-  if (flags & GTK_STATE_FLAG_INSENSITIVE)
+  if (text_shadow != NULL)
     {
-      cairo_save (cr);
-      cairo_set_source_rgb (cr, 1, 1, 1);
-      cairo_move_to (cr, x + 1, y + 1);
-      _gtk_pango_fill_layout (cr, layout);
-      cairo_restore (cr);
+      _gtk_text_shadow_paint_layout (text_shadow, cr, layout);
+      _gtk_shadow_unref (text_shadow);
     }
 
-  gdk_cairo_set_source_rgba (cr, fg_color);
+  gdk_cairo_set_source_rgba (cr, &fg_color);
   pango_cairo_show_layout (cr, layout);
 
   cairo_restore (cr);
-
-  gdk_rgba_free (fg_color);
 }
 
 static void
@@ -2467,20 +2509,42 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
 {
   GtkJunctionSides junction;
   GtkStateFlags state;
-  gint border_width, radius;
+  gint border_width;
+  GtkCssBorderCornerRadius *top_left_radius, *top_right_radius;
+  GtkCssBorderCornerRadius *bottom_left_radius, *bottom_right_radius;
+  GtkCssBorderRadius border_radius = { { 0, },  };
   gdouble x0, y0, x1, y1, xc, yc, wc, hc;
-  GtkBorder *border;
+  GtkBorder border;
 
   xc = yc = wc = hc = 0;
   state = gtk_theming_engine_get_state (engine);
   junction = gtk_theming_engine_get_junction_sides (engine);
+
+  gtk_theming_engine_get_border (engine, state, &border);
   gtk_theming_engine_get (engine, state,
-                          "border-width", &border,
-                          "border-radius", &radius,
+                          /* Can't use border-radius as it's an int for
+                           * backwards compat */
+                          "border-top-left-radius", &top_left_radius,
+                          "border-top-right-radius", &top_right_radius,
+                          "border-bottom-right-radius", &bottom_right_radius,
+                          "border-bottom-left-radius", &bottom_left_radius,
                           NULL);
 
-  border_width = MIN (MIN (border->top, border->bottom),
-                      MIN (border->left, border->right));
+  if (top_left_radius)
+    border_radius.top_left = *top_left_radius;
+  g_free (top_left_radius);
+  if (top_right_radius)
+    border_radius.top_right = *top_right_radius;
+  g_free (top_right_radius);
+  if (bottom_right_radius)
+    border_radius.bottom_right = *bottom_right_radius;
+  g_free (bottom_right_radius);
+  if (bottom_left_radius)
+    border_radius.bottom_left = *bottom_left_radius;
+  g_free (bottom_left_radius);
+
+  border_width = MIN (MIN (border.top, border.bottom),
+                      MIN (border.left, border.right));
 
   cairo_save (cr);
 
@@ -2492,10 +2556,10 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
       hc = border_width;
 
-      if (xy0_gap < radius)
+      if (xy0_gap < border_radius.top_left.horizontal)
         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
 
-      if (xy1_gap > width - radius)
+      if (xy1_gap > width - border_radius.top_right.horizontal)
         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
       break;
     case GTK_POS_BOTTOM:
@@ -2504,10 +2568,10 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
       wc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
       hc = border_width;
 
-      if (xy0_gap < radius)
+      if (xy0_gap < border_radius.bottom_left.horizontal)
         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
 
-      if (xy1_gap > width - radius)
+      if (xy1_gap > width - border_radius.bottom_right.horizontal)
         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
 
       break;
@@ -2517,10 +2581,10 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
       wc = border_width;
       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
 
-      if (xy0_gap < radius)
+      if (xy0_gap < border_radius.top_left.vertical)
         junction |= GTK_JUNCTION_CORNER_TOPLEFT;
 
-      if (xy1_gap > height - radius)
+      if (xy1_gap > height - border_radius.bottom_left.vertical)
         junction |= GTK_JUNCTION_CORNER_BOTTOMLEFT;
 
       break;
@@ -2530,10 +2594,10 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
       wc = border_width;
       hc = MAX (xy1_gap - xy0_gap - 2 * border_width, 0);
 
-      if (xy0_gap < radius)
+      if (xy0_gap < border_radius.top_right.vertical)
         junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
 
-      if (xy1_gap > height - radius)
+      if (xy1_gap > height - border_radius.bottom_right.vertical)
         junction |= GTK_JUNCTION_CORNER_BOTTOMRIGHT;
 
       break;
@@ -2551,8 +2615,6 @@ gtk_theming_engine_render_frame_gap (GtkThemingEngine *engine,
                          0, junction);
 
   cairo_restore (cr);
-
-  gtk_border_free (border);
 }
 
 static void
@@ -2660,34 +2722,24 @@ gtk_theming_engine_render_handle (GtkThemingEngine *engine,
                                   gdouble           height)
 {
   GtkStateFlags flags;
-  GdkRGBA *bg_color;
-  GdkRGBA lighter, darker;
+  GdkRGBA bg_color, lighter, darker;
+  GtkJunctionSides sides;
   gint xx, yy;
 
   cairo_save (cr);
   flags = gtk_theming_engine_get_state (engine);
 
-  cairo_set_line_width (cr, 1);
+  cairo_set_line_width (cr, 1.0);
+  sides = gtk_theming_engine_get_junction_sides (engine);
+  gtk_theming_engine_get_background_color (engine, flags, &bg_color);
 
-  gtk_theming_engine_get (engine, flags,
-                          "background-color", &bg_color,
-                          NULL);
-  color_shade (bg_color, 0.7, &darker);
-  color_shade (bg_color, 1.3, &lighter);
+  color_shade (&bg_color, 0.7, &darker);
+  color_shade (&bg_color, 1.3, &lighter);
 
-  gdk_cairo_set_source_rgba (cr, bg_color);
-  cairo_rectangle (cr, x, y, width, height);
-  cairo_fill (cr);
+  render_background_internal (engine, cr, x, y, width, height, sides);
 
   if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_GRIP))
     {
-      GtkJunctionSides sides;
-
-      cairo_save (cr);
-
-      cairo_set_line_width (cr, 1.0);
-      sides = gtk_theming_engine_get_junction_sides (engine);
-
       /* reduce confusing values to a meaningful state */
       if ((sides & (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT)) == (GTK_JUNCTION_CORNER_TOPLEFT | GTK_JUNCTION_CORNER_BOTTOMRIGHT))
         sides &= ~GTK_JUNCTION_CORNER_TOPLEFT;
@@ -2932,8 +2984,6 @@ gtk_theming_engine_render_handle (GtkThemingEngine *engine,
               yi += 3;
             }
         }
-
-      cairo_restore (cr);
     }
   else if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_PANE_SEPARATOR))
     {
@@ -2955,84 +3005,116 @@ gtk_theming_engine_render_handle (GtkThemingEngine *engine,
     }
 
   cairo_restore (cr);
-
-  gdk_rgba_free (bg_color);
 }
 
-static void
-gtk_theming_engine_render_activity (GtkThemingEngine *engine,
-                                    cairo_t          *cr,
-                                    gdouble           x,
-                                    gdouble           y,
-                                    gdouble           width,
-                                    gdouble           height)
+void
+_gtk_theming_engine_paint_spinner (cairo_t *cr,
+                                   gdouble  radius,
+                                   gdouble  progress,
+                                   GdkRGBA *color)
 {
-  if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_SPINNER))
-    {
-      GtkStateFlags state;
-      guint num_steps, step;
-      GdkRGBA *color;
-      gdouble dx, dy;
-      gdouble progress;
-      gdouble radius;
-      gdouble half;
-      gint i;
+  guint num_steps, step;
+  gdouble half;
+  gint i;
 
-      num_steps = 12;
+  num_steps = 12;
 
-      state = gtk_theming_engine_get_state (engine);
-      gtk_theming_engine_get (engine, state,
-                              "color", &color,
-                              NULL);
+  if (progress >= 0)
+    step = (guint) (progress * num_steps);
+  else
+    step = 0;
 
-      if (gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress))
-        step = (guint) (progress * num_steps);
-      else
-        step = 0;
+  cairo_save (cr);
 
-      cairo_save (cr);
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+  cairo_set_line_width (cr, 2.0);
 
-      cairo_translate (cr, x, y);
+  half = num_steps / 2;
 
-      /* draw clip region */
-      cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+  for (i = 0; i < num_steps; i++)
+    {
+      gint inset = 0.7 * radius;
+
+      /* transparency is a function of time and intial value */
+      gdouble t = 1.0 - (gdouble) ((i + step) % num_steps) / num_steps;
+      gdouble xscale = - sin (i * G_PI / half);
+      gdouble yscale = - cos (i * G_PI / half);
+
+      cairo_set_source_rgba (cr,
+                             color->red,
+                             color->green,
+                             color->blue,
+                             color->alpha * t);
+
+      cairo_move_to (cr,
+                     (radius - inset) * xscale,
+                     (radius - inset) * yscale);
+      cairo_line_to (cr,
+                     radius * xscale,
+                     radius * yscale);
 
-      dx = width / 2;
-      dy = height / 2;
-      radius = MIN (width / 2, height / 2);
-      half = num_steps / 2;
+      cairo_stroke (cr);
+    }
 
-      for (i = 0; i < num_steps; i++)
-        {
-          gint inset = 0.7 * radius;
+  cairo_restore (cr);
+}
 
-          /* transparency is a function of time and intial value */
-          gdouble t = (gdouble) ((i + num_steps - step)
-                                 % num_steps) / num_steps;
+static void
+render_spinner (GtkThemingEngine *engine,
+                cairo_t          *cr,
+                gdouble           x,
+                gdouble           y,
+                gdouble           width,
+                gdouble           height)
+{
+  GtkStateFlags state;
+  GtkShadow *shadow;
+  GdkRGBA color;
+  gdouble progress;
+  gdouble radius;
 
-          cairo_save (cr);
+  state = gtk_theming_engine_get_state (engine);
 
-          cairo_set_source_rgba (cr,
-                                 color->red,
-                                 color->green,
-                                 color->blue,
-                                 color->alpha * t);
-
-          cairo_set_line_width (cr, 2.0);
-          cairo_move_to (cr,
-                         dx + (radius - inset) * cos (i * G_PI / half),
-                         dy + (radius - inset) * sin (i * G_PI / half));
-          cairo_line_to (cr,
-                         dx + radius * cos (i * G_PI / half),
-                         dy + radius * sin (i * G_PI / half));
-          cairo_stroke (cr);
+  if (!gtk_theming_engine_state_is_running (engine, GTK_STATE_ACTIVE, &progress))
+    progress = -1;
 
-          cairo_restore (cr);
-        }
+  radius = MIN (width / 2, height / 2);
 
-      cairo_restore (cr);
+  gtk_theming_engine_get_color (engine, state, &color);
+  gtk_theming_engine_get (engine, state,
+                          "icon-shadow", &shadow,
+                          NULL);
 
-      gdk_rgba_free (color);
+  cairo_save (cr);
+  cairo_translate (cr, x + width / 2, y + height / 2);
+
+  if (shadow != NULL)
+    {
+      _gtk_icon_shadow_paint_spinner (shadow, cr,
+                                      radius,
+                                      progress);
+      _gtk_shadow_unref (shadow);
+    }
+
+  _gtk_theming_engine_paint_spinner (cr,
+                                     radius,
+                                     progress,
+                                     &color);
+
+  cairo_restore (cr);
+}
+
+static void
+gtk_theming_engine_render_activity (GtkThemingEngine *engine,
+                                    cairo_t          *cr,
+                                    gdouble           x,
+                                    gdouble           y,
+                                    gdouble           width,
+                                    gdouble           height)
+{
+  if (gtk_theming_engine_has_class (engine, GTK_STYLE_CLASS_SPINNER))
+    {
+      render_spinner (engine, cr, x, y, width, height);
     }
   else
     {
@@ -3070,53 +3152,24 @@ lookup_icon_size (GtkThemingEngine *engine,
   return gtk_icon_size_lookup_for_settings (settings, size, width, height);
 }
 
-/* Kudos to the gnome-panel guys. */
 static void
-colorshift_pixbuf (GdkPixbuf *src,
-                   GdkPixbuf *dest,
-                   gint       shift)
-{
-  gint i, j;
-  gint width, height, has_alpha, src_rowstride, dest_rowstride;
-  guchar *target_pixels;
-  guchar *original_pixels;
-  guchar *pix_src;
-  guchar *pix_dest;
-  int val;
-  guchar r, g, b;
-
-  has_alpha       = gdk_pixbuf_get_has_alpha (src);
-  width           = gdk_pixbuf_get_width (src);
-  height          = gdk_pixbuf_get_height (src);
-  src_rowstride   = gdk_pixbuf_get_rowstride (src);
-  dest_rowstride  = gdk_pixbuf_get_rowstride (dest);
-  original_pixels = gdk_pixbuf_get_pixels (src);
-  target_pixels   = gdk_pixbuf_get_pixels (dest);
-
-  for (i = 0; i < height; i++)
-    {
-      pix_dest = target_pixels   + i * dest_rowstride;
-      pix_src  = original_pixels + i * src_rowstride;
+colorshift_source (cairo_t *cr,
+                  gdouble shift)
+{
+  cairo_pattern_t *source;
 
-      for (j = 0; j < width; j++)
-        {
-          r = *(pix_src++);
-          g = *(pix_src++);
-          b = *(pix_src++);
+  cairo_save (cr);
+  cairo_paint (cr);
 
-          val = r + shift;
-          *(pix_dest++) = CLAMP (val, 0, 255);
+  source = cairo_pattern_reference (cairo_get_source (cr));
 
-          val = g + shift;
-          *(pix_dest++) = CLAMP (val, 0, 255);
+  cairo_set_source_rgb (cr, shift, shift, shift);
+  cairo_set_operator (cr, CAIRO_OPERATOR_COLOR_DODGE);
 
-          val = b + shift;
-          *(pix_dest++) = CLAMP (val, 0, 255);
+  cairo_mask (cr, source);
 
-          if (has_alpha)
-            *(pix_dest++) = *(pix_src++);
-        }
-    }
+  cairo_pattern_destroy (source);
+  cairo_restore (cr);
 }
 
 static GdkPixbuf *
@@ -3130,6 +3183,8 @@ gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
   GtkStateFlags state;
   gint width = 1;
   gint height = 1;
+  cairo_t *cr;
+  cairo_surface_t *surface;
 
   base_pixbuf = gtk_icon_source_get_pixbuf (source);
   state = gtk_theming_engine_get_state (engine);
@@ -3157,16 +3212,36 @@ gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
     {
       if (state & GTK_STATE_FLAG_INSENSITIVE)
         {
-          stated = gdk_pixbuf_copy (scaled);
-          gdk_pixbuf_saturate_and_pixelate (scaled, stated,
-                                            0.8, TRUE);
-          g_object_unref (scaled);
+         surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                               gdk_pixbuf_get_width (scaled),
+                                               gdk_pixbuf_get_height (scaled));
+         cr = cairo_create (surface);
+         gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
+         cairo_paint_with_alpha (cr, 0.5);
+
+         cairo_destroy (cr);
+
+         g_object_unref (scaled);
+         stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
+                                               cairo_image_surface_get_width (surface),
+                                               cairo_image_surface_get_height (surface));
         }
       else if (state & GTK_STATE_FLAG_PRELIGHT)
         {
-          stated = gdk_pixbuf_copy (scaled);
-          colorshift_pixbuf (scaled, stated, 30);
-          g_object_unref (scaled);
+         surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                               gdk_pixbuf_get_width (scaled),
+                                               gdk_pixbuf_get_height (scaled));
+
+         cr = cairo_create (surface);
+         gdk_cairo_set_source_pixbuf (cr, scaled, 0, 0);
+         colorshift_source (cr, 0.10);
+
+         cairo_destroy (cr);
+
+         g_object_unref (scaled);
+         stated = gdk_pixbuf_get_from_surface (surface, 0, 0,
+                                               cairo_image_surface_get_width (surface),
+                                               cairo_image_surface_get_height (surface));
         }
       else
         stated = scaled;
@@ -3176,3 +3251,35 @@ gtk_theming_engine_render_icon_pixbuf (GtkThemingEngine    *engine,
 
   return stated;
 }
+
+static void
+gtk_theming_engine_render_icon (GtkThemingEngine *engine,
+                                cairo_t *cr,
+                               GdkPixbuf *pixbuf,
+                                gdouble x,
+                                gdouble y)
+{
+  GtkStateFlags state;
+  GtkShadow *icon_shadow;
+
+  state = gtk_theming_engine_get_state (engine);
+
+  cairo_save (cr);
+
+  gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
+
+  gtk_theming_engine_get (engine, state,
+                          "icon-shadow", &icon_shadow,
+                          NULL);
+
+  if (icon_shadow != NULL)
+    {
+      _gtk_icon_shadow_paint (icon_shadow, cr);
+      _gtk_shadow_unref (icon_shadow);
+    }
+
+  cairo_paint (cr);
+
+  cairo_restore (cr);
+}
+