]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkborderimage.c
gdk/gdkwindow.c, gtk/gtkwidget.c: Include fallback-c89.c
[~andy/gtk] / gtk / gtkborderimage.c
index cf9da9cf0a804b4b32a63b9fa82344e81a9363b0..694db6fe0a86c079643af176c142f6321b52a1aa 100644 (file)
@@ -16,9 +16,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <math.h>
 
 #include "gtkborderimageprivate.h"
+#include "gtkcssbordervalueprivate.h"
+#include "gtkcssimagevalueprivate.h"
+#include "gtkcssnumbervalueprivate.h"
+#include "gtkcssrepeatvalueprivate.h"
+#include "gtkstylepropertiesprivate.h"
+#include "gtkthemingengineprivate.h"
+
+/* this is in case round() is not provided by the compiler, 
+ * such as in the case of C89 compilers, like MSVC
+ */
+#include "fallback-c89.c"
 
-G_DEFINE_BOXED_TYPE (GtkBorderImage, _gtk_border_image,
-                     _gtk_border_image_ref, _gtk_border_image_unref)
-
-enum {
-  BORDER_LEFT,
-  BORDER_MIDDLE,
-  BORDER_RIGHT,
-  BORDER_LAST,
-  BORDER_TOP = BORDER_LEFT,
-  BORDER_BOTTOM = BORDER_RIGHT
-};
-
-enum {
-  SIDE_TOP,
-  SIDE_RIGHT,
-  SIDE_BOTTOM,
-  SIDE_LEFT
-};
-
-struct _GtkBorderImage {
-  cairo_pattern_t *source;
-  GtkGradient *source_gradient;
-
-  GtkBorder slice;
-  GtkCssBorderImageRepeat repeat;
-
-  gint ref_count;
-};
-
-GtkBorderImage *
-_gtk_border_image_new (cairo_pattern_t      *pattern,
-                       GtkBorder            *slice,
-                       GtkCssBorderImageRepeat *repeat)
-{
-  GtkBorderImage *image;
-
-  image = g_slice_new0 (GtkBorderImage);
-  image->ref_count = 1;
-
-  if (pattern != NULL)
-    image->source = cairo_pattern_reference (pattern);
-
-  if (slice != NULL)
-    image->slice = *slice;
-
-  if (repeat != NULL)
-    image->repeat = *repeat;
-
-  return image;
-}
-
-GtkBorderImage *
-_gtk_border_image_new_for_gradient (GtkGradient          *gradient,
-                                    GtkBorder            *slice,
-                                    GtkCssBorderImageRepeat *repeat)
-{
-  GtkBorderImage *image;
-
-  image = g_slice_new0 (GtkBorderImage);
-  image->ref_count = 1;
-
-  if (gradient != NULL)
-    image->source_gradient = gtk_gradient_ref (gradient);
-
-  if (slice != NULL)
-    image->slice = *slice;
-
-  if (repeat != NULL)
-    image->repeat = *repeat;
-
-  return image;  
-}
-
-GtkBorderImage *
-_gtk_border_image_ref (GtkBorderImage *image)
-{
-  g_return_val_if_fail (image != NULL, NULL);
-
-  image->ref_count++;
-
-  return image;
-}
-
-void
-_gtk_border_image_unref (GtkBorderImage *image)
-{
-  g_return_if_fail (image != NULL);
-
-  image->ref_count--;
-
-  if (image->ref_count == 0)
-    {
-      if (image->source != NULL)
-        cairo_pattern_destroy (image->source);
-
-      if (image->source_gradient != NULL)
-        gtk_gradient_unref (image->source_gradient);
-
-      g_slice_free (GtkBorderImage, image);
-    }
-}
-
-GParameter *
-_gtk_border_image_unpack (const GValue *value,
-                          guint        *n_params)
+gboolean
+_gtk_border_image_init (GtkBorderImage   *image,
+                        GtkThemingEngine *engine)
 {
-  GParameter *parameter = g_new0 (GParameter, 3);
-  GtkBorderImage *image = g_value_get_boxed (value);
-
-  parameter[0].name = "border-image-source";
-  g_value_init (&parameter[0].value, CAIRO_GOBJECT_TYPE_PATTERN);
-  g_value_set_boxed (&parameter[0].value, image->source);
+  image->source = _gtk_css_image_value_get_image (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE));
+  if (image->source == NULL)
+    return FALSE;
 
