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