1 /*-
2 * Copyright (c) 2004-2007 os-cillation e.K.
3 * Copyright (c) 2003 Red Hat, Inc.
4 *
5 * Written by Benedikt Meurer <benny@xfce.org>.
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <terminal/terminal-image-loader.h>
26 #include <terminal/terminal-private.h>
27
28 /* max image resolution is 8K */
29 #define MAX_IMAGE_WIDTH 7680
30 #define MAX_IMAGE_HEIGHT 4320
31
32
33
34 static void terminal_image_loader_finalize (GObject *object);
35 static void terminal_image_loader_check (TerminalImageLoader *loader);
36 static void terminal_image_loader_tile (TerminalImageLoader *loader,
37 GdkPixbuf *target,
38 gint width,
39 gint height);
40 static void terminal_image_loader_center (TerminalImageLoader *loader,
41 GdkPixbuf *target,
42 gint width,
43 gint height);
44 static void terminal_image_loader_scale (TerminalImageLoader *loader,
45 GdkPixbuf *target,
46 gint width,
47 gint height);
48 static void terminal_image_loader_stretch (TerminalImageLoader *loader,
49 GdkPixbuf *target,
50 gint width,
51 gint height);
52
53
54 struct _TerminalImageLoaderClass
55 {
56 GObjectClass parent_class;
57 };
58
59 struct _TerminalImageLoader
60 {
61 GObject parent_instance;
62 TerminalPreferences *preferences;
63
64 /* the cached image data */
65 gchar *path;
66 GSList *cache;
67 GSList *cache_invalid;
68 GdkRGBA bgcolor;
69 GdkPixbuf *pixbuf;
70 TerminalBackgroundStyle style;
71 };
72
73
74
G_DEFINE_TYPE(TerminalImageLoader,terminal_image_loader,G_TYPE_OBJECT)75 G_DEFINE_TYPE (TerminalImageLoader, terminal_image_loader, G_TYPE_OBJECT)
76
77
78
79 static void
80 terminal_image_loader_class_init (TerminalImageLoaderClass *klass)
81 {
82 GObjectClass *gobject_class;
83
84 gobject_class = G_OBJECT_CLASS (klass);
85 gobject_class->finalize = terminal_image_loader_finalize;
86 }
87
88
89
90 static void
terminal_image_loader_init(TerminalImageLoader * loader)91 terminal_image_loader_init (TerminalImageLoader *loader)
92 {
93 loader->preferences = terminal_preferences_get ();
94 }
95
96
97
98 static void
terminal_image_loader_finalize(GObject * object)99 terminal_image_loader_finalize (GObject *object)
100 {
101 TerminalImageLoader *loader = TERMINAL_IMAGE_LOADER (object);
102
103 g_slist_free_full (loader->cache, g_object_unref);
104 g_slist_free_full (loader->cache_invalid, g_object_unref);
105
106 g_object_unref (G_OBJECT (loader->preferences));
107
108 if (G_LIKELY (loader->pixbuf != NULL))
109 g_object_unref (G_OBJECT (loader->pixbuf));
110 g_free (loader->path);
111
112 (*G_OBJECT_CLASS (terminal_image_loader_parent_class)->finalize) (object);
113 }
114
115
116
117 static void
terminal_image_loader_check(TerminalImageLoader * loader)118 terminal_image_loader_check (TerminalImageLoader *loader)
119 {
120 TerminalBackgroundStyle selected_style;
121 GdkRGBA selected_color;
122 gboolean invalidate = FALSE;
123 gchar *selected_color_spec;
124 gchar *selected_path;
125
126 terminal_return_if_fail (TERMINAL_IS_IMAGE_LOADER (loader));
127
128 g_object_get (G_OBJECT (loader->preferences),
129 "background-image-file", &selected_path,
130 "background-image-style", &selected_style,
131 "color-background", &selected_color_spec,
132 NULL);
133
134 if (g_strcmp0 (selected_path, loader->path) != 0)
135 {
136 gint width, height;
137
138 g_free (loader->path);
139 loader->path = g_strdup (selected_path);
140
141 if (GDK_IS_PIXBUF (loader->pixbuf))
142 g_object_unref (G_OBJECT (loader->pixbuf));
143
144 if (gdk_pixbuf_get_file_info (loader->path, &width, &height) == NULL)
145 {
146 g_warning ("Unable to load background image file \"%s\"", loader->path);
147 loader->pixbuf = NULL;
148 }
149 else if (width <= MAX_IMAGE_WIDTH && height <= MAX_IMAGE_HEIGHT)
150 loader->pixbuf = gdk_pixbuf_new_from_file (loader->path, NULL);
151 else
152 loader->pixbuf = gdk_pixbuf_new_from_file_at_size (loader->path,
153 MAX_IMAGE_WIDTH, MAX_IMAGE_WIDTH,
154 NULL);
155
156 invalidate = TRUE;
157 }
158
159 if (selected_style != loader->style)
160 {
161 loader->style = selected_style;
162 invalidate = TRUE;
163 }
164
165 gdk_rgba_parse (&selected_color, selected_color_spec);
166 if (!gdk_rgba_equal (&selected_color, &loader->bgcolor))
167 {
168 loader->bgcolor = selected_color;
169 invalidate = TRUE;
170 }
171
172 if (invalidate)
173 {
174 loader->cache_invalid = g_slist_concat (loader->cache_invalid,
175 loader->cache);
176 loader->cache = NULL;
177 }
178
179 g_free (selected_color_spec);
180 g_free (selected_path);
181 }
182
183
184
185 static void
terminal_image_loader_tile(TerminalImageLoader * loader,GdkPixbuf * target,gint width,gint height)186 terminal_image_loader_tile (TerminalImageLoader *loader,
187 GdkPixbuf *target,
188 gint width,
189 gint height)
190 {
191 GdkRectangle area;
192 gint source_width;
193 gint source_height;
194 gint i;
195 gint j;
196
197 source_width = gdk_pixbuf_get_width (loader->pixbuf);
198 source_height = gdk_pixbuf_get_height (loader->pixbuf);
199
200 for (i = 0; (i * source_width) < width; ++i)
201 for (j = 0; (j * source_height) < height; ++j)
202 {
203 area.x = i * source_width;
204 area.y = j * source_height;
205 area.width = source_width;
206 area.height = source_height;
207
208 if (area.x + area.width > width)
209 area.width = width - area.x;
210 if (area.y + area.height > height)
211 area.height = height - area.y;
212
213 gdk_pixbuf_copy_area (loader->pixbuf, 0, 0,
214 area.width, area.height,
215 target, area.x, area.y);
216 }
217 }
218
219
220
221 static void
terminal_image_loader_center(TerminalImageLoader * loader,GdkPixbuf * target,gint width,gint height)222 terminal_image_loader_center (TerminalImageLoader *loader,
223 GdkPixbuf *target,
224 gint width,
225 gint height)
226 {
227 guint32 rgba;
228 gint source_width;
229 gint source_height;
230 gint dx;
231 gint dy;
232 gint x0;
233 gint y0;
234
235 /* fill with background color */
236 rgba = ((((guint)(loader->bgcolor.red * 65535) & 0xff00) << 8)
237 | (((guint)(loader->bgcolor.green * 65535) & 0xff00))
238 | (((guint)(loader->bgcolor.blue * 65535) & 0xff00) >> 8)) << 8;
239 gdk_pixbuf_fill (target, rgba);
240
241 source_width = gdk_pixbuf_get_width (loader->pixbuf);
242 source_height = gdk_pixbuf_get_height (loader->pixbuf);
243
244 dx = MAX ((width - source_width) / 2, 0);
245 dy = MAX ((height - source_height) / 2, 0);
246 x0 = MIN ((width - source_width) / 2, dx);
247 y0 = MIN ((height - source_height) / 2, dy);
248
249 gdk_pixbuf_composite (loader->pixbuf, target, dx, dy,
250 MIN (width, source_width),
251 MIN (height, source_height),
252 x0, y0, 1.0, 1.0,
253 GDK_INTERP_BILINEAR, 255);
254 }
255
256
257
258 static void
terminal_image_loader_scale(TerminalImageLoader * loader,GdkPixbuf * target,gint width,gint height)259 terminal_image_loader_scale (TerminalImageLoader *loader,
260 GdkPixbuf *target,
261 gint width,
262 gint height)
263 {
264 gdouble xscale;
265 gdouble yscale;
266 guint32 rgba;
267 gint source_width;
268 gint source_height;
269 gint x;
270 gint y;
271
272 /* fill with background color */
273 rgba = ((((guint)(loader->bgcolor.red * 65535) & 0xff00) << 8)
274 | (((guint)(loader->bgcolor.green * 65535) & 0xff00))
275 | (((guint)(loader->bgcolor.blue * 65535) & 0xff00) >> 8)) << 8;
276 gdk_pixbuf_fill (target, rgba);
277
278 source_width = gdk_pixbuf_get_width (loader->pixbuf);
279 source_height = gdk_pixbuf_get_height (loader->pixbuf);
280
281 xscale = (gdouble) width / source_width;
282 yscale = (gdouble) height / source_height;
283
284 if (xscale < yscale)
285 {
286 yscale = xscale;
287 x = 0;
288 y = (height - (source_height * yscale)) / 2;
289 }
290 else
291 {
292 xscale = yscale;
293 x = (width - (source_width * xscale)) / 2;
294 y = 0;
295 }
296
297 gdk_pixbuf_composite (loader->pixbuf, target, x, y,
298 source_width * xscale,
299 source_height * yscale,
300 x, y, xscale, yscale,
301 GDK_INTERP_BILINEAR, 255);
302 }
303
304
305
306 static void
terminal_image_loader_stretch(TerminalImageLoader * loader,GdkPixbuf * target,gint width,gint height)307 terminal_image_loader_stretch (TerminalImageLoader *loader,
308 GdkPixbuf *target,
309 gint width,
310 gint height)
311 {
312 gdouble xscale;
313 gdouble yscale;
314 gint source_width;
315 gint source_height;
316
317 source_width = gdk_pixbuf_get_width (loader->pixbuf);
318 source_height = gdk_pixbuf_get_height (loader->pixbuf);
319
320 xscale = (gdouble) width / source_width;
321 yscale = (gdouble) height / source_height;
322
323 gdk_pixbuf_composite (loader->pixbuf, target,
324 0, 0, width, height,
325 0, 0, xscale, yscale,
326 GDK_INTERP_BILINEAR, 255);
327 }
328
329
330
331 /**
332 * terminal_image_loader_get:
333 *
334 * Returns the default #TerminalImageLoader instance. The returned
335 * pointer is already ref'ed, call g_object_unref() if you don't
336 * need it any longer.
337 *
338 * Return value : The default #TerminalImageLoader instance.
339 **/
340 TerminalImageLoader*
terminal_image_loader_get(void)341 terminal_image_loader_get (void)
342 {
343 static TerminalImageLoader *loader = NULL;
344
345 if (G_UNLIKELY (loader == NULL))
346 {
347 loader = g_object_new (TERMINAL_TYPE_IMAGE_LOADER, NULL);
348 g_object_add_weak_pointer (G_OBJECT (loader), (gpointer) &loader);
349 }
350 else
351 {
352 g_object_ref (G_OBJECT (loader));
353 }
354
355 return loader;
356 }
357
358
359
360 /**
361 * terminal_image_loader_load:
362 * @loader : A #TerminalImageLoader.
363 * @width : The image width.
364 * @height : The image height.
365 *
366 * Return value : The image in the given @width and @height drawn with
367 * the configured style or %NULL on error.
368 **/
369 GdkPixbuf*
terminal_image_loader_load(TerminalImageLoader * loader,gint width,gint height)370 terminal_image_loader_load (TerminalImageLoader *loader,
371 gint width,
372 gint height)
373 {
374 GdkPixbuf *pixbuf;
375 GSList *lp;
376
377 terminal_return_val_if_fail (TERMINAL_IS_IMAGE_LOADER (loader), NULL);
378 terminal_return_val_if_fail (width > 0, NULL);
379 terminal_return_val_if_fail (height > 0, NULL);
380
381 terminal_image_loader_check (loader);
382
383 if (G_UNLIKELY (loader->pixbuf == NULL || width <= 1 || height <= 1))
384 return NULL;
385
386 #ifdef G_ENABLE_DEBUG
387 g_debug ("Image Loader Memory Status: %d images in valid "
388 "cache, %d in invalid cache",
389 g_slist_length (loader->cache),
390 g_slist_length (loader->cache_invalid));
391 #endif
392
393 /* check for a cached version */
394 for (lp = loader->cache; lp != NULL; lp = lp->next)
395 {
396 gint w, h;
397 pixbuf = GDK_PIXBUF (lp->data);
398 w = gdk_pixbuf_get_width (pixbuf);
399 h = gdk_pixbuf_get_height (pixbuf);
400
401 if ((w == width && h == height) ||
402 (w >= width && h >= height && loader->style == TERMINAL_BACKGROUND_STYLE_TILED))
403 {
404 return GDK_PIXBUF (g_object_ref (G_OBJECT (pixbuf)));
405 }
406 }
407
408 pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (loader->pixbuf),
409 gdk_pixbuf_get_has_alpha (loader->pixbuf),
410 gdk_pixbuf_get_bits_per_sample (loader->pixbuf),
411 width, height);
412
413 switch (loader->style)
414 {
415 case TERMINAL_BACKGROUND_STYLE_TILED:
416 terminal_image_loader_tile (loader, pixbuf, width, height);
417 break;
418
419 case TERMINAL_BACKGROUND_STYLE_CENTERED:
420 terminal_image_loader_center (loader, pixbuf, width, height);
421 break;
422
423 case TERMINAL_BACKGROUND_STYLE_SCALED:
424 terminal_image_loader_scale (loader, pixbuf, width, height);
425 break;
426
427 case TERMINAL_BACKGROUND_STYLE_STRETCHED:
428 terminal_image_loader_stretch (loader, pixbuf, width, height);
429 break;
430
431 default:
432 terminal_assert_not_reached ();
433 }
434
435 loader->cache = g_slist_prepend (loader->cache, pixbuf);
436
437 return GDK_PIXBUF (g_object_ref (G_OBJECT (pixbuf)));
438 }
439
440
441