1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3 * Copyright (C) 2011 Red Hat, Inc.
5 * Authors: Carlos Garnacho <carlosg@gnome.org>
6 * Cosimo Cecchi <cosimoc@gnome.org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 #include <cairo-gobject.h>
29 #include "gtkborderimageprivate.h"
30 #include "gtkstylepropertiesprivate.h"
31 #include "gtkthemingengineprivate.h"
33 /* this is in case round() is not provided by the compiler,
34 * such as in the case of C89 compilers, like MSVC
36 #include "fallback-c89.c"
38 G_DEFINE_BOXED_TYPE (GtkBorderImage, _gtk_border_image,
39 _gtk_border_image_ref, _gtk_border_image_unref)
41 struct _GtkBorderImage {
46 GtkCssBorderImageRepeat repeat;
52 _gtk_border_image_new (GtkCssImage *source,
55 GtkCssBorderImageRepeat *repeat)
57 GtkBorderImage *image;
59 image = g_slice_new0 (GtkBorderImage);
62 image->source = g_object_ref (source);
65 image->slice = *slice;
68 image->width = gtk_border_copy (width);
71 image->repeat = *repeat;
77 _gtk_border_image_new_for_engine (GtkThemingEngine *engine)
81 source = g_value_get_object (_gtk_theming_engine_peek_property (engine, "border-image-source"));
85 return _gtk_border_image_new (source,
86 g_value_get_boxed (_gtk_theming_engine_peek_property (engine, "border-image-slice")),
87 g_value_get_boxed (_gtk_theming_engine_peek_property (engine, "border-image-width")),
88 g_value_get_boxed (_gtk_theming_engine_peek_property (engine, "border-image-repeat")));
92 _gtk_border_image_ref (GtkBorderImage *image)
94 g_return_val_if_fail (image != NULL, NULL);
102 _gtk_border_image_unref (GtkBorderImage *image)
104 g_return_if_fail (image != NULL);
108 if (image->ref_count == 0)
110 g_object_unref (image->source);
112 if (image->width != NULL)
113 gtk_border_free (image->width);
115 g_slice_free (GtkBorderImage, image);
120 _gtk_border_image_unpack (const GValue *value,
123 GParameter *parameter = g_new0 (GParameter, 4);
124 GtkBorderImage *image = g_value_get_boxed (value);
126 parameter[0].name = "border-image-source";
127 g_value_init (¶meter[0].value, GTK_TYPE_CSS_IMAGE);
129 parameter[1].name = "border-image-slice";
130 g_value_init (¶meter[1].value, GTK_TYPE_BORDER);
132 parameter[2].name = "border-image-repeat";
133 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
135 parameter[3].name = "border-image-width";
136 g_value_init (¶meter[3].value, GTK_TYPE_BORDER);
140 g_value_set_object (¶meter[0].value, image->source);
141 g_value_set_boxed (¶meter[1].value, &image->slice);
142 g_value_set_boxed (¶meter[2].value, &image->repeat);
143 g_value_set_boxed (¶meter[3].value, image->width);
151 _gtk_border_image_pack (GValue *value,
152 GtkStyleProperties *props,
155 GtkBorderImage *image;
156 GtkBorder *slice, *width;
157 GtkCssBorderImageRepeat *repeat;
161 val = _gtk_style_properties_peek_property (props,
162 GTK_CSS_STYLE_PROPERTY (_gtk_style_property_lookup ("border-image-source")),
164 source = g_value_get_object (val);
168 gtk_style_properties_get (props, state,
169 "border-image-slice", &slice,
170 "border-image-repeat", &repeat,
171 "border-image-width", &width,
174 image = _gtk_border_image_new (source, slice, width, repeat);
175 g_value_take_boxed (value, image);
178 gtk_border_free (slice);
181 gtk_border_free (width);
187 typedef struct _GtkBorderImageSliceSize GtkBorderImageSliceSize;
188 struct _GtkBorderImageSliceSize {
194 gtk_border_image_compute_border_size (GtkBorderImageSliceSize sizes[3],
200 /* This code assumes area_size >= start_border + end_border */
202 sizes[0].offset = offset;
203 sizes[0].size = start_border;
204 sizes[1].offset = offset + start_border;
205 sizes[1].size = area_size - start_border - end_border;
206 sizes[2].offset = offset + area_size - end_border;
207 sizes[2].size = end_border;
211 gtk_border_image_render_slice (cairo_t *cr,
212 cairo_surface_t *slice,
219 GtkCssBorderRepeatStyle hrepeat,
220 GtkCssBorderRepeatStyle vrepeat)
222 double hscale, vscale;
224 cairo_extend_t extend = CAIRO_EXTEND_PAD;
225 cairo_matrix_t matrix;
226 cairo_pattern_t *pattern;
228 /* We can't draw center tiles yet */
229 g_assert (hrepeat == GTK_CSS_REPEAT_STYLE_STRETCH || vrepeat == GTK_CSS_REPEAT_STYLE_STRETCH);
231 hscale = width / slice_width;
232 vscale = height / slice_height;
238 case GTK_CSS_REPEAT_STYLE_REPEAT:
239 extend = CAIRO_EXTEND_REPEAT;
242 case GTK_CSS_REPEAT_STYLE_SPACE:
246 extend = CAIRO_EXTEND_NONE;
249 xstep = hscale * slice_width;
250 n = floor (width / xstep);
251 space = (width - n * xstep) / (n + 1);
257 case GTK_CSS_REPEAT_STYLE_STRETCH:
259 case GTK_CSS_REPEAT_STYLE_ROUND:
260 extend = CAIRO_EXTEND_REPEAT;
261 hscale = width / (slice_width * MAX (round (width / (slice_width * vscale)), 1));
264 g_assert_not_reached ();
270 case GTK_CSS_REPEAT_STYLE_REPEAT:
271 extend = CAIRO_EXTEND_REPEAT;
274 case GTK_CSS_REPEAT_STYLE_SPACE:
278 extend = CAIRO_EXTEND_NONE;
281 ystep = vscale * slice_height;
282 n = floor (height / ystep);
283 space = (height - n * ystep) / (n + 1);
289 case GTK_CSS_REPEAT_STYLE_STRETCH:
291 case GTK_CSS_REPEAT_STYLE_ROUND:
292 extend = CAIRO_EXTEND_REPEAT;
293 vscale = height / (slice_height * MAX (round (height / (slice_height * hscale)), 1));
296 g_assert_not_reached ();
300 pattern = cairo_pattern_create_for_surface (slice);
302 cairo_matrix_init_translate (&matrix,
303 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_width / 2 : 0,
304 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_height / 2 : 0);
305 cairo_matrix_scale (&matrix, 1 / hscale, 1 / vscale);
306 cairo_matrix_translate (&matrix,
307 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - width / 2 : 0,
308 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - height / 2 : 0);
310 cairo_pattern_set_matrix (pattern, &matrix);
311 cairo_pattern_set_extend (pattern, extend);
314 cairo_translate (cr, x, y);
316 for (y = 0; y < height; y += ystep)
318 for (x = 0; x < width; x += xstep)
321 cairo_translate (cr, x, y);
322 cairo_set_source (cr, pattern);
323 cairo_rectangle (cr, 0, 0, xstep, ystep);
331 cairo_pattern_destroy (pattern);
335 gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
340 sizes[0].size = MIN (start_size, surface_size);
343 sizes[2].size = MIN (end_size, surface_size);
344 sizes[2].offset = surface_size - sizes[2].size;
346 sizes[1].size = MAX (0, surface_size - sizes[0].size - sizes[2].size);
347 sizes[1].offset = sizes[0].size;
351 _gtk_border_image_render (GtkBorderImage *image,
352 GtkBorder *border_width,
359 cairo_surface_t *surface, *slice;
360 GtkBorderImageSliceSize vertical_slice[3], horizontal_slice[3];
361 GtkBorderImageSliceSize vertical_border[3], horizontal_border[3];
362 double source_width, source_height;
365 if (image->width != NULL)
366 border_width = image->width;
368 _gtk_css_image_get_concrete_size (image->source,
371 &source_width, &source_height);
373 /* XXX: Optimize for (source_width == width && source_height == height) */
375 surface = _gtk_css_image_get_surface (image->source,
376 cairo_get_target (cr),
377 source_width, source_height);
379 gtk_border_image_compute_slice_size (horizontal_slice,
383 gtk_border_image_compute_slice_size (vertical_slice,
386 image->slice.bottom);
387 gtk_border_image_compute_border_size (horizontal_border,
391 border_width->right);
392 gtk_border_image_compute_border_size (vertical_border,
396 border_width->bottom);
398 for (v = 0; v < 3; v++)
400 if (vertical_slice[v].size == 0 ||
401 vertical_border[v].size == 0)
404 for (h = 0; h < 3; h++)
406 if (horizontal_slice[h].size == 0 ||
407 horizontal_border[h].size == 0)
410 if (h == 1 && v == 1)
413 slice = cairo_surface_create_for_rectangle (surface,
414 horizontal_slice[h].offset,
415 vertical_slice[v].offset,
416 horizontal_slice[h].size,
417 vertical_slice[v].size);
419 gtk_border_image_render_slice (cr,
421 horizontal_slice[h].size,
422 vertical_slice[v].size,
423 horizontal_border[h].offset,
424 vertical_border[v].offset,
425 horizontal_border[h].size,
426 vertical_border[v].size,
427 h == 1 ? image->repeat.hrepeat : GTK_CSS_REPEAT_STYLE_STRETCH,
428 v == 1 ? image->repeat.vrepeat : GTK_CSS_REPEAT_STYLE_STRETCH);
430 cairo_surface_destroy (slice);
434 cairo_surface_destroy (surface);