diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 1fd9555d02..9e37c3a944 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -375,7 +375,6 @@ endif # GTK+ header files that don't get installed gtk_private_h_sources = \ - gtk9slice.h \ gtkaccelgroupprivate.h \ gtkaccessibleprivate.h \ gtkanimationdescription.h \ @@ -383,6 +382,7 @@ gtk_private_h_sources = \ gtkappchoosermodule.h \ gtkappchooseronline.h \ gtkbindingsprivate.h \ + gtkborderimageprivate.h \ gtkbuilderprivate.h \ gtkbuttonprivate.h \ gtkcellareaboxcontextprivate.h \ @@ -457,7 +457,6 @@ gtk_private_h_sources = \ # GTK+ C sources to build the library from gtk_base_c_sources = \ - gtk9slice.c \ gtkquery.c \ gtksearchengine.c \ gtksearchenginesimple.c \ @@ -487,6 +486,7 @@ gtk_base_c_sources = \ gtkbin.c \ gtkbindings.c \ gtkborder.c \ + gtkborderimage.c \ gtkbox.c \ gtkbuildable.c \ gtkbuilder.c \ diff --git a/gtk/gtk9slice.c b/gtk/gtk9slice.c deleted file mode 100644 index 1f2e517131..0000000000 --- a/gtk/gtk9slice.c +++ /dev/null @@ -1,375 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2010 Carlos Garnacho - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * 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. - */ - -#include "config.h" -#include "gtk9slice.h" - -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 Gtk9Slice -{ - cairo_surface_t *surfaces[BORDER_LAST][BORDER_LAST]; - GtkSliceSideModifier modifiers[4]; - gdouble distances[4]; - gint ref_count; -}; - -G_DEFINE_BOXED_TYPE (Gtk9Slice, _gtk_9slice, - _gtk_9slice_ref, _gtk_9slice_unref) - - -Gtk9Slice * -_gtk_9slice_new (GdkPixbuf *pixbuf, - gdouble distance_top, - gdouble distance_bottom, - gdouble distance_left, - gdouble distance_right, - GtkSliceSideModifier horizontal_modifier, - GtkSliceSideModifier vertical_modifier) -{ - Gtk9Slice *slice; - cairo_surface_t *surface; - gint width, height; - cairo_t *cr; - - g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL); - - slice = g_slice_new0 (Gtk9Slice); - slice->ref_count = 1; - - slice->distances[SIDE_TOP] = distance_top; - slice->distances[SIDE_BOTTOM] = distance_bottom; - slice->distances[SIDE_LEFT] = distance_left; - slice->distances[SIDE_RIGHT] = distance_right; - - slice->modifiers[SIDE_TOP] = slice->modifiers[SIDE_BOTTOM] = horizontal_modifier; - slice->modifiers[SIDE_LEFT] = slice->modifiers[SIDE_RIGHT] = vertical_modifier; - - width = gdk_pixbuf_get_width (pixbuf); - height = gdk_pixbuf_get_height (pixbuf); - - /* Get an image surface from the pixbuf */ - surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); - cr = cairo_create (surface); - - gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); - cairo_paint (cr); - - cairo_destroy (cr); - - /* Top /left corner */ - slice->surfaces[BORDER_LEFT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, - distance_left, distance_top); - cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_TOP]); - - cairo_set_source_surface (cr, surface, 0, 0); - cairo_paint (cr); - - cairo_destroy (cr); - - /* Top/right corner */ - slice->surfaces[BORDER_RIGHT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, - distance_right, distance_top); - cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_TOP]); - - cairo_set_source_surface (cr, surface, - width + distance_right, 0); - cairo_paint (cr); - - cairo_destroy (cr); - - /* Bottom/left corner */ - slice->surfaces[BORDER_LEFT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, - distance_left, distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_BOTTOM]); - - cairo_set_source_surface (cr, surface, 0, - height + distance_bottom); - cairo_paint (cr); - - cairo_destroy (cr); - - /* Bottom/right corner */ - slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, - distance_right, distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM]); - - cairo_set_source_surface (cr, surface, - width + distance_right, - height + distance_bottom); - cairo_paint (cr); - - cairo_destroy (cr); - - /* Top side */ - slice->surfaces[BORDER_MIDDLE][BORDER_TOP] = cairo_surface_create_similar (surface, - CAIRO_CONTENT_COLOR_ALPHA, - width - distance_left - distance_right, - distance_top); - cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_TOP]); - cairo_set_source_surface (cr, surface, - distance_left, 0); - cairo_paint (cr); - cairo_destroy (cr); - - /* Bottom side */ - slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM] = cairo_surface_create_similar (surface, - CAIRO_CONTENT_COLOR_ALPHA, - width - distance_left - distance_right, - distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]); - cairo_set_source_surface (cr, surface, - distance_left, - height + distance_bottom); - cairo_paint (cr); - cairo_destroy (cr); - - /* Left side */ - slice->surfaces[BORDER_LEFT][BORDER_MIDDLE] = cairo_surface_create_similar (surface, - CAIRO_CONTENT_COLOR_ALPHA, - distance_left, - height - distance_top - distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_MIDDLE]); - cairo_set_source_surface (cr, surface, 0, - distance_top); - cairo_paint (cr); - cairo_destroy (cr); - - /* Right side */ - slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE] = cairo_surface_create_similar (surface, - CAIRO_CONTENT_COLOR_ALPHA, - distance_right, - height - distance_top - distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE]); - cairo_set_source_surface (cr, surface, - width + distance_right, - distance_top); - cairo_paint (cr); - cairo_destroy (cr); - - /* Center */ - slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE] = cairo_surface_create_similar (surface, - CAIRO_CONTENT_COLOR_ALPHA, - width - distance_left - distance_right, - height - distance_top - distance_bottom); - cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE]); - cairo_set_source_surface (cr, surface, - distance_left, - distance_top); - cairo_paint (cr); - cairo_destroy (cr); - - cairo_surface_destroy (surface); - - return slice; -} - -static void -render_border (cairo_t *cr, - cairo_surface_t *surface, - guint side, - gdouble x, - gdouble y, - gdouble width, - gdouble height, - GtkSliceSideModifier modifier) -{ - cairo_pattern_t *pattern; - cairo_matrix_t matrix; - - cairo_save (cr); - - cairo_rectangle (cr, x, y, width, height); - cairo_clip (cr); - - pattern = cairo_pattern_create_for_surface (surface); - - if (modifier == GTK_SLICE_REPEAT) - { - cairo_matrix_init_translate (&matrix, - x, - y); - cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); - - cairo_pattern_set_matrix (pattern, &matrix); - cairo_set_source (cr, pattern); - - cairo_rectangle (cr, x, y, width, height); - cairo_fill (cr); - } - else - { - /* Use nearest filter so borders aren't blurred */ - cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST); - - if (side == SIDE_TOP || side == SIDE_BOTTOM) - { - gint w = cairo_image_surface_get_width (surface); - - cairo_translate (cr, x, y); - cairo_scale (cr, width / w, 1.0); - } - else - { - gint h = cairo_image_surface_get_height (surface); - - cairo_translate (cr, x, y); - cairo_scale (cr, 1.0, height / h); - } - - cairo_set_source (cr, pattern); - cairo_rectangle (cr, x, y, width, height); - cairo_paint (cr); - } - - cairo_restore (cr); - - cairo_pattern_destroy (pattern); -} - -static void -render_corner (cairo_t *cr, - cairo_surface_t *surface, - gdouble x, - gdouble y, - gdouble width, - gdouble height) -{ - cairo_save (cr); - - cairo_rectangle (cr, x, y, width, height); - cairo_clip (cr); - - cairo_set_source_surface (cr, surface, x, y); - cairo_rectangle (cr, x, y, width, height); - cairo_fill (cr); - - cairo_restore (cr); -} - -void -_gtk_9slice_render (Gtk9Slice *slice, - cairo_t *cr, - gdouble x, - gdouble y, - gdouble width, - gdouble height) -{ - int img_width, img_height; - cairo_surface_t *surface; - - cairo_save (cr); - - /* Top side */ - surface = slice->surfaces[BORDER_MIDDLE][BORDER_TOP]; - img_height = cairo_image_surface_get_height (surface); - - render_border (cr, surface, SIDE_TOP, - x + slice->distances[SIDE_LEFT], y, - width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT], - (gdouble) img_height, - slice->modifiers[SIDE_TOP]); - - /* Bottom side */ - surface = slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]; - img_height = cairo_image_surface_get_height (surface); - - render_border (cr, surface, SIDE_BOTTOM, - x + slice->distances[SIDE_LEFT], y + height - img_height, - width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT], - (gdouble) img_height, - slice->modifiers[SIDE_BOTTOM]); - - /* Left side */ - surface = slice->surfaces[BORDER_LEFT][BORDER_MIDDLE]; - img_width = cairo_image_surface_get_width (surface); - - render_border (cr, surface, SIDE_LEFT, - x, y + slice->distances[SIDE_TOP], - (gdouble) img_width, - height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM], - slice->modifiers[SIDE_LEFT]); - - /* Right side */ - surface = slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE]; - img_width = cairo_image_surface_get_width (surface); - - render_border (cr, surface, SIDE_RIGHT, - x + width - img_width, y + slice->distances[SIDE_TOP], - (gdouble) img_width, - height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM], - slice->modifiers[SIDE_RIGHT]); - - /* Top/Left corner */ - surface = slice->surfaces[BORDER_LEFT][BORDER_TOP]; - img_width = cairo_image_surface_get_width (surface); - img_height = cairo_image_surface_get_height (surface); - render_corner (cr, surface, x, y, (gdouble) img_width, (gdouble) img_height); - - /* Top/right corner */ - surface = slice->surfaces[BORDER_RIGHT][BORDER_TOP]; - img_width = cairo_image_surface_get_width (surface); - img_height = cairo_image_surface_get_height (surface); - render_corner (cr, surface, x + width - img_width, y, - (gdouble) img_width, (gdouble) img_height); - - /* Bottom/left corner */ - surface = slice->surfaces[BORDER_LEFT][BORDER_BOTTOM]; - img_width = cairo_image_surface_get_width (surface); - img_height = cairo_image_surface_get_height (surface); - render_corner (cr, surface, x, y + height - img_height, - (gdouble) img_width, (gdouble) img_height); - - /* Bottom/right corner */ - surface = slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM]; - img_width = cairo_image_surface_get_width (surface); - img_height = cairo_image_surface_get_height (surface); - render_corner (cr, surface, x + width - img_width, y + height - img_height, - (gdouble) img_width, (gdouble) img_height); - - cairo_restore (cr); -} - -Gtk9Slice * -_gtk_9slice_ref (Gtk9Slice *slice) -{ - g_return_val_if_fail (slice != NULL, NULL); - - slice->ref_count++; - return slice; -} - -void -_gtk_9slice_unref (Gtk9Slice *slice) -{ - g_return_if_fail (slice != NULL); - - slice->ref_count--; - - if (slice->ref_count == 0) - { - gint i, j; - - for (i = 0; i < BORDER_LAST; i++) - for (j = 0; j < BORDER_LAST; j++) - cairo_surface_destroy (slice->surfaces[i][j]); - - g_slice_free (Gtk9Slice, slice); - } -} diff --git a/gtk/gtk9slice.h b/gtk/gtk9slice.h deleted file mode 100644 index 72b5efd435..0000000000 --- a/gtk/gtk9slice.h +++ /dev/null @@ -1,59 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2010 Carlos Garnacho - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library 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 - * 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. - */ - -#ifndef __GTK_9SLICE_H__ -#define __GTK_9SLICE_H__ - -#include "gtktimeline.h" - -G_BEGIN_DECLS - -/* Dummy typedefs */ -typedef struct Gtk9Slice Gtk9Slice; - -#define GTK_TYPE_9SLICE (_gtk_9slice_get_type ()) - -typedef enum { - GTK_SLICE_REPEAT, - GTK_SLICE_STRETCH -} GtkSliceSideModifier; - -GType _gtk_9slice_get_type (void) G_GNUC_CONST; - -Gtk9Slice * _gtk_9slice_new (GdkPixbuf *pixbuf, - gdouble distance_top, - gdouble distance_bottom, - gdouble distance_left, - gdouble distance_right, - GtkSliceSideModifier horizontal_modifier, - GtkSliceSideModifier vertical_modifier); - -Gtk9Slice * _gtk_9slice_ref (Gtk9Slice *slice); -void _gtk_9slice_unref (Gtk9Slice *slice); - -void _gtk_9slice_render (Gtk9Slice *slice, - cairo_t *cr, - gdouble x, - gdouble y, - gdouble width, - gdouble height); - -G_END_DECLS - -#endif /* __GTK_9SLICE_H__ */ diff --git a/gtk/gtkborderimage.c b/gtk/gtkborderimage.c new file mode 100644 index 0000000000..80ec80034e --- /dev/null +++ b/gtk/gtkborderimage.c @@ -0,0 +1,685 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2010 Carlos Garnacho + * Copyright (C) 2011 Red Hat, Inc. + * + * Authors: Carlos Garnacho + * Cosimo Cecchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * 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. + */ + +#include +#include + +#include + +#include "gtkborderimageprivate.h" + +static GtkBorderImageRepeat * +_gtk_border_image_repeat_copy (const GtkBorderImageRepeat *repeat) +{ + GtkBorderImageRepeat *retval; + + retval = g_slice_new0 (GtkBorderImageRepeat); + *retval = *repeat; + + return retval; +} + +static void +_gtk_border_image_repeat_free (GtkBorderImageRepeat *repeat) +{ + g_slice_free (GtkBorderImageRepeat, repeat); +} + +G_DEFINE_BOXED_TYPE (GtkBorderImageRepeat, _gtk_border_image_repeat, + _gtk_border_image_repeat_copy, _gtk_border_image_repeat_free) + +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; + GtkBorderImageRepeat repeat; + + gint ref_count; + gboolean resolved; + + cairo_surface_t *surfaces[BORDER_LAST][BORDER_LAST]; +}; + +static void +_gtk_border_image_ensure_slices (GtkBorderImage *image, + gdouble width, + gdouble height) +{ + cairo_surface_t *surface; + cairo_t *cr; + + if (image->surfaces[0][0] != NULL) + return; + + if (cairo_pattern_get_type (image->source) != CAIRO_PATTERN_TYPE_SURFACE) + { + cairo_matrix_t matrix; + + 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); + cr = cairo_create (surface); + cairo_set_source (cr, image->source); + cairo_paint (cr); + + cairo_destroy (cr); + } + else + { + cairo_pattern_get_surface (image->source, &surface); + cairo_surface_reference (surface); + + width = cairo_image_surface_get_width (surface); + height = cairo_image_surface_get_height (surface); + } + + /* Top /left corner */ + image->surfaces[BORDER_LEFT][BORDER_TOP] = + cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, + image->slice.left, image->slice.top); + cr = cairo_create (image->surfaces[BORDER_LEFT][BORDER_TOP]); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + + cairo_destroy (cr); + + /* Top/right corner */ + image->surfaces[BORDER_RIGHT][BORDER_TOP] = + cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, + image->slice.right, image->slice.top); + cr = cairo_create (image->surfaces[BORDER_RIGHT][BORDER_TOP]); + cairo_set_source_surface (cr, surface, + - width + image->slice.right, + 0); + cairo_paint (cr); + cairo_destroy (cr); + + /* Bottom/left corner */ + image->surfaces[BORDER_LEFT][BORDER_BOTTOM] = + cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, + image->slice.left, image->slice.bottom); + cr = cairo_create (image->surfaces[BORDER_LEFT][BORDER_BOTTOM]); + cairo_set_source_surface (cr, surface, + 0, + - height + image->slice.bottom); + cairo_paint (cr); + cairo_destroy (cr); + + /* Bottom/right corner */ + image->surfaces[BORDER_RIGHT][BORDER_BOTTOM] = + cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA, + image->slice.right, image->slice.bottom); + cr = cairo_create (image->surfaces[BORDER_RIGHT][BORDER_BOTTOM]); + cairo_set_source_surface (cr, surface, + - width + image->slice.right, + - height + image->slice.bottom); + cairo_paint (cr); + cairo_destroy (cr); + + if ((image->slice.left + image->slice.right) < width) + { + /* Top side */ + image->surfaces[BORDER_MIDDLE][BORDER_TOP] = + cairo_surface_create_similar (surface, + CAIRO_CONTENT_COLOR_ALPHA, + width - image->slice.left - image->slice.right, + image->slice.top); + cr = cairo_create (image->surfaces[BORDER_MIDDLE][BORDER_TOP]); + cairo_set_source_surface (cr, surface, + - image->slice.left, + 0); + cairo_paint (cr); + cairo_destroy (cr); + + /* Bottom side */ + image->surfaces[BORDER_MIDDLE][BORDER_BOTTOM] = + cairo_surface_create_similar (surface, + CAIRO_CONTENT_COLOR_ALPHA, + width - image->slice.left - image->slice.right, + image->slice.bottom); + cr = cairo_create (image->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]); + cairo_set_source_surface (cr, surface, + - image->slice.left, + - height + image->slice.bottom); + cairo_paint (cr); + cairo_destroy (cr); + } + + if ((image->slice.top + image->slice.bottom) < height) + { + /* Left side */ + image->surfaces[BORDER_LEFT][BORDER_MIDDLE] = + cairo_surface_create_similar (surface, + CAIRO_CONTENT_COLOR_ALPHA, + image->slice.left, + height - image->slice.top - image->slice.bottom); + cr = cairo_create (image->surfaces[BORDER_LEFT][BORDER_MIDDLE]); + cairo_set_source_surface (cr, surface, + 0, + - image->slice.top); + cairo_paint (cr); + cairo_destroy (cr); + + /* Right side */ + image->surfaces[BORDER_RIGHT][BORDER_MIDDLE] = + cairo_surface_create_similar (surface, + CAIRO_CONTENT_COLOR_ALPHA, + image->slice.right, + height - image->slice.top - image->slice.bottom); + cr = cairo_create (image->surfaces[BORDER_RIGHT][BORDER_MIDDLE]); + cairo_set_source_surface (cr, surface, + - width + image->slice.right, + - image->slice.top); + cairo_paint (cr); + cairo_destroy (cr); + } + + cairo_surface_destroy (surface); +} + +GtkBorderImage * +_gtk_border_image_new (cairo_pattern_t *pattern, + GtkBorder *slice, + GtkBorderImageRepeat *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; + + image->resolved = TRUE; + + return image; +} + +GtkBorderImage * +_gtk_border_image_new_for_gradient (GtkGradient *gradient, + GtkBorder *slice, + GtkBorderImageRepeat *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; + + image->resolved = FALSE; + + return image; +} + +gboolean +_gtk_border_image_get_resolved (GtkBorderImage *image) +{ + return image->resolved; +} + +GtkBorderImage * +_gtk_border_image_resolve (GtkBorderImage *image, + GtkStyleProperties *props) +{ + GtkBorderImage *resolved_image; + cairo_pattern_t *pattern; + + if (image->resolved) + return _gtk_border_image_ref (image); + + if (!gtk_gradient_resolve (image->source_gradient, props, &pattern)) + return NULL; + + resolved_image = _gtk_border_image_new (pattern, &image->slice, &image->repeat); + + return resolved_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) +{ + GParameter *parameter = g_new0 (GParameter, 3); + GtkBorderImage *image = g_value_get_boxed (value); + + parameter[0].name = "border-image-source"; + g_value_init (¶meter[0].value, CAIRO_GOBJECT_TYPE_PATTERN); + g_value_set_boxed (¶meter[0].value, image->source); + + parameter[1].name = "border-image-slice"; + g_value_init (¶meter[1].value, GTK_TYPE_BORDER); + g_value_set_boxed (¶meter[1].value, &image->slice); + + parameter[2].name = "border-image-repeat"; + g_value_init (¶meter[2].value, GTK_TYPE_BORDER_IMAGE_REPEAT); + g_value_set_boxed (¶meter[2].value, &image->repeat); + + *n_params = 3; + return parameter; +} + +void +_gtk_border_image_pack (GValue *value, + GtkStyleProperties *props, + GtkStateFlags state) +{ + GtkBorderImage *image; + cairo_pattern_t *source; + GtkBorder *slice; + GtkBorderImageRepeat *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) + _gtk_border_image_repeat_free (repeat); +} + +static void +render_corner (cairo_t *cr, + cairo_surface_t *surface, + gdouble x, + gdouble y, + gdouble width, + gdouble height) +{ + gint image_width, image_height; + + if (width == 0 || height == 0) + return; + + cairo_save (cr); + + image_width = cairo_image_surface_get_width (surface); + image_height = cairo_image_surface_get_height (surface); + + cairo_translate (cr, x, y); + cairo_scale (cr, + width / image_width, + 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); +} + +static cairo_surface_t * +create_spaced_surface (cairo_surface_t *tile, + gdouble width, + gdouble height, + GtkOrientation orientation) +{ + gint n_repeats, idx; + gint tile_width, tile_height; + gdouble avail_space, step; + cairo_surface_t *retval; + cairo_t *cr; + + tile_width = cairo_image_surface_get_width (tile); + tile_height = cairo_image_surface_get_height (tile); + + 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) + { + 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++; + } + + cairo_destroy (cr); + + return retval; +} + +static void +render_border (cairo_t *cr, + cairo_surface_t *surface, + gdouble total_width, + gdouble total_height, + guint side, + GtkBorder *border_area, + GtkBorderImageRepeat *repeat) +{ + gdouble target_x, target_y; + gdouble target_width, target_height; + gint original_width, original_height; + GdkRectangle image_area; + cairo_pattern_t *pattern; + gboolean repeat_pattern; + + if (surface == NULL) + return; + + cairo_surface_reference (surface); + repeat_pattern = FALSE; + + original_width = cairo_image_surface_get_width (surface); + original_height = cairo_image_surface_get_height (surface); + + if (side == SIDE_TOP || side == SIDE_BOTTOM) + { + target_height = (side == SIDE_TOP) ? (border_area->top) : (border_area->bottom); + target_width = original_width * (target_height / original_height); + } + else + { + target_width = (side == SIDE_LEFT) ? (border_area->left) : (border_area->right); + target_height = original_height * (target_width / original_width); + } + + if (side == SIDE_TOP || side == SIDE_BOTTOM) + { + 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_REPEAT_STYLE_NONE) + { + target_width = image_area.width; + } + else if (repeat->vrepeat == GTK_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_REPEAT_STYLE_ROUND) + { + gint n_repeats; + + repeat_pattern = TRUE; + + n_repeats = (gint) floor (image_area.width / original_width); + target_width = image_area.width / n_repeats; + } + else if (repeat->vrepeat == GTK_REPEAT_STYLE_SPACE) + { + cairo_surface_t *spaced_surface; + + spaced_surface = create_spaced_surface (surface, + image_area.width, original_height, + GTK_ORIENTATION_HORIZONTAL); + cairo_surface_destroy (surface); + surface = spaced_surface; + + /* short-circuit hscaling */ + target_width = original_width = cairo_image_surface_get_width (surface); + } + } + 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; + + if (repeat->hrepeat == GTK_REPEAT_STYLE_NONE) + { + target_height = total_height - border_area->top - border_area->bottom; + } + else if (repeat->hrepeat == GTK_REPEAT_STYLE_REPEAT) + { + repeat_pattern = TRUE; + + 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_REPEAT_STYLE_ROUND) + { + gint n_repeats; + + repeat_pattern = TRUE; + + n_repeats = (gint) floor (image_area.height / original_height); + target_height = image_area.height / n_repeats; + } + else if (repeat->hrepeat == GTK_REPEAT_STYLE_SPACE) + { + cairo_surface_t *spaced_surface; + + spaced_surface = create_spaced_surface (surface, + original_width, image_area.height, + GTK_ORIENTATION_VERTICAL); + cairo_surface_destroy (surface); + surface = spaced_surface; + + /* short-circuit vscaling */ + target_height = original_height = cairo_image_surface_get_height (surface); + } + } + + 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_scale (cr, + target_width / original_width, + target_height / original_height); + + cairo_set_source (cr, pattern); + cairo_paint (cr); + + cairo_restore (cr); + + cairo_pattern_destroy (pattern); +} + +void +_gtk_border_image_render (GtkBorderImage *image, + GtkBorder *border_width, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble width, + gdouble height) +{ + cairo_surface_t *surface; + + _gtk_border_image_ensure_slices (image, width, height); + + cairo_save (cr); + cairo_translate (cr, x, y); + + /* Top side */ + surface = image->surfaces[BORDER_MIDDLE][BORDER_TOP]; + render_border (cr, surface, + width, height, SIDE_TOP, + border_width, &image->repeat); + + /* Bottom side */ + surface = image->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]; + render_border (cr, surface, + width, height, SIDE_BOTTOM, + border_width, &image->repeat); + + /* Left side */ + surface = image->surfaces[BORDER_LEFT][BORDER_MIDDLE]; + render_border (cr, surface, + width, height, SIDE_LEFT, + border_width, &image->repeat); + + /* Right side */ + surface = image->surfaces[BORDER_RIGHT][BORDER_MIDDLE]; + render_border (cr, surface, + width, height, SIDE_RIGHT, + border_width, &image->repeat); + + /* Top/Left corner */ + surface = image->surfaces[BORDER_LEFT][BORDER_TOP]; + render_corner (cr, surface, + 0, 0, + border_width->left, border_width->top); + + /* Top/right corner */ + surface = image->surfaces[BORDER_RIGHT][BORDER_TOP]; + render_corner (cr, surface, + width - border_width->right, 0, + border_width->right, border_width->top); + + /* Bottom/left corner */ + surface = image->surfaces[BORDER_LEFT][BORDER_BOTTOM]; + render_corner (cr, surface, + 0, height - border_width->bottom, + border_width->left, border_width->bottom); + + /* Bottom/right corner */ + surface = image->surfaces[BORDER_RIGHT][BORDER_BOTTOM]; + render_corner (cr, surface, + width - border_width->right, height - border_width->bottom, + border_width->right, border_width->bottom); + + cairo_restore (cr); +} diff --git a/gtk/gtkborderimageprivate.h b/gtk/gtkborderimageprivate.h new file mode 100644 index 0000000000..2eddd9363a --- /dev/null +++ b/gtk/gtkborderimageprivate.h @@ -0,0 +1,80 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 2010 Carlos Garnacho + * Copyright (C) 2011 Red Hat, Inc. + * + * Authors: Carlos Garnacho + * Cosimo Cecchi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * 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. + */ + +#ifndef __GTK_BORDER_IMAGE_H__ +#define __GTK_BORDER_IMAGE_H__ + +#include +#include +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_BORDER_IMAGE_REPEAT (_gtk_border_image_repeat_get_type ()) + +GType _gtk_border_image_repeat_get_type (void) G_GNUC_CONST; + +typedef struct { + GtkRepeatStyle vrepeat; + GtkRepeatStyle hrepeat; +} GtkBorderImageRepeat; + +typedef struct _GtkBorderImage GtkBorderImage; + +#define GTK_TYPE_BORDER_IMAGE (_gtk_border_image_get_type ()) + +GType _gtk_border_image_get_type (void) G_GNUC_CONST; + +GtkBorderImage * _gtk_border_image_new (cairo_pattern_t *source, + GtkBorder *slice, + GtkBorderImageRepeat *repeat); +GtkBorderImage * _gtk_border_image_new_for_gradient (GtkGradient *gradient, + GtkBorder *slice, + GtkBorderImageRepeat *repeat); + +GtkBorderImage * _gtk_border_image_resolve (GtkBorderImage *image, + GtkStyleProperties *props); +gboolean _gtk_border_image_get_resolved (GtkBorderImage *image); + +GtkBorderImage * _gtk_border_image_ref (GtkBorderImage *image); +void _gtk_border_image_unref (GtkBorderImage *image); + +void _gtk_border_image_render (GtkBorderImage *image, + GtkBorder *border_width, + cairo_t *cr, + gdouble x, + gdouble y, + gdouble width, + gdouble height); + +GParameter * _gtk_border_image_unpack (const GValue *value, + guint *n_params); +void _gtk_border_image_pack (GValue *value, + GtkStyleProperties *props, + GtkStateFlags state); + +G_END_DECLS + +#endif /* __GTK_BORDER_IMAGE_H__ */ diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 33e3aea02b..2a4c70023a 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -916,6 +916,13 @@ typedef enum { GTK_BORDER_STYLE_OUTSET } GtkBorderStyle; +typedef enum { + GTK_REPEAT_STYLE_NONE, + GTK_REPEAT_STYLE_REPEAT, + GTK_REPEAT_STYLE_ROUND, + GTK_REPEAT_STYLE_SPACE +} GtkRepeatStyle; + G_END_DECLS diff --git a/gtk/gtkthemingengine.c b/gtk/gtkthemingengine.c index e2580e7949..6f0f392dba 100644 --- a/gtk/gtkthemingengine.c +++ b/gtk/gtkthemingengine.c @@ -27,7 +27,7 @@ #include #include "gtkprivate.h" -#include "gtk9slice.h" +#include "gtkborderimageprivate.h" #include "gtkpango.h" #include "gtkshadowprivate.h" #include "gtkcsstypesprivate.h" @@ -2148,22 +2148,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,