diff --git a/gtk/Makefile.am b/gtk/Makefile.am index ff5ba8d876..a4780a9f1a 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -372,6 +372,7 @@ endif # GTK+ header files that don't get installed gtk_private_h_sources = \ + gtk9slice.h \ gtkbuttonprivate.h \ gtkquery.h \ gtksearchengine.h \ @@ -431,6 +432,7 @@ gtk_private_h_sources = \ # GTK+ C sources to build the library from gtk_base_c_sources = \ + gtk9slice.c \ gtkquery.c \ gtksearchengine.c \ gtksearchenginesimple.c \ diff --git a/gtk/gtk9slice.c b/gtk/gtk9slice.c new file mode 100644 index 0000000000..9bfdafb914 --- /dev/null +++ b/gtk/gtk9slice.c @@ -0,0 +1,351 @@ +/* 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 vertical_modifier, + GtkSliceSideModifier horizontal_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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE]); + cairo_set_source_surface (cr, surface, - distance_left, - distance_top); + cairo_paint (cr); + cairo_destroy (cr); + + return slice; +} + +static void +render_border (cairo_t *cr, + cairo_surface_t *surface, + 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); + + cairo_matrix_init_translate (&matrix, - x, - y); + + if (modifier == GTK_SLICE_REPEAT) + cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); + else + { + gint w; + + w = cairo_image_surface_get_width (surface); + cairo_matrix_scale (&matrix, width / w, 0); + } + + cairo_pattern_set_matrix (pattern, &matrix); + cairo_set_source (cr, pattern); + + cairo_rectangle (cr, x, y, width, height); + cairo_fill (cr); + + cairo_restore (cr); +} + +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, + x + slice->distances[SIDE_LEFT], y, + (gdouble) 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, + x + slice->distances[SIDE_LEFT], y + height - img_height, + (gdouble) 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, + x, y + slice->distances[SIDE_TOP], + (gdouble) img_width, + (gdouble) 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, + 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 new file mode 100644 index 0000000000..28d998b4e3 --- /dev/null +++ b/gtk/gtk9slice.h @@ -0,0 +1,59 @@ +/* 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 vertical_modifier, + GtkSliceSideModifier horizontal_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__ */