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"
31 /* this is in case round() is not provided by the compiler,
32 * such as in the case of C89 compilers, like MSVC
34 #include "fallback-c89.c"
36 G_DEFINE_BOXED_TYPE (GtkBorderImage, _gtk_border_image,
37 _gtk_border_image_ref, _gtk_border_image_unref)
44 BORDER_TOP = BORDER_LEFT,
45 BORDER_BOTTOM = BORDER_RIGHT
55 struct _GtkBorderImage {
56 cairo_pattern_t *source;
57 GtkGradient *source_gradient;
61 GtkCssBorderImageRepeat repeat;
67 _gtk_border_image_new (cairo_pattern_t *pattern,
70 GtkCssBorderImageRepeat *repeat)
72 GtkBorderImage *image;
74 image = g_slice_new0 (GtkBorderImage);
78 image->source = cairo_pattern_reference (pattern);
81 image->slice = *slice;
84 image->width = gtk_border_copy (width);
87 image->repeat = *repeat;
93 _gtk_border_image_new_for_gradient (GtkGradient *gradient,
96 GtkCssBorderImageRepeat *repeat)
98 GtkBorderImage *image;
100 image = g_slice_new0 (GtkBorderImage);
101 image->ref_count = 1;
103 if (gradient != NULL)
104 image->source_gradient = gtk_gradient_ref (gradient);
107 image->slice = *slice;
110 image->width = gtk_border_copy (width);
113 image->repeat = *repeat;
119 _gtk_border_image_ref (GtkBorderImage *image)
121 g_return_val_if_fail (image != NULL, NULL);
129 _gtk_border_image_unref (GtkBorderImage *image)
131 g_return_if_fail (image != NULL);
135 if (image->ref_count == 0)
137 if (image->source != NULL)
138 cairo_pattern_destroy (image->source);
140 if (image->source_gradient != NULL)
141 gtk_gradient_unref (image->source_gradient);
143 if (image->width != NULL)
144 gtk_border_free (image->width);
146 g_slice_free (GtkBorderImage, image);
151 _gtk_border_image_unpack (const GValue *value,
154 GParameter *parameter = g_new0 (GParameter, 4);
155 GtkBorderImage *image = g_value_get_boxed (value);
157 parameter[0].name = "border-image-source";
159 if ((image != NULL) &&
160 (image->source_gradient != NULL))
161 g_value_init (¶meter[0].value, GTK_TYPE_GRADIENT);
163 g_value_init (¶meter[0].value, CAIRO_GOBJECT_TYPE_PATTERN);
165 parameter[1].name = "border-image-slice";
166 g_value_init (¶meter[1].value, GTK_TYPE_BORDER);
168 parameter[2].name = "border-image-repeat";
169 g_value_init (¶meter[2].value, GTK_TYPE_CSS_BORDER_IMAGE_REPEAT);
171 parameter[3].name = "border-image-width";
172 g_value_init (¶meter[3].value, GTK_TYPE_BORDER);
176 if (image->source_gradient != NULL)
177 g_value_set_boxed (¶meter[0].value, image->source_gradient);
179 g_value_set_boxed (¶meter[0].value, image->source);
181 g_value_set_boxed (¶meter[1].value, &image->slice);
182 g_value_set_boxed (¶meter[2].value, &image->repeat);
183 g_value_set_boxed (¶meter[3].value, image->width);
191 _gtk_border_image_pack (GValue *value,
192 GtkStyleProperties *props,
195 GtkBorderImage *image;
196 cairo_pattern_t *source;
197 GtkBorder *slice, *width;
198 GtkCssBorderImageRepeat *repeat;
200 gtk_style_properties_get (props, state,
201 "border-image-source", &source,
202 "border-image-slice", &slice,
203 "border-image-repeat", &repeat,
204 "border-image-width", &width,
209 g_value_take_boxed (value, NULL);
213 image = _gtk_border_image_new (source, slice, width, repeat);
214 g_value_take_boxed (value, image);
216 cairo_pattern_destroy (source);
220 gtk_border_free (slice);
223 gtk_border_free (width);
229 typedef struct _GtkBorderImageSliceSize GtkBorderImageSliceSize;
230 struct _GtkBorderImageSliceSize {
236 gtk_border_image_compute_border_size (GtkBorderImageSliceSize sizes[3],
242 /* This code assumes area_size >= start_border + end_border */
244 sizes[0].offset = offset;
245 sizes[0].size = start_border;
246 sizes[1].offset = offset + start_border;
247 sizes[1].size = area_size - start_border - end_border;
248 sizes[2].offset = offset + area_size - end_border;
249 sizes[2].size = end_border;
253 gtk_border_image_render_slice (cairo_t *cr,
254 cairo_surface_t *slice,
261 GtkCssRepeatStyle hrepeat,
262 GtkCssRepeatStyle vrepeat)
264 double hscale, vscale;
266 cairo_extend_t extend = CAIRO_EXTEND_PAD;
267 cairo_matrix_t matrix;
268 cairo_pattern_t *pattern;
270 /* We can't draw center tiles yet */
271 g_assert (hrepeat == GTK_CSS_REPEAT_STYLE_NONE || vrepeat == GTK_CSS_REPEAT_STYLE_NONE);
273 hscale = width / slice_width;
274 vscale = height / slice_height;
280 case GTK_CSS_REPEAT_STYLE_REPEAT:
281 extend = CAIRO_EXTEND_REPEAT;
284 case GTK_CSS_REPEAT_STYLE_SPACE:
288 extend = CAIRO_EXTEND_NONE;
291 xstep = hscale * slice_width;
292 n = floor (width / xstep);
293 space = (width - n * xstep) / (n + 1);
299 case GTK_CSS_REPEAT_STYLE_NONE:
301 case GTK_CSS_REPEAT_STYLE_ROUND:
302 extend = CAIRO_EXTEND_REPEAT;
303 hscale = width / (slice_width * MAX (round (width / (slice_width * vscale)), 1));
306 g_assert_not_reached ();
312 case GTK_CSS_REPEAT_STYLE_REPEAT:
313 extend = CAIRO_EXTEND_REPEAT;
316 case GTK_CSS_REPEAT_STYLE_SPACE:
320 extend = CAIRO_EXTEND_NONE;
323 ystep = vscale * slice_height;
324 n = floor (height / ystep);
325 space = (height - n * ystep) / (n + 1);
331 case GTK_CSS_REPEAT_STYLE_NONE:
333 case GTK_CSS_REPEAT_STYLE_ROUND:
334 extend = CAIRO_EXTEND_REPEAT;
335 vscale = height / (slice_height * MAX (round (height / (slice_height * hscale)), 1));
338 g_assert_not_reached ();
342 pattern = cairo_pattern_create_for_surface (slice);
344 cairo_matrix_init_translate (&matrix,
345 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_width / 2 : 0,
346 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_height / 2 : 0);
347 cairo_matrix_scale (&matrix, 1 / hscale, 1 / vscale);
348 cairo_matrix_translate (&matrix,
349 hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - width / 2 : 0,
350 vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - height / 2 : 0);
352 cairo_pattern_set_matrix (pattern, &matrix);
353 cairo_pattern_set_extend (pattern, extend);
356 cairo_translate (cr, x, y);
358 for (y = 0; y < height; y += ystep)
360 for (x = 0; x < width; x += xstep)
363 cairo_translate (cr, x, y);
364 cairo_set_source (cr, pattern);
365 cairo_rectangle (cr, 0, 0, xstep, ystep);
373 cairo_pattern_destroy (pattern);
377 gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
382 sizes[0].size = MIN (start_size, surface_size);
385 sizes[2].size = MIN (end_size, surface_size);
386 sizes[2].offset = surface_size - sizes[2].size;
388 sizes[1].size = MAX (0, surface_size - sizes[0].size - sizes[2].size);
389 sizes[1].offset = sizes[0].size;
393 _gtk_border_image_render (GtkBorderImage *image,
394 GtkBorder *border_width,
401 cairo_surface_t *surface, *slice;
402 GtkBorderImageSliceSize vertical_slice[3], horizontal_slice[3];
403 GtkBorderImageSliceSize vertical_border[3], horizontal_border[3];
404 int surface_width, surface_height;
407 if (image->width != NULL)
408 border_width = image->width;
410 if (cairo_pattern_get_type (image->source) != CAIRO_PATTERN_TYPE_SURFACE)
412 cairo_matrix_t matrix;
415 surface_width = width;
416 surface_height = height;
418 cairo_matrix_init_scale (&matrix, 1 / width, 1 / height);
419 cairo_pattern_set_matrix (image->source, &matrix);
421 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
422 surface_cr = cairo_create (surface);
423 cairo_set_source (surface_cr, image->source);
424 cairo_paint (surface_cr);
426 cairo_destroy (surface_cr);
430 cairo_pattern_get_surface (image->source, &surface);
431 cairo_surface_reference (surface);
433 surface_width = cairo_image_surface_get_width (surface);
434 surface_height = cairo_image_surface_get_height (surface);
437 gtk_border_image_compute_slice_size (horizontal_slice,
441 gtk_border_image_compute_slice_size (vertical_slice,
444 image->slice.bottom);
445 gtk_border_image_compute_border_size (horizontal_border,
449 border_width->right);
450 gtk_border_image_compute_border_size (vertical_border,
454 border_width->bottom);
456 for (v = 0; v < 3; v++)
458 if (vertical_slice[v].size == 0 ||
459 vertical_border[v].size == 0)
462 for (h = 0; h < 3; h++)
464 if (horizontal_slice[h].size == 0 ||
465 horizontal_border[h].size == 0)
468 if (h == 1 && v == 1)
471 slice = cairo_surface_create_for_rectangle (surface,
472 horizontal_slice[h].offset,
473 vertical_slice[v].offset,
474 horizontal_slice[h].size,
475 vertical_slice[v].size);
477 gtk_border_image_render_slice (cr,
479 horizontal_slice[h].size,
480 vertical_slice[v].size,
481 horizontal_border[h].offset,
482 vertical_border[v].offset,
483 horizontal_border[h].size,
484 vertical_border[v].size,
485 h == 1 ? image->repeat.hrepeat : GTK_CSS_REPEAT_STYLE_NONE,
486 v == 1 ? image->repeat.vrepeat : GTK_CSS_REPEAT_STYLE_NONE);
488 cairo_surface_destroy (slice);
492 cairo_surface_destroy (surface);