]> Pileus Git - ~andy/gtk/blob - gtk/gtk9slice.c
wayland: Remove non-existing gdkscreen-wayland.h from SOURCES
[~andy/gtk] / gtk / gtk9slice.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "gtk9slice.h"
22
23 enum {
24   BORDER_LEFT,
25   BORDER_MIDDLE,
26   BORDER_RIGHT,
27   BORDER_LAST,
28   BORDER_TOP = BORDER_LEFT,
29   BORDER_BOTTOM = BORDER_RIGHT
30 };
31
32 enum {
33   SIDE_TOP,
34   SIDE_RIGHT,
35   SIDE_BOTTOM,
36   SIDE_LEFT
37 };
38
39 struct Gtk9Slice
40 {
41   cairo_surface_t *surfaces[BORDER_LAST][BORDER_LAST];
42   GtkSliceSideModifier modifiers[4];
43   gdouble distances[4];
44   gint ref_count;
45 };
46
47 G_DEFINE_BOXED_TYPE (Gtk9Slice, _gtk_9slice,
48                      _gtk_9slice_ref, _gtk_9slice_unref)
49
50
51 Gtk9Slice *
52 _gtk_9slice_new (GdkPixbuf            *pixbuf,
53                  gdouble               distance_top,
54                  gdouble               distance_bottom,
55                  gdouble               distance_left,
56                  gdouble               distance_right,
57                  GtkSliceSideModifier  horizontal_modifier,
58                  GtkSliceSideModifier  vertical_modifier)
59 {
60   Gtk9Slice *slice;
61   cairo_surface_t *surface;
62   gint width, height;
63   cairo_t *cr;
64
65   g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
66
67   slice = g_slice_new0 (Gtk9Slice);
68   slice->ref_count = 1;
69
70   slice->distances[SIDE_TOP] = distance_top;
71   slice->distances[SIDE_BOTTOM] = distance_bottom;
72   slice->distances[SIDE_LEFT] = distance_left;
73   slice->distances[SIDE_RIGHT] = distance_right;
74
75   slice->modifiers[SIDE_TOP] = slice->modifiers[SIDE_BOTTOM] = horizontal_modifier;
76   slice->modifiers[SIDE_LEFT] = slice->modifiers[SIDE_RIGHT] = vertical_modifier;
77
78   width = gdk_pixbuf_get_width (pixbuf);
79   height = gdk_pixbuf_get_height (pixbuf);
80
81   /* Get an image surface from the pixbuf */
82   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
83   cr = cairo_create (surface);
84
85   gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
86   cairo_paint (cr);
87
88   cairo_destroy (cr);
89
90   /* Top /left corner */
91   slice->surfaces[BORDER_LEFT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
92                                                                            distance_left, distance_top);
93   cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_TOP]);
94
95   cairo_set_source_surface (cr, surface, 0, 0);
96   cairo_paint (cr);
97
98   cairo_destroy (cr);
99
100   /* Top/right corner */
101   slice->surfaces[BORDER_RIGHT][BORDER_TOP] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
102                                                                             distance_right, distance_top);
103   cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_TOP]);
104
105   cairo_set_source_surface (cr, surface, - width + distance_right, 0);
106   cairo_paint (cr);
107
108   cairo_destroy (cr);
109
110   /* Bottom/left corner */
111   slice->surfaces[BORDER_LEFT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
112                                                                               distance_left, distance_bottom);
113   cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_BOTTOM]);
114
115   cairo_set_source_surface (cr, surface, 0, - height + distance_bottom);
116   cairo_paint (cr);
117
118   cairo_destroy (cr);
119
120   /* Bottom/right corner */
121   slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM] = cairo_surface_create_similar (surface, CAIRO_CONTENT_COLOR_ALPHA,
122                                                                                distance_right, distance_bottom);
123   cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM]);
124
125   cairo_set_source_surface (cr, surface, - width + distance_right, - height + distance_bottom);
126   cairo_paint (cr);
127
128   cairo_destroy (cr);
129
130   /* Top side */
131   slice->surfaces[BORDER_MIDDLE][BORDER_TOP] = cairo_surface_create_similar (surface,
132                                                                              CAIRO_CONTENT_COLOR_ALPHA,
133                                                                              width - distance_left - distance_right,
134                                                                              distance_top);
135   cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_TOP]);
136   cairo_set_source_surface (cr, surface, - distance_left, 0);
137   cairo_paint (cr);
138   cairo_destroy (cr);
139
140   /* Bottom side */
141   slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM] = cairo_surface_create_similar (surface,
142                                                                                 CAIRO_CONTENT_COLOR_ALPHA,
143                                                                                 width - distance_left - distance_right,
144                                                                                 distance_bottom);
145   cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM]);
146   cairo_set_source_surface (cr, surface, - distance_left, - height + distance_bottom);
147   cairo_paint (cr);
148   cairo_destroy (cr);
149
150   /* Left side */
151   slice->surfaces[BORDER_LEFT][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
152                                                                               CAIRO_CONTENT_COLOR_ALPHA,
153                                                                               distance_left,
154                                                                               height - distance_top - distance_bottom);
155   cr = cairo_create (slice->surfaces[BORDER_LEFT][BORDER_MIDDLE]);
156   cairo_set_source_surface (cr, surface, 0, - distance_top);
157   cairo_paint (cr);
158   cairo_destroy (cr);
159
160   /* Right side */
161   slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
162                                                                                CAIRO_CONTENT_COLOR_ALPHA,
163                                                                                distance_right,
164                                                                                height - distance_top - distance_bottom);
165   cr = cairo_create (slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE]);
166   cairo_set_source_surface (cr, surface, - width + distance_right, - distance_top);
167   cairo_paint (cr);
168   cairo_destroy (cr);
169
170   /* Center */
171   slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE] = cairo_surface_create_similar (surface,
172                                                                                 CAIRO_CONTENT_COLOR_ALPHA,
173                                                                                 width - distance_left - distance_right,
174                                                                                 height - distance_top - distance_bottom);
175   cr = cairo_create (slice->surfaces[BORDER_MIDDLE][BORDER_MIDDLE]);
176   cairo_set_source_surface (cr, surface, - distance_left, - distance_top);
177   cairo_paint (cr);
178   cairo_destroy (cr);
179
180   cairo_surface_destroy (surface);
181
182   return slice;
183 }
184
185 static void
186 render_border (cairo_t              *cr,
187                cairo_surface_t      *surface,
188                guint                 side,
189                gdouble               x,
190                gdouble               y,
191                gdouble               width,
192                gdouble               height,
193                GtkSliceSideModifier  modifier)
194 {
195   cairo_pattern_t *pattern;
196   cairo_matrix_t matrix;
197
198   cairo_save (cr);
199
200   cairo_rectangle (cr, x, y, width, height);
201   cairo_clip (cr);
202
203   pattern = cairo_pattern_create_for_surface (surface);
204
205   if (modifier == GTK_SLICE_REPEAT)
206     {
207       cairo_matrix_init_translate (&matrix, - x, - y);
208       cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT);
209
210       cairo_pattern_set_matrix (pattern, &matrix);
211       cairo_set_source (cr, pattern);
212
213       cairo_rectangle (cr, x, y, width, height);
214       cairo_fill (cr);
215     }
216   else
217     {
218       /* Use nearest filter so borders aren't blurred */
219       cairo_pattern_set_filter (pattern, CAIRO_FILTER_NEAREST);
220
221       if (side == SIDE_TOP || side == SIDE_BOTTOM)
222         {
223           gint w = cairo_image_surface_get_width (surface);
224
225           cairo_translate (cr, x, y);
226           cairo_scale (cr, width / w, 1.0);
227         }
228       else
229         {
230           gint h = cairo_image_surface_get_height (surface);
231
232           cairo_translate (cr, x, y);
233           cairo_scale (cr, 1.0, height / h);
234         }
235
236       cairo_set_source (cr, pattern);
237       cairo_rectangle (cr, x, y, width, height);
238       cairo_paint (cr);
239     }
240
241   cairo_restore (cr);
242
243   cairo_pattern_destroy (pattern);
244 }
245
246 static void
247 render_corner (cairo_t         *cr,
248                cairo_surface_t *surface,
249                gdouble          x,
250                gdouble          y,
251                gdouble          width,
252                gdouble          height)
253 {
254   cairo_save (cr);
255
256   cairo_rectangle (cr, x, y, width, height);
257   cairo_clip (cr);
258
259   cairo_set_source_surface (cr, surface, x, y);
260   cairo_rectangle (cr, x, y, width, height);
261   cairo_fill (cr);
262
263   cairo_restore (cr);
264 }
265
266 void
267 _gtk_9slice_render (Gtk9Slice *slice,
268                     cairo_t   *cr,
269                     gdouble    x,
270                     gdouble    y,
271                     gdouble    width,
272                     gdouble    height)
273 {
274   int img_width, img_height;
275   cairo_surface_t *surface;
276
277   cairo_save (cr);
278
279   /* Top side */
280   surface = slice->surfaces[BORDER_MIDDLE][BORDER_TOP];
281   img_height = cairo_image_surface_get_height (surface);
282
283   render_border (cr, surface, SIDE_TOP,
284                  x + slice->distances[SIDE_LEFT], y,
285                  width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT],
286                  (gdouble) img_height,
287                  slice->modifiers[SIDE_TOP]);
288
289   /* Bottom side */
290   surface = slice->surfaces[BORDER_MIDDLE][BORDER_BOTTOM];
291   img_height = cairo_image_surface_get_height (surface);
292
293   render_border (cr, surface, SIDE_BOTTOM,
294                  x + slice->distances[SIDE_LEFT], y + height - img_height,
295                  width - slice->distances[SIDE_LEFT] - slice->distances[SIDE_RIGHT],
296                  (gdouble) img_height,
297                  slice->modifiers[SIDE_BOTTOM]);
298
299   /* Left side */
300   surface = slice->surfaces[BORDER_LEFT][BORDER_MIDDLE];
301   img_width = cairo_image_surface_get_width (surface);
302
303   render_border (cr, surface, SIDE_LEFT,
304                  x, y + slice->distances[SIDE_TOP],
305                  (gdouble) img_width,
306                  height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM],
307                  slice->modifiers[SIDE_LEFT]);
308
309   /* Right side */
310   surface = slice->surfaces[BORDER_RIGHT][BORDER_MIDDLE];
311   img_width = cairo_image_surface_get_width (surface);
312
313   render_border (cr, surface, SIDE_RIGHT,
314                  x + width - img_width, y + slice->distances[SIDE_TOP],
315                  (gdouble) img_width,
316                  height - slice->distances[SIDE_TOP] - slice->distances[SIDE_BOTTOM],
317                  slice->modifiers[SIDE_RIGHT]);
318
319   /* Top/Left corner */
320   surface = slice->surfaces[BORDER_LEFT][BORDER_TOP];
321   img_width = cairo_image_surface_get_width (surface);
322   img_height = cairo_image_surface_get_height (surface);
323   render_corner (cr, surface, x, y, (gdouble) img_width, (gdouble) img_height);
324
325   /* Top/right corner */
326   surface = slice->surfaces[BORDER_RIGHT][BORDER_TOP];
327   img_width = cairo_image_surface_get_width (surface);
328   img_height = cairo_image_surface_get_height (surface);
329   render_corner (cr, surface, x + width - img_width, y,
330                  (gdouble) img_width, (gdouble) img_height);
331
332   /* Bottom/left corner */
333   surface = slice->surfaces[BORDER_LEFT][BORDER_BOTTOM];
334   img_width = cairo_image_surface_get_width (surface);
335   img_height = cairo_image_surface_get_height (surface);
336   render_corner (cr, surface, x, y + height - img_height,
337                  (gdouble) img_width, (gdouble) img_height);
338
339   /* Bottom/right corner */
340   surface = slice->surfaces[BORDER_RIGHT][BORDER_BOTTOM];
341   img_width = cairo_image_surface_get_width (surface);
342   img_height = cairo_image_surface_get_height (surface);
343   render_corner (cr, surface, x + width - img_width, y + height - img_height,
344                  (gdouble) img_width, (gdouble) img_height);
345
346   cairo_restore (cr);
347 }
348
349 Gtk9Slice *
350 _gtk_9slice_ref (Gtk9Slice *slice)
351 {
352   g_return_val_if_fail (slice != NULL, NULL);
353
354   slice->ref_count++;
355   return slice;
356 }
357
358 void
359 _gtk_9slice_unref (Gtk9Slice *slice)
360 {
361   g_return_if_fail (slice != NULL);
362
363   slice->ref_count--;
364
365   if (slice->ref_count == 0)
366     {
367       gint i, j;
368
369       for (i = 0; i < BORDER_LAST; i++)
370         for (j = 0; j < BORDER_LAST; j++)
371           cairo_surface_destroy (slice->surfaces[i][j]);
372
373       g_slice_free (Gtk9Slice, slice);
374     }
375 }