-  parameter[1].name = "border-image-slice";
-  g_value_init (&parameter[1].value, GTK_TYPE_BORDER);
-  g_value_set_boxed (&parameter[1].value, &image->slice);
+  image->slice = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE);
+  image->width = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH);
+  image->repeat = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT);
 
-  parameter[2].name = "border-image-repeat";
-  g_value_init (&parameter[2].value, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
-  g_value_set_boxed (&parameter[2].value, &image->repeat);
-
-  *n_params = 3;
-  return parameter;
+  return TRUE;
 }
 
-void
-_gtk_border_image_pack (GValue             *value,
-                        GtkStyleProperties *props,
-                        GtkStateFlags       state)
-{
-  GtkBorderImage *image;
-  cairo_pattern_t *source;
-  GtkBorder *slice;
-  GtkCssBorderImageRepeat *repeat;
-
-  gtk_style_properties_get (props, state,
-                            "border-image-source", &source,
-                            "border-image-slice", &slice,
-                            "border-image-repeat", &repeat,
-                            NULL);
-
-  if (source == NULL)
-    {
-      g_value_take_boxed (value, NULL);
-    }
-  else
-    {
-      image = _gtk_border_image_new (source, slice, repeat);
-      g_value_take_boxed (value, image);
-
-      cairo_pattern_destroy (source);
-    }
-
-  if (slice != NULL)
-    gtk_border_free (slice);
-
-  if (repeat != NULL)
-    g_free (repeat);
-}
+typedef struct _GtkBorderImageSliceSize GtkBorderImageSliceSize;
+struct _GtkBorderImageSliceSize {
+  double offset;
+  double size;
+};
 
 static void
-render_corner (cairo_t         *cr,
-               gdouble          corner_x,
-               gdouble          corner_y,
-               gdouble          corner_width,
-               gdouble          corner_height,
-               cairo_surface_t *surface,
-               gdouble          image_width,
-               gdouble          image_height)
+gtk_border_image_compute_border_size (GtkBorderImageSliceSize  sizes[3],
+                                      double                   offset,
+                                      double                   area_size,
+                                      double                   start_border_width,
+                                      double                   end_border_width,
+                                      const GtkCssValue       *start_border,
+                                      const GtkCssValue       *end_border)
 {
-  if (corner_width == 0 || corner_height == 0)
-    return;
-
-  cairo_save (cr);
+  double start, end;
 
-  cairo_translate (cr, corner_x, corner_y);
-  cairo_scale (cr,
-               corner_width / image_width,
-               corner_height / image_height);
-  cairo_set_source_surface (cr, surface, 0, 0);
-
-  /* use the nearest filter for scaling, to avoid color blending */
-  cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
-
-  cairo_paint (cr);
-
-  cairo_restore (cr);
-}
+  if (_gtk_css_number_value_get_unit (start_border) == GTK_CSS_NUMBER)
+    start = start_border_width * _gtk_css_number_value_get (start_border, 100);
+  else
+    start = _gtk_css_number_value_get (start_border, area_size);
+  if (_gtk_css_number_value_get_unit (end_border) == GTK_CSS_NUMBER)
+    end = end_border_width * _gtk_css_number_value_get (end_border, 100);
+  else
+    end = _gtk_css_number_value_get (end_border, area_size);
 
