]> Pileus Git - ~andy/gtk/blob - gtk/gtkborderimage.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkborderimage.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  * Copyright (C) 2011 Red Hat, Inc.
4  *
5  * Authors: Carlos Garnacho <carlosg@gnome.org>
6  *          Cosimo Cecchi <cosimoc@gnome.org>
7  *
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.
12  *
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.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include <config.h>
23 #include <cairo-gobject.h>
24
25 #include <math.h>
26
27 #include "gtkborderimageprivate.h"
28 #include "gtkcssbordervalueprivate.h"
29 #include "gtkcssimagevalueprivate.h"
30 #include "gtkcssnumbervalueprivate.h"
31 #include "gtkcssrepeatvalueprivate.h"
32 #include "gtkstylepropertiesprivate.h"
33 #include "gtkthemingengineprivate.h"
34
35 /* this is in case round() is not provided by the compiler, 
36  * such as in the case of C89 compilers, like MSVC
37  */
38 #include "fallback-c89.c"
39
40 gboolean
41 _gtk_border_image_init (GtkBorderImage   *image,
42                         GtkThemingEngine *engine)
43 {
44   image->source = _gtk_css_image_value_get_image (_gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_SOURCE));
45   if (image->source == NULL)
46     return FALSE;
47
48   image->slice = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_SLICE);
49   image->width = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_WIDTH);
50   image->repeat = _gtk_theming_engine_peek_property (engine, GTK_CSS_PROPERTY_BORDER_IMAGE_REPEAT);
51
52   return TRUE;
53 }
54
55 typedef struct _GtkBorderImageSliceSize GtkBorderImageSliceSize;
56 struct _GtkBorderImageSliceSize {
57   double offset;
58   double size;
59 };
60
61 static void
62 gtk_border_image_compute_border_size (GtkBorderImageSliceSize  sizes[3],
63                                       double                   offset,
64                                       double                   area_size,
65                                       double                   start_border_width,
66                                       double                   end_border_width,
67                                       const GtkCssValue       *start_border,
68                                       const GtkCssValue       *end_border)
69 {
70   double start, end;
71
72   if (_gtk_css_number_value_get_unit (start_border) == GTK_CSS_NUMBER)
73     start = start_border_width * _gtk_css_number_value_get (start_border, 100);
74   else
75     start = _gtk_css_number_value_get (start_border, area_size);
76   if (_gtk_css_number_value_get_unit (end_border) == GTK_CSS_NUMBER)
77     end = end_border_width * _gtk_css_number_value_get (end_border, 100);
78   else
79     end = _gtk_css_number_value_get (end_border, area_size);
80
81   /* XXX: reduce vertical and horizontal by the same factor */
82   if (start + end > area_size)
83     {
84       start = start * area_size / (start + end);
85       end = end * area_size / (start + end);
86     }
87
88   sizes[0].offset = offset;
89   sizes[0].size = start;
90   sizes[1].offset = offset + start;
91   sizes[1].size = area_size - start - end;
92   sizes[2].offset = offset + area_size - end;
93   sizes[2].size = end;
94 }
95
96 static void
97 gtk_border_image_render_slice (cairo_t           *cr,
98                                cairo_surface_t   *slice,
99                                double             slice_width,
100                                double             slice_height,
101                                double             x,
102                                double             y,
103                                double             width,
104                                double             height,
105                                GtkCssRepeatStyle  hrepeat,
106                                GtkCssRepeatStyle  vrepeat)
107 {
108   double hscale, vscale;
109   double xstep, ystep;
110   cairo_extend_t extend = CAIRO_EXTEND_PAD;
111   cairo_matrix_t matrix;
112   cairo_pattern_t *pattern;
113
114   /* We can't draw center tiles yet */
115   g_assert (hrepeat == GTK_CSS_REPEAT_STYLE_STRETCH || vrepeat == GTK_CSS_REPEAT_STYLE_STRETCH);
116
117   hscale = width / slice_width;
118   vscale = height / slice_height;
119   xstep = width;
120   ystep = height;
121
122   switch (hrepeat)
123     {
124     case GTK_CSS_REPEAT_STYLE_REPEAT:
125       extend = CAIRO_EXTEND_REPEAT;
126       hscale = vscale;
127       break;
128     case GTK_CSS_REPEAT_STYLE_SPACE:
129       {
130         double space, n;
131
132         extend = CAIRO_EXTEND_NONE;
133         hscale = vscale;
134
135         xstep = hscale * slice_width;
136         n = floor (width / xstep);
137         space = (width - n * xstep) / (n + 1);
138         xstep += space;
139         x += space;
140         width -= 2 * space;
141       }
142       break;
143     case GTK_CSS_REPEAT_STYLE_STRETCH:
144       break;
145     case GTK_CSS_REPEAT_STYLE_ROUND:
146       extend = CAIRO_EXTEND_REPEAT;
147       hscale = width / (slice_width * MAX (round (width / (slice_width * vscale)), 1));
148       break;
149     default:
150       g_assert_not_reached ();
151       break;
152     }
153
154   switch (vrepeat)
155     {
156     case GTK_CSS_REPEAT_STYLE_REPEAT:
157       extend = CAIRO_EXTEND_REPEAT;
158       vscale = hscale;
159       break;
160     case GTK_CSS_REPEAT_STYLE_SPACE:
161       {
162         double space, n;
163
164         extend = CAIRO_EXTEND_NONE;
165         vscale = hscale;
166
167         ystep = vscale * slice_height;
168         n = floor (height / ystep);
169         space = (height - n * ystep) / (n + 1);
170         ystep += space;
171         y += space;
172         height -= 2 * space;
173       }
174       break;
175     case GTK_CSS_REPEAT_STYLE_STRETCH:
176       break;
177     case GTK_CSS_REPEAT_STYLE_ROUND:
178       extend = CAIRO_EXTEND_REPEAT;
179       vscale = height / (slice_height * MAX (round (height / (slice_height * hscale)), 1));
180       break;
181     default:
182       g_assert_not_reached ();
183       break;
184     }
185
186   pattern = cairo_pattern_create_for_surface (slice);
187
188   cairo_matrix_init_translate (&matrix,
189                                hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_width / 2 : 0,
190                                vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? slice_height / 2 : 0);
191   cairo_matrix_scale (&matrix, 1 / hscale, 1 / vscale);
192   cairo_matrix_translate (&matrix,
193                           hrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - width / 2 : 0,
194                           vrepeat == GTK_CSS_REPEAT_STYLE_REPEAT ? - height / 2 : 0);
195
196   cairo_pattern_set_matrix (pattern, &matrix);
197   cairo_pattern_set_extend (pattern, extend);
198
199   cairo_save (cr);
200   cairo_translate (cr, x, y);
201
202   for (y = 0; y < height; y += ystep)
203     {
204       for (x = 0; x < width; x += xstep)
205         {
206           cairo_save (cr);
207           cairo_translate (cr, x, y);
208           cairo_set_source (cr, pattern);
209           cairo_rectangle (cr, 0, 0, xstep, ystep);
210           cairo_fill (cr);
211           cairo_restore (cr);
212         }
213     }
214
215   cairo_restore (cr);
216
217   cairo_pattern_destroy (pattern);
218 }
219
220 static void
221 gtk_border_image_compute_slice_size (GtkBorderImageSliceSize sizes[3],
222                                      int                     surface_size,
223                                      int                     start_size,
224                                      int                     end_size)
225 {
226   sizes[0].size = MIN (start_size, surface_size);
227   sizes[0].offset = 0;
228
229   sizes[2].size = MIN (end_size, surface_size);
230   sizes[2].offset = surface_size - sizes[2].size;
231
232   sizes[1].size = MAX (0, surface_size - sizes[0].size - sizes[2].size);
233   sizes[1].offset = sizes[0].size;
234 }
235
236 void
237 _gtk_border_image_render (GtkBorderImage   *image,
238                           const double      border_width[4],
239                           cairo_t          *cr,
240                           gdouble           x,
241                           gdouble           y,
242                           gdouble           width,
243                           gdouble           height)
244 {
245   cairo_surface_t *surface, *slice;
246   GtkBorderImageSliceSize vertical_slice[3], horizontal_slice[3];
247   GtkBorderImageSliceSize vertical_border[3], horizontal_border[3];
248   double source_width, source_height;
249   int h, v;
250
251   _gtk_css_image_get_concrete_size (image->source,
252                                     0, 0,
253                                     width, height,
254                                     &source_width, &source_height);
255
256   /* XXX: Optimize for (source_width == width && source_height == height) */
257
258   surface = _gtk_css_image_get_surface (image->source,
259                                         cairo_get_target (cr),
260                                         source_width, source_height);
261
262   gtk_border_image_compute_slice_size (horizontal_slice,
263                                        source_width, 
264                                        _gtk_css_number_value_get (_gtk_css_border_value_get_left (image->slice), source_width),
265                                        _gtk_css_number_value_get (_gtk_css_border_value_get_right (image->slice), source_width));
266   gtk_border_image_compute_slice_size (vertical_slice,
267                                        source_height, 
268                                        _gtk_css_number_value_get (_gtk_css_border_value_get_top (image->slice), source_height),
269                                        _gtk_css_number_value_get (_gtk_css_border_value_get_bottom (image->slice), source_height));
270   gtk_border_image_compute_border_size (horizontal_border,
271                                         x,
272                                         width,
273                                         border_width[GTK_CSS_LEFT],
274                                         border_width[GTK_CSS_RIGHT],
275                                         _gtk_css_border_value_get_left (image->width),
276                                         _gtk_css_border_value_get_right (image->width));
277   gtk_border_image_compute_border_size (vertical_border,
278                                         y,
279                                         height,
280                                         border_width[GTK_CSS_TOP],
281                                         border_width[GTK_CSS_BOTTOM],
282                                         _gtk_css_border_value_get_top (image->width),
283                                         _gtk_css_border_value_get_bottom(image->width));
284   
285   for (v = 0; v < 3; v++)
286     {
287       if (vertical_slice[v].size == 0 ||
288           vertical_border[v].size == 0)
289         continue;
290
291       for (h = 0; h < 3; h++)
292         {
293           if (horizontal_slice[h].size == 0 ||
294               horizontal_border[h].size == 0)
295             continue;
296
297           if (h == 1 && v == 1)
298             continue;
299
300           slice = cairo_surface_create_for_rectangle (surface,
301                                                       horizontal_slice[h].offset,
302                                                       vertical_slice[v].offset,
303                                                       horizontal_slice[h].size,
304                                                       vertical_slice[v].size);
305
306           gtk_border_image_render_slice (cr,
307                                          slice,
308                                          horizontal_slice[h].size,
309                                          vertical_slice[v].size,
310                                          horizontal_border[h].offset,
311                                          vertical_border[v].offset,
312                                          horizontal_border[h].size,
313                                          vertical_border[v].size,
314                                          h == 1 ? _gtk_css_border_repeat_value_get_x (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH,
315                                          v == 1 ? _gtk_css_border_repeat_value_get_y (image->repeat) : GTK_CSS_REPEAT_STYLE_STRETCH);
316
317           cairo_surface_destroy (slice);
318         }
319     }
320
321   cairo_surface_destroy (surface);
322 }