1 /*
2 * Copyright (c) 2011, 2012, 2015, 2016 Red Hat, Inc.
3 *
4 * Gnome Documents is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * Gnome Documents is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with Gnome Documents; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author: Cosimo Cecchi <cosimoc@redhat.com>
19 *
20 */
21
22 #include "gd-icon-utils.h"
23
24 #include <gdk-pixbuf/gdk-pixbuf.h>
25 #include <string.h>
26 #include <math.h>
27
28 #define _BG_MIN_SIZE 20
29 #define _EMBLEM_MIN_SIZE 8
30
31 /**
32 * gd_copy_image_surface:
33 * @surface:
34 *
35 * Returns: (transfer full):
36 */
37 cairo_surface_t *
gd_copy_image_surface(cairo_surface_t * surface)38 gd_copy_image_surface (cairo_surface_t *surface)
39 {
40 cairo_surface_t *copy = NULL;
41 cairo_t *cr;
42 gdouble scale_x;
43 gdouble scale_y;
44
45 copy = cairo_surface_create_similar_image (surface, CAIRO_FORMAT_ARGB32,
46 cairo_image_surface_get_width (surface),
47 cairo_image_surface_get_height (surface));
48 cairo_surface_get_device_scale (surface, &scale_x, &scale_y);
49 cairo_surface_set_device_scale (copy, scale_x, scale_y);
50
51 cr = cairo_create (copy);
52 cairo_set_source_surface (cr, surface, 0, 0);
53 cairo_paint (cr);
54 cairo_destroy (cr);
55
56 return copy;
57 }
58
59 /**
60 * gd_create_surface_with_counter:
61 * @widget:
62 * @base:
63 * @number:
64 *
65 * Returns: (transfer full):
66 */
67 cairo_surface_t *
gd_create_surface_with_counter(GtkWidget * widget,cairo_surface_t * base,gint number)68 gd_create_surface_with_counter (GtkWidget *widget, cairo_surface_t *base, gint number)
69 {
70 GtkStyleContext *context;
71 cairo_t *cr, *emblem_cr;
72 cairo_surface_t *emblem_surface;
73 cairo_surface_t *surface;
74 gint height;
75 gint height_scaled;
76 gint width;
77 gint width_scaled;
78 gint layout_width, layout_height;
79 gint emblem_size;
80 gint emblem_size_scaled;
81 gdouble scale;
82 gdouble scale_x;
83 gdouble scale_y;
84 gchar *str;
85 PangoLayout *layout;
86 PangoAttrList *attr_list;
87 PangoAttribute *attr;
88 PangoFontDescription *desc;
89 GdkRGBA color;
90
91 context = gtk_widget_get_style_context (GTK_WIDGET (widget));
92 gtk_style_context_save (context);
93 gtk_style_context_add_class (context, "documents-counter");
94
95 width_scaled = cairo_image_surface_get_width (base);
96 height_scaled = cairo_image_surface_get_height (base);
97 cairo_surface_get_device_scale (base, &scale_x, &scale_y);
98
99 width = width_scaled / (gint) floor (scale_x),
100 height = height_scaled / (gint) floor (scale_y);
101
102 surface = cairo_surface_create_similar_image (base, CAIRO_FORMAT_ARGB32,
103 width_scaled, height_scaled);
104 cairo_surface_set_device_scale (surface, scale_x, scale_y);
105
106 cr = cairo_create (surface);
107 cairo_set_source_surface (cr, base, 0, 0);
108 cairo_paint (cr);
109
110 emblem_size_scaled = MIN (width_scaled / 2, height_scaled / 2);
111 emblem_size = MIN (width / 2, height / 2);
112
113 emblem_surface = cairo_surface_create_similar_image (base, CAIRO_FORMAT_ARGB32,
114 emblem_size_scaled, emblem_size_scaled);
115 cairo_surface_set_device_scale (emblem_surface, scale_x, scale_y);
116
117 emblem_cr = cairo_create (emblem_surface);
118 gtk_render_background (context, emblem_cr,
119 0, 0, emblem_size, emblem_size);
120
121 if (number > 99)
122 number = 99;
123 if (number < -99)
124 number = -99;
125
126 str = g_strdup_printf ("%d", number);
127 layout = gtk_widget_create_pango_layout (GTK_WIDGET (widget), str);
128 g_free (str);
129
130 pango_layout_get_pixel_size (layout, &layout_width, &layout_height);
131
132 /* scale the layout to be 0.5 of the size still available for drawing */
133 scale = (emblem_size * 0.50) / (MAX (layout_width, layout_height));
134 attr_list = pango_attr_list_new ();
135
136 attr = pango_attr_scale_new (scale);
137 pango_attr_list_insert (attr_list, attr);
138 pango_layout_set_attributes (layout, attr_list);
139
140 gtk_style_context_get (context, GTK_STATE_FLAG_NORMAL, "font", &desc, NULL);
141 pango_layout_set_font_description (layout, desc);
142 pango_font_description_free (desc);
143
144 gtk_style_context_get_color (context, 0, &color);
145 gdk_cairo_set_source_rgba (emblem_cr, &color);
146
147 /* update these values */
148 pango_layout_get_pixel_size (layout, &layout_width, &layout_height);
149
150 cairo_move_to (emblem_cr,
151 emblem_size / 2 - layout_width / 2,
152 emblem_size / 2 - layout_height / 2);
153
154 pango_cairo_show_layout (emblem_cr, layout);
155
156 g_object_unref (layout);
157 pango_attr_list_unref (attr_list);
158 cairo_destroy (emblem_cr);
159
160 cairo_set_source_surface (cr, emblem_surface,
161 width - emblem_size, height - emblem_size);
162 cairo_paint (cr);
163 cairo_destroy (cr);
164
165 cairo_surface_destroy (emblem_surface);
166 gtk_style_context_restore (context);
167
168 return surface;
169 }
170
171 /**
172 * gd_create_symbolic_icon_for_scale:
173 * @name:
174 * @base_size:
175 * @scale:
176 *
177 * Returns: (transfer full):
178 */
179 GIcon *
gd_create_symbolic_icon_for_scale(const gchar * name,gint base_size,gint scale)180 gd_create_symbolic_icon_for_scale (const gchar *name,
181 gint base_size,
182 gint scale)
183 {
184 gchar *symbolic_name;
185 GIcon *icon, *retval = NULL;
186 cairo_surface_t *icon_surface;
187 cairo_surface_t *surface;
188 cairo_t *cr;
189 GtkStyleContext *style;
190 GtkWidgetPath *path;
191 GdkPixbuf *pixbuf;
192 GtkIconTheme *theme;
193 GtkIconInfo *info;
194 gint bg_size;
195 gint emblem_size;
196 gint total_size;
197 gint total_size_scaled;
198
199 total_size = base_size / 2;
200 total_size_scaled = total_size * scale;
201
202 bg_size = MAX (total_size / 2, _BG_MIN_SIZE);
203 emblem_size = MAX (bg_size - 8, _EMBLEM_MIN_SIZE);
204
205 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, total_size_scaled, total_size_scaled);
206 cairo_surface_set_device_scale (surface, (gdouble) scale, (gdouble) scale);
207 cr = cairo_create (surface);
208
209 style = gtk_style_context_new ();
210
211 path = gtk_widget_path_new ();
212 gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
213 gtk_style_context_set_path (style, path);
214 gtk_widget_path_unref (path);
215
216 gtk_style_context_add_class (style, "documents-icon-bg");
217
218 gtk_render_background (style, cr, (total_size - bg_size) / 2, (total_size - bg_size) / 2, bg_size, bg_size);
219
220 symbolic_name = g_strconcat (name, "-symbolic", NULL);
221 icon = g_themed_icon_new_with_default_fallbacks (symbolic_name);
222 g_free (symbolic_name);
223
224 theme = gtk_icon_theme_get_default();
225 info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, emblem_size, scale,
226 GTK_ICON_LOOKUP_FORCE_SIZE);
227 g_object_unref (icon);
228
229 if (info == NULL)
230 goto out;
231
232 pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL);
233 g_object_unref (info);
234
235 if (pixbuf == NULL)
236 goto out;
237
238 icon_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL);
239 g_object_unref (pixbuf);
240
241 gtk_render_icon_surface (style, cr, icon_surface, (total_size - emblem_size) / 2, (total_size - emblem_size) / 2);
242 cairo_surface_destroy (icon_surface);
243
244 retval = G_ICON (gdk_pixbuf_get_from_surface (surface, 0, 0, total_size_scaled, total_size_scaled));
245
246 out:
247 g_object_unref (style);
248 cairo_surface_destroy (surface);
249 cairo_destroy (cr);
250
251 return retval;
252 }
253
254 /**
255 * gd_create_symbolic_icon:
256 * @name:
257 * @base_size:
258 *
259 * Returns: (transfer full):
260 */
261 GIcon *
gd_create_symbolic_icon(const gchar * name,gint base_size)262 gd_create_symbolic_icon (const gchar *name,
263 gint base_size)
264 {
265 return gd_create_symbolic_icon_for_scale (name, base_size, 1);
266 }
267
268 /**
269 * gd_embed_surface_in_frame:
270 * @source_image:
271 * @frame_image_url:
272 * @slice_width:
273 * @border_width:
274 *
275 * Returns: (transfer full):
276 */
277 cairo_surface_t *
gd_embed_surface_in_frame(cairo_surface_t * source_image,const gchar * frame_image_url,GtkBorder * slice_width,GtkBorder * border_width)278 gd_embed_surface_in_frame (cairo_surface_t *source_image,
279 const gchar *frame_image_url,
280 GtkBorder *slice_width,
281 GtkBorder *border_width)
282 {
283 cairo_surface_t *surface;
284 cairo_t *cr;
285 int source_width, source_height;
286 gchar *css_str;
287 GtkCssProvider *provider;
288 GtkStyleContext *context;
289 GError *error = NULL;
290 GtkWidgetPath *path;
291 gdouble scale_x, scale_y;
292
293 cairo_surface_get_device_scale (source_image, &scale_x, &scale_y);
294
295 source_width = cairo_image_surface_get_width (source_image) / (gint) floor (scale_x),
296 source_height = cairo_image_surface_get_height (source_image) / (gint) floor (scale_y);
297
298 css_str = g_strdup_printf (".embedded-image { border-image: url(\"%s\") %d %d %d %d / %dpx %dpx %dpx %dpx }",
299 frame_image_url,
300 slice_width->top, slice_width->right, slice_width->bottom, slice_width->left,
301 border_width->top, border_width->right, border_width->bottom, border_width->left);
302 provider = gtk_css_provider_new ();
303 gtk_css_provider_load_from_data (provider, css_str, -1, &error);
304
305 if (error != NULL)
306 {
307 g_warning ("Unable to create the thumbnail frame image: %s", error->message);
308 g_error_free (error);
309 g_free (css_str);
310
311 return g_object_ref (source_image);
312 }
313
314 surface = cairo_surface_create_similar (source_image,
315 CAIRO_CONTENT_COLOR_ALPHA,
316 source_width, source_height);
317 cr = cairo_create (surface);
318
319 context = gtk_style_context_new ();
320 path = gtk_widget_path_new ();
321 gtk_widget_path_append_type (path, GTK_TYPE_ICON_VIEW);
322
323 gtk_style_context_set_path (context, path);
324 gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), 600);
325
326 cairo_save (cr);
327 cairo_rectangle (cr,
328 border_width->left,
329 border_width->top,
330 source_width - border_width->left - border_width->right,
331 source_height - border_width->top - border_width->bottom);
332 cairo_clip (cr);
333 gtk_render_icon_surface (context, cr,
334 source_image,
335 0, 0);
336 cairo_restore (cr);
337
338 gtk_style_context_save (context);
339 gtk_style_context_add_class (context, "embedded-image");
340
341 gtk_render_frame (context, cr,
342 0, 0,
343 source_width, source_height);
344
345 gtk_style_context_restore (context);
346 cairo_destroy (cr);
347
348 gtk_widget_path_unref (path);
349 g_object_unref (provider);
350 g_object_unref (context);
351 g_free (css_str);
352
353 return surface;
354 }
355
356 /**
357 * gd_embed_image_in_frame:
358 * @source_image:
359 * @frame_image_url:
360 * @slice_width:
361 * @border_width:
362 *
363 * Returns: (transfer full):
364 */
365 GdkPixbuf *
gd_embed_image_in_frame(GdkPixbuf * source_image,const gchar * frame_image_url,GtkBorder * slice_width,GtkBorder * border_width)366 gd_embed_image_in_frame (GdkPixbuf *source_image,
367 const gchar *frame_image_url,
368 GtkBorder *slice_width,
369 GtkBorder *border_width)
370 {
371 cairo_surface_t *surface, *embedded_surface;
372 GdkPixbuf *retval;
373
374 surface = gdk_cairo_surface_create_from_pixbuf (source_image,
375 0, NULL);
376
377 /* Force the device scale to 1.0, since pixbufs are always in unscaled
378 * dimensions.
379 */
380 cairo_surface_set_device_scale (surface, 1.0, 1.0);
381 embedded_surface = gd_embed_surface_in_frame (surface, frame_image_url,
382 slice_width, border_width);
383 retval = gdk_pixbuf_get_from_surface (embedded_surface,
384 0, 0,
385 cairo_image_surface_get_width (embedded_surface),
386 cairo_image_surface_get_height (embedded_surface));
387
388 cairo_surface_destroy (embedded_surface);
389 cairo_surface_destroy (surface);
390
391 return retval;
392 }
393