-static cairo_surface_t *
-create_spaced_surface (cairo_surface_t *tile,
-                       gdouble          tile_width,
-                       gdouble          tile_height,
-                       gdouble          width,
-                       gdouble          height,
-                       GtkOrientation   orientation)
-{
-  gint n_repeats, idx;
-  gdouble avail_space, step;
-  cairo_surface_t *retval;
-  cairo_t *cr;
-
-  n_repeats = (orientation == GTK_ORIENTATION_HORIZONTAL) ?
-    (gint) floor (width / tile_width) :
-    (gint) floor (height / tile_height);
-
-  avail_space = (orientation == GTK_ORIENTATION_HORIZONTAL) ?
-    width - (n_repeats * tile_width) :
-    height - (n_repeats * tile_height);
-  step = avail_space / (n_repeats + 2);
-
-  retval = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-                                       width, height);
-  cr = cairo_create (retval);
-  idx = 0;
-
-  while (idx < n_repeats)
+  /* XXX: reduce vertical and horizontal by the same factor */
+  if (start + end > area_size)
     {
-      cairo_save (cr);
-
-      if (orientation == GTK_ORIENTATION_HORIZONTAL)
-        cairo_set_source_surface (cr, tile,
-                                  ((idx + 1) * step) + (idx * tile_width), 0);
-      else
-        cairo_set_source_surface (cr, tile,
-                                  0, ((idx + 1 ) * step) + (idx * tile_height));
-
-      cairo_paint (cr);
-      cairo_restore (cr);
-
-      idx++;
+      start = start * area_size / (start + end);
+      end = end * area_size / (start + end);
     }
 
-  cairo_destroy (cr);
-
-  return retval;
+  sizes[0].offset = offset;
+  sizes[0].size = start;
+  sizes[1].offset = offset + start;
+  sizes[1].size = area_size - start - end;
+  sizes[2].offset = offset + area_size - end;
+  sizes[2].size = end;
 }
 
 static void
-render_border (cairo_t              *cr,
-               gdouble               total_width,
-               gdouble               total_height,
-               cairo_surface_t      *surface,
-               gdouble               surface_width,
-               gdouble               surface_height,
-               guint                 side,
-               GtkBorder            *border_area,
-               GtkCssBorderImageRepeat *repeat)
+gtk_border_image_render_slice (cairo_t           *cr,
+                               cairo_surface_t   *slice,
+                               double             slice_width,
+                               double             slice_height,
+                               double             x,
+                               double             y,
+                               double             width,
+                               double             height,
+                               GtkCssRepeatStyle  hrepeat,
+                               GtkCssRepeatStyle  vrepeat)
 {
-  gdouble target_x, target_y;
-  gdouble target_width, target_height;
-  GdkRectangle image_area;
+  double hscale, vscale;
+  double xstep, ystep;
+  cairo_extend_t extend = CAIRO_EXTEND_PAD;
+  cairo_matrix_t matrix;
   cairo_pattern_t *pattern;
-  gboolean repeat_pattern;
 
-  if (surface == NULL)
-    return;
+  /* We can't draw center tiles yet */
+  g_assert (hrepeat == GTK_CSS_REPEAT_STYLE_STRETCH || vrepeat == GTK_CSS_REPEAT_STYLE_STRETCH);
 
-  cairo_surface_reference (surface);
-  repeat_pattern = FALSE;
+  hscale = width / slice_width;
+  vscale = height / slice_height;
+  xstep = width;
+  ystep = height;
 
-  if (side == SIDE_TOP || side == SIDE_BOTTOM)
+  switch (hrepeat)
     {
-      target_height = (side == SIDE_TOP) ? (border_area->top) : (border_area->bottom);
-      target_width = surface_width * (target_height / surface_height);
-    }
-  else
-    {
-      target_width = (side == SIDE_LEFT) ? (border_area->left) : (border_area->right);
-      target_height = surface_height * (target_width / surface_width);
+    case GTK_CSS_REPEAT_STYLE_REPEAT:
+      extend = CAIRO_EXTEND_REPEAT;
+      hscale = vscale;
+      break;
+    case GTK_CSS_REPEAT_STYLE_SPACE:
+      {
+        double space, n;
+
+        extend = CAIRO_EXTEND_NONE;
+        hscale = vscale;
+
+        xstep = hscale * slice_width;
+        n = floor (width / xstep);
+        space = (width - n * xstep) / (n + 1);
+        xstep += space;
+        x += space;
+        width -= 2 * space;
+      }
+      break;
+    case GTK_CSS_REPEAT_STYLE_STRETCH:
+      break;
+    case GTK_CSS_REPEAT_STYLE_ROUND:
+      extend = CAIRO_EXTEND_REPEAT;
+      hscale = width / (slice_width * MAX (round (width / (slice_width * vscale)), 1));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
     }
 
-  if (side == SIDE_TOP || side == SIDE_BOTTOM)
+  switch (vrepeat)
     {
-      image_area.x = border_area->left;
-      image_area.y = (side == SIDE_TOP) ? 0 : (total_height - border_area->bottom);
-      image_area.width = total_width - border_area->left - border_area->right;
-      image_area.height = (side == SIDE_TOP) ? border_area->top : border_area->bottom;
-
-      target_x = border_area->left;
-      target_y = (side == SIDE_TOP) ? 0 : (total_height - border_area->bottom);
-
-      if (repeat->vrepeat == GTK_CSS_REPEAT_STYLE_NONE)
-        {
-          target_width = image_area.width;
-        }
-      else if (repeat->vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT)
-        {
-          repeat_pattern = TRUE;
-
-          target_x = border_area->left + (total_width - border_area->left - border_area->right) / 2;
-          target_y = ((side == SIDE_TOP) ? 0 : (total_height - border_area->bottom)) / 2;
-        }
-      else if (repeat->vrepeat == GTK_CSS_REPEAT_STYLE_ROUND)
-        {
-          gint n_repeats;
-
-          repeat_pattern = TRUE;
-
-          n_repeats = (gint) floor (image_area.width / surface_width);
-          target_width = image_area.width / n_repeats;
-        }
-      else if (repeat->vrepeat == GTK_CSS_REPEAT_STYLE_SPACE)
-        {
-          cairo_surface_t *spaced_surface;
-
-          spaced_surface = create_spaced_surface (surface,
-                                                  surface_width, surface_height,
-                                                  image_area.width, surface_height,
-                                                  GTK_ORIENTATION_HORIZONTAL);
-          cairo_surface_destroy (surface);
-          surface = spaced_surface;
-
-          /* short-circuit hscaling */
-          target_width = surface_width = cairo_image_surface_get_width (spaced_surface);
-        }
+    case GTK_CSS_REPEAT_STYLE_REPEAT:
+      extend = CAIRO_EXTEND_REPEAT;
+      vscale = hscale;
+      break;
+    case GTK_CSS_REPEAT_STYLE_SPACE:
+      {
+        double space, n;
+
+        extend = CAIRO_EXTEND_NONE;
+        vscale = hscale;
+
+        ystep = vscale * slice_height;
+        n = floor (height / ystep);
+        space = (height - n * ystep) / (n + 1);
+        ystep += space;
+        y += space;
+        height -= 2 * space;
+      }
+      break;
+    case GTK_CSS_REPEAT_STYLE_STRETCH:
+      break;
+    case GTK_CSS_REPEAT_STYLE_ROUND:
+      extend = CAIRO_EXTEND_REPEAT;
+      vscale = height / (slice_height * MAX (round (height / (slice_height * hscale)), 1));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
     }
-  else
-    {
-      image_area.x = (side == SIDE_LEFT) ? 0 : (total_width - border_area->right);
-      image_area.y = border_area->top;
-      image_area.width = (side == SIDE_LEFT) ? border_area->left : border_area->right;
-      image_area.height = total_height - border_area->top - border_area->bottom;
 
-      target_x = (side == SIDE_LEFT) ? 0 : (total_width - border_area->right);
-      target_y = border_area->top;
+  pattern = cairo_pattern_create_for_surface (slice);
 
-      if (repeat->hrepeat == GTK_CSS_REPEAT_STYLE_NONE)
-        {
-          target_height = total_height - border_area->top - border_area->bottom;
-        }
-      else if (repeat->hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT)
-        {
-          repeat_pattern = TRUE;
+  cairo_matrix_init_translate (&matrix,
+                               hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_width / 2 : 0,
+                               vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_height / 2 : 0);
+  cairo_matrix_scale (&matrix, 1 / hscale, 1 / vscale);
+  cairo_matrix_translate (&matrix,
+                          hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - width / 2 : 0,
+                          vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - height / 2 : 0);
 
-          target_height = total_height - border_area->top - border_area->bottom;
-          target_x = (side == SIDE_LEFT) ? 0 : (total_width - border_area->right) / 2;
-          target_y = border_area->top + (total_height - border_area->top - border_area->bottom) / 2;
-        }
-      else if (repeat->hrepeat == GTK_CSS_REPEAT_STYLE_ROUND)
-        {
-          gint n_repeats;
+  cairo_pattern_set_matrix (pattern, &matrix);
+  cairo_pattern_set_extend (pattern, extend);
 
-          repeat_pattern = TRUE;
+  cairo_save (cr);
+  cairo_translate (cr, x, y);
 
-          n_repeats = (gint) floor (image_area.height / surface_height);
-          target_height = image_area.height / n_repeats;
-        }
-      else if (repeat->hrepeat == GTK_CSS_REPEAT_STYLE_SPACE)
+  for (y = 0; y < height; y += ystep)
+    {
+      for (x = 0; x < width; x += xstep)
         {
-          cairo_surface_t *spaced_surface;
-
-          spaced_surface = create_spaced_surface (surface,
-                                                  surface_width, surface_height,
-                                                  surface_width, image_area.height,
-                                                  GTK_ORIENTATION_VERTICAL);
-          cairo_surface_destroy (surface);
-          surface = spaced_surface;
-
-          /* short-circuit vscaling */
-          target_height = surface_height = cairo_image_surface_get_height (spaced_surface);
+          cairo_save (cr);
+          cairo_translate (cr, x, y);
+          cairo_set_source (cr, pattern);
+          cairo_rectangle (cr, 0, 0, xstep, ystep);
+          cairo_fill (cr);
+          cairo_restore (cr);
         }
     }
 
-  if (target_width == 0 || target_height == 0)
-    return;
-
-  cairo_save (cr);
-
-  pattern = cairo_pattern_create_for_surface (surface);
-
-  gdk_cairo_rectangle (cr, &image_area);
-  cairo_clip (cr);
-
-  cairo_translate (cr,
-                   target_x, target_y);
-
-  /* use the nearest filter for scaling, to avoid color blending */
-  cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
-  
-  if (repeat_pattern)
-    cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
+  cairo_restore (cr);
 
-  cairo_scale (cr,
-               target_width / surface_width,
-               target_height / surface_height);
+  cairo_pattern_destroy (pattern);
+}
 
-  cairo_set_source (cr, pattern);
-  cairo_paint (cr);
+static void
+gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
+                                     int                     surface_size,
+                                     int                     start_size,
+                                     int                     end_size)
+{
+  sizes[0].size = MIN (start_size, surface_size);
+  sizes[0].offset = 0;
 
-  cairo_restore (cr);
+  sizes[2].size = MIN (end_size, surface_size);
+  sizes[2].offset = surface_size - sizes[2].size;
 
-  cairo_pattern_destroy (pattern);
-  cairo_surface_destroy (surface);
+  sizes[1].size = MAX (0, surface_size - sizes[0].size - sizes[2].size);
+  sizes[1].offset = sizes[0].size;
 }
 
 void
 _gtk_border_image_render (GtkBorderImage   *image,
-                          GtkBorder        *border_width,
+                          const double      border_width[4],
                           cairo_t          *cr,
                           gdouble           x,
                           gdouble           y,
@@ -434,176 +243,80 @@ _gtk_border_image_render (GtkBorderImage   *image,
                           gdouble           height)
 {
   cairo_surface_t *surface, *slice;
-  gdouble slice_width, slice_height, surface_width, surface_height;
-
-  if (cairo_pattern_get_type (image->source) != CAIRO_PATTERN_TYPE_SURFACE)
-    {
-      cairo_matrix_t matrix;
-      cairo_t *surface_cr;
-
-      surface_width = width;
-      surface_height = height;
-
-      cairo_matrix_init_scale (&matrix, 1 / width, 1 / height);
-      cairo_pattern_set_matrix (image->source, &matrix);
-
-      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
-      surface_cr = cairo_create (surface);
-      cairo_set_source (surface_cr, image->source);
-      cairo_paint (surface_cr);
-
-      cairo_destroy (surface_cr);
-    }
-  else
-    {
-      cairo_pattern_get_surface (image->source, &surface);
-      cairo_surface_reference (surface);
-
-      surface_width = cairo_image_surface_get_width (surface);
-      surface_height = cairo_image_surface_get_height (surface);
-    }
-
-  cairo_save (cr);
-  cairo_translate (cr, x, y);
-
-  if ((image->slice.left + image->slice.right) < surface_width)
+  GtkBorderImageSliceSize vertical_slice[3], horizontal_slice[3];
+  GtkBorderImageSliceSize vertical_border[3], horizontal_border[3];
+  double source_width, source_height;
+  int h, v;
+
+  _gtk_css_image_get_concrete_size (image->source,
+                                    0, 0,
+                                    width, height,
+                                    &source_width, &source_height);
+
+  /* XXX: Optimize for (source_width == width && source_height == height) */
+
+  surface = _gtk_css_image_get_surface (image->source,
+                                        cairo_get_target (cr),
+                                        source_width, source_height);
+
+  gtk_border_image_compute_slice_size (horizontal_slice,
+                                       source_width, 
+                                       _gtk_css_number_value_get (_gtk_css_border_value_get_left (image->slice), source_width),
+                                       _gtk_css_number_value_get (_gtk_css_border_value_get_right (image->slice), source_width));
+  gtk_border_image_compute_slice_size (vertical_slice,
+                                       source_height, 
+                                       _gtk_css_number_value_get (_gtk_css_border_value_get_top (image->slice), source_height),
+                                       _gtk_css_number_value_get (_gtk_css_border_value_get_bottom (image->slice), source_height));
+  gtk_border_image_compute_border_size (horizontal_border,
+                                        x,
+                                        width,
+                                        border_width[GTK_CSS_LEFT],
+                                        border_width[GTK_CSS_RIGHT],
+                                        _gtk_css_border_value_get_left (image->width),
+                                        _gtk_css_border_value_get_right (image->width));
+  gtk_border_image_compute_border_size (vertical_border,
+                                        y,
+                                        height,
+                                        border_width[GTK_CSS_TOP],
+                                        border_width[GTK_CSS_BOTTOM],
+                                        _gtk_css_border_value_get_top (image->width),
+                                        _gtk_css_border_value_get_bottom(image->width));
+  
+  for (v = 0; v < 3; v++)
     {
-      /* Top side */
-      slice_width = surface_width - image->slice.left - image->slice.right;
-      slice_height = image->slice.top;
-      slice = cairo_surface_create_for_rectangle
-        (surface,
-         image->slice.left, 0,
-         slice_width, slice_height);
-
-      render_border (cr,
-                     width, height,
-                     slice,
-                     slice_width, slice_height,
-                     SIDE_TOP,
-                     border_width,
-                     &image->repeat);
-
-      cairo_surface_destroy (slice);
-
-      /* Bottom side */
-      slice_height = image->slice.bottom;
-      slice = cairo_surface_create_for_rectangle
-        (surface,
-         image->slice.left, surface_height - image->slice.bottom,
-         slice_width, slice_height);
-
-      render_border (cr,
-                     width, height,
-                     slice,
-                     slice_width, slice_height,
-                     SIDE_BOTTOM,
-                     border_width,
-                     &image->repeat);
-
-      cairo_surface_destroy (slice);
-    }
+      if (vertical_slice[v].size == 0 ||
+          vertical_border[v].size == 0)
+        continue;
 
-  if ((image->slice.top + image->slice.bottom) < surface_height)
-    {
-      /* Left side */
-      slice_width = image->slice.left;
-      slice_height = surface_height - image->slice.top - image->slice.bottom;
-      slice = cairo_surface_create_for_rectangle
-        (surface,
-         0, image->slice.top,
-         slice_width, slice_height);
-
-      render_border (cr,
-                     width, height,
-                     slice,
-                     slice_width, slice_height,
-                     SIDE_LEFT,
-                     border_width,
-                     &image->repeat);
-
-      cairo_surface_destroy (slice);
-
-      /* Right side */
-      slice_width = image->slice.right;
-      slice = cairo_surface_create_for_rectangle
-        (surface, 
-         surface_width - image->slice.right, image->slice.top,
-         slice_width, slice_height);
-
-      render_border (cr,
-                     width, height,
-                     slice,
-                     slice_width, slice_height,
-                     SIDE_RIGHT,
-                     border_width,
-                     &image->repeat);
-
-      cairo_surface_destroy (slice);
+      for (h = 0; h < 3; h++)
+        {
+          if (horizontal_slice[h].size == 0 ||
+              horizontal_border[h].size == 0)
+            continue;
+
+          if (h == 1 && v == 1)
+            continue;
+
+          slice = cairo_surface_create_for_rectangle (surface,
+                                                      horizontal_slice[h].offset,
+                                                      vertical_slice[v].offset,
+                                                      horizontal_slice[h].size,
+                                                      vertical_slice[v].size);
+
+          gtk_border_image_render_slice (cr,
+                                         slice,
+                                         horizontal_slice[h].size,
+                                         vertical_slice[v].size,
+                                         horizontal_border[h].offset,
+                                         vertical_border[v].offset,
+                                         horizontal_border[h].size,
+                                         vertical_border[v].size,
+                                         h == 1 ? _gtk_css_border_repeat_value_get_x (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH,
+                                         v == 1 ? _gtk_css_border_repeat_value_get_y (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH);
+
+          cairo_surface_destroy (slice);
+        }
     }
 
-  /* Top/left corner */
-  slice_width = image->slice.left;
-  slice_height = image->slice.top;
-  slice = cairo_surface_create_for_rectangle
-    (surface, 
-     0, 0,
-     slice_width, slice_height);
-
-  render_corner (cr,
-                 0, 0,
-                 border_width->left, border_width->top,
-                 slice,
-                 slice_width, slice_height);
-
-  cairo_surface_destroy (slice);
-
-  /* Top/right corner */
-  slice_width = image->slice.right;
-  slice = cairo_surface_create_for_rectangle
-    (surface,
-     surface_width - image->slice.right, 0,
-     slice_width, slice_height);
-
-  render_corner (cr,
-                 width - border_width->right, 0,
-                 border_width->right, border_width->top,
-                 slice,
-                 slice_width, slice_height);
-
-  cairo_surface_destroy (slice);
-
-  /* Bottom/left corner */
-  slice_width = image->slice.left;
-  slice_height = image->slice.bottom;
-  slice = cairo_surface_create_for_rectangle
-    (surface,
-     0, surface_height - image->slice.bottom,
-     slice_width, slice_height);
-
-  render_corner (cr,
-                 0, height - border_width->bottom,
-                 border_width->left, border_width->bottom,
-                 slice,
-                 slice_width, slice_height);
-
-  cairo_surface_destroy (slice);
-
-  /* Bottom/right corner */
-  slice_width = image->slice.right;
-  slice = cairo_surface_create_for_rectangle
-    (surface,
-     surface_width - image->slice.right,
-     surface_height - image->slice.bottom,
-     slice_width, slice_height);
-
-  render_corner (cr,
-                 width - border_width->right, height - border_width->bottom,
-                 border_width->right, border_width->bottom,
-                 slice,
-                 slice_width, slice_height);
-
-  cairo_surface_destroy (slice);
-
-  cairo_restore (cr);
+  cairo_surface_destroy (surface);
 }