1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
2
3 gnomebg.c: Object for the desktop background.
4
5 Copyright (C) 2000 Eazel, Inc.
6 Copyright (C) 2007-2008 Red Hat, Inc.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 This program 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 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the
20 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22
23 Derived from eel-background.c and eel-gdk-pixbuf-extensions.c by
24 Darin Adler <darin@eazel.com> and Ramiro Estrugo <ramiro@eazel.com>
25
26 Author: Soren Sandmann <sandmann@redhat.com>
27
28 */
29
30 #include <string.h>
31 #include <math.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34
35 #include <glib/gstdio.h>
36 #include <gio/gio.h>
37
38 #include <cairo.h>
39
40 #define GNOME_DESKTOP_USE_UNSTABLE_API
41 #include "gnome-bg.h"
42 #include "gnome-bg-slide-show.h"
43 #include "gnome-bg-crossfade.h"
44
45 #define BG_KEY_PRIMARY_COLOR "primary-color"
46 #define BG_KEY_SECONDARY_COLOR "secondary-color"
47 #define BG_KEY_COLOR_TYPE "color-shading-type"
48 #define BG_KEY_PICTURE_PLACEMENT "picture-options"
49 #define BG_KEY_PICTURE_OPACITY "picture-opacity"
50 #define BG_KEY_PICTURE_URI "picture-uri"
51
52 /* We keep the large pixbufs around if the next update
53 in the slideshow is less than 60 seconds away */
54 #define KEEP_EXPENSIVE_CACHE_SECS 60
55
56 /* This is the size of the GdkRGB dither matrix, in order to avoid
57 * bad dithering when tiling the gradient
58 */
59 #define GRADIENT_PIXMAP_TILE_SIZE 128
60 #define THUMBNAIL_SIZE 256
61
62 typedef struct FileCacheEntry FileCacheEntry;
63 #define CACHE_SIZE 4
64
65 /*
66 * Implementation of the GnomeBG class
67 */
68 struct _GnomeBG
69 {
70 GObject parent_instance;
71 char * filename;
72 GDesktopBackgroundStyle placement;
73 GDesktopBackgroundShading color_type;
74 GdkRGBA primary;
75 GdkRGBA secondary;
76
77 GFileMonitor * file_monitor;
78
79 guint changed_id;
80 guint transitioned_id;
81 guint blow_caches_id;
82
83 /* Cached information, only access through cache accessor functions */
84 GnomeBGSlideShow * slideshow;
85 time_t file_mtime;
86 GdkPixbuf * pixbuf_cache;
87 int timeout_id;
88
89 GList * file_cache;
90 };
91
92 struct _GnomeBGClass
93 {
94 GObjectClass parent_class;
95 };
96
97 enum {
98 CHANGED,
99 TRANSITIONED,
100 N_SIGNALS
101 };
102
103 static guint signals[N_SIGNALS] = { 0 };
104
105 G_DEFINE_TYPE (GnomeBG, gnome_bg, G_TYPE_OBJECT)
106
107 /* Pixbuf utils */
108 static void pixbuf_average_value (GdkPixbuf *pixbuf,
109 GdkRGBA *result);
110 static GdkPixbuf *pixbuf_scale_to_fit (GdkPixbuf *src,
111 int max_width,
112 int max_height);
113 static GdkPixbuf *pixbuf_scale_to_min (GdkPixbuf *src,
114 int min_width,
115 int min_height);
116 static void pixbuf_draw_gradient (GdkPixbuf *pixbuf,
117 gboolean horizontal,
118 GdkRGBA *c1,
119 GdkRGBA *c2,
120 GdkRectangle *rect);
121 static void pixbuf_tile (GdkPixbuf *src,
122 GdkPixbuf *dest);
123 static void pixbuf_blend (GdkPixbuf *src,
124 GdkPixbuf *dest,
125 int src_x,
126 int src_y,
127 int width,
128 int height,
129 int dest_x,
130 int dest_y,
131 double alpha);
132
133 /* Thumbnail utilities */
134 static GdkPixbuf *create_thumbnail_for_filename (GnomeDesktopThumbnailFactory *factory,
135 const char *filename);
136 static gboolean get_thumb_annotations (GdkPixbuf *thumb,
137 int *orig_width,
138 int *orig_height);
139
140 /* Cache */
141 static GdkPixbuf *get_pixbuf_for_size (GnomeBG *bg,
142 gint num_monitor,
143 int width,
144 int height);
145 static void clear_cache (GnomeBG *bg);
146 static gboolean is_different (GnomeBG *bg,
147 const char *filename);
148 static time_t get_mtime (const char *filename);
149 static GdkPixbuf *create_img_thumbnail (GnomeBG *bg,
150 GnomeDesktopThumbnailFactory *factory,
151 GdkScreen *screen,
152 int dest_width,
153 int dest_height,
154 int frame_num);
155 static GnomeBGSlideShow * get_as_slideshow (GnomeBG *bg,
156 const char *filename);
157 static GnomeBGSlideShow *read_slideshow_file (const char *filename,
158 GError **err);
159
160 static void
color_from_string(const char * string,GdkRGBA * colorp)161 color_from_string (const char *string,
162 GdkRGBA *colorp)
163 {
164 /* If all else fails use black */
165 gdk_rgba_parse (colorp, "black");
166
167 if (!string)
168 return;
169
170 gdk_rgba_parse (colorp, string);
171 }
172
173 static char *
color_to_string(const GdkRGBA * color)174 color_to_string (const GdkRGBA *color)
175 {
176 return g_strdup_printf ("#%02x%02x%02x",
177 (int) (0.5 + color->red * 255),
178 (int) (0.5 + color->green * 255),
179 (int) (0.5 + color->blue * 255));
180 }
181
182 static gboolean
do_changed(GnomeBG * bg)183 do_changed (GnomeBG *bg)
184 {
185 gboolean ignore_pending_change;
186 bg->changed_id = 0;
187
188 ignore_pending_change =
189 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (bg),
190 "ignore-pending-change"));
191
192 if (!ignore_pending_change) {
193 g_signal_emit (G_OBJECT (bg), signals[CHANGED], 0);
194 }
195
196 return FALSE;
197 }
198
199 static void
queue_changed(GnomeBG * bg)200 queue_changed (GnomeBG *bg)
201 {
202 if (bg->changed_id > 0) {
203 g_source_remove (bg->changed_id);
204 }
205
206 /* We unset this here to allow apps to set it if they don't want
207 to get the change event. This is used by nautilus when it
208 gets the pixmap from the bg (due to a reason other than the changed
209 event). Because if there is no other change after this time the
210 pending changed event will just uselessly cause us to recreate
211 the pixmap. */
212 g_object_set_data (G_OBJECT (bg), "ignore-pending-change",
213 GINT_TO_POINTER (FALSE));
214 bg->changed_id = g_timeout_add_full (G_PRIORITY_LOW,
215 100,
216 (GSourceFunc)do_changed,
217 bg,
218 NULL);
219 }
220
221 static gboolean
do_transitioned(GnomeBG * bg)222 do_transitioned (GnomeBG *bg)
223 {
224 bg->transitioned_id = 0;
225
226 if (bg->pixbuf_cache) {
227 g_object_unref (bg->pixbuf_cache);
228 bg->pixbuf_cache = NULL;
229 }
230
231 g_signal_emit (G_OBJECT (bg), signals[TRANSITIONED], 0);
232
233 return FALSE;
234 }
235
236 static void
queue_transitioned(GnomeBG * bg)237 queue_transitioned (GnomeBG *bg)
238 {
239 if (bg->transitioned_id > 0) {
240 g_source_remove (bg->transitioned_id);
241 }
242
243 bg->transitioned_id = g_timeout_add_full (G_PRIORITY_LOW,
244 100,
245 (GSourceFunc)do_transitioned,
246 bg,
247 NULL);
248 }
249
250 static gboolean
bg_gsettings_mapping(GVariant * value,gpointer * result,gpointer user_data)251 bg_gsettings_mapping (GVariant *value,
252 gpointer *result,
253 gpointer user_data)
254 {
255 const gchar *bg_key_value;
256 char *filename = NULL;
257
258 /* The final fallback if nothing matches is with a NULL value. */
259 if (value == NULL) {
260 *result = NULL;
261 return TRUE;
262 }
263
264 bg_key_value = g_variant_get_string (value, NULL);
265
266 if (bg_key_value && *bg_key_value != '\0') {
267 filename = g_filename_from_uri (bg_key_value, NULL, NULL);
268
269 if (filename != NULL && g_file_test (filename, G_FILE_TEST_EXISTS) == FALSE) {
270 g_free (filename);
271 return FALSE;
272 }
273
274 if (filename != NULL) {
275 *result = filename;
276 return TRUE;
277 }
278 }
279
280 return FALSE;
281 }
282
283 void
gnome_bg_load_from_preferences(GnomeBG * bg,GSettings * settings)284 gnome_bg_load_from_preferences (GnomeBG *bg,
285 GSettings *settings)
286 {
287 char *tmp;
288 char *filename;
289 GDesktopBackgroundShading ctype;
290 GdkRGBA c1, c2;
291 GDesktopBackgroundStyle placement;
292
293 g_return_if_fail (GNOME_IS_BG (bg));
294 g_return_if_fail (G_IS_SETTINGS (settings));
295
296 /* Filename */
297 filename = g_settings_get_mapped (settings, BG_KEY_PICTURE_URI, bg_gsettings_mapping, NULL);
298
299 /* Colors */
300 tmp = g_settings_get_string (settings, BG_KEY_PRIMARY_COLOR);
301 color_from_string (tmp, &c1);
302 g_free (tmp);
303
304 tmp = g_settings_get_string (settings, BG_KEY_SECONDARY_COLOR);
305 color_from_string (tmp, &c2);
306 g_free (tmp);
307
308 /* Color type */
309 ctype = g_settings_get_enum (settings, BG_KEY_COLOR_TYPE);
310
311 /* Placement */
312 placement = g_settings_get_enum (settings, BG_KEY_PICTURE_PLACEMENT);
313
314 gnome_bg_set_rgba (bg, ctype, &c1, &c2);
315 gnome_bg_set_placement (bg, placement);
316 gnome_bg_set_filename (bg, filename);
317
318 g_free (filename);
319 }
320
321 void
gnome_bg_save_to_preferences(GnomeBG * bg,GSettings * settings)322 gnome_bg_save_to_preferences (GnomeBG *bg,
323 GSettings *settings)
324 {
325 gchar *primary;
326 gchar *secondary;
327 gchar *uri;
328
329 g_return_if_fail (GNOME_IS_BG (bg));
330 g_return_if_fail (G_IS_SETTINGS (settings));
331
332 primary = color_to_string (&bg->primary);
333 secondary = color_to_string (&bg->secondary);
334
335 g_settings_delay (settings);
336
337 uri = NULL;
338 if (bg->filename != NULL)
339 uri = g_filename_to_uri (bg->filename, NULL, NULL);
340 if (uri == NULL)
341 uri = g_strdup ("");
342 g_settings_set_string (settings, BG_KEY_PICTURE_URI, uri);
343 g_settings_set_string (settings, BG_KEY_PRIMARY_COLOR, primary);
344 g_settings_set_string (settings, BG_KEY_SECONDARY_COLOR, secondary);
345 g_settings_set_enum (settings, BG_KEY_COLOR_TYPE, bg->color_type);
346 g_settings_set_enum (settings, BG_KEY_PICTURE_PLACEMENT, bg->placement);
347
348 /* Apply changes atomically. */
349 g_settings_apply (settings);
350
351 g_free (primary);
352 g_free (secondary);
353 g_free (uri);
354 }
355
356
357 static void
gnome_bg_init(GnomeBG * bg)358 gnome_bg_init (GnomeBG *bg)
359 {
360 }
361
362 static void
gnome_bg_dispose(GObject * object)363 gnome_bg_dispose (GObject *object)
364 {
365 GnomeBG *bg = GNOME_BG (object);
366
367 if (bg->file_monitor) {
368 g_object_unref (bg->file_monitor);
369 bg->file_monitor = NULL;
370 }
371
372 clear_cache (bg);
373
374 G_OBJECT_CLASS (gnome_bg_parent_class)->dispose (object);
375 }
376
377 static void
gnome_bg_finalize(GObject * object)378 gnome_bg_finalize (GObject *object)
379 {
380 GnomeBG *bg = GNOME_BG (object);
381
382 if (bg->changed_id != 0) {
383 g_source_remove (bg->changed_id);
384 bg->changed_id = 0;
385 }
386
387 if (bg->transitioned_id != 0) {
388 g_source_remove (bg->transitioned_id);
389 bg->transitioned_id = 0;
390 }
391
392 if (bg->blow_caches_id != 0) {
393 g_source_remove (bg->blow_caches_id);
394 bg->blow_caches_id = 0;
395 }
396
397 g_free (bg->filename);
398 bg->filename = NULL;
399
400 G_OBJECT_CLASS (gnome_bg_parent_class)->finalize (object);
401 }
402
403 static void
gnome_bg_class_init(GnomeBGClass * klass)404 gnome_bg_class_init (GnomeBGClass *klass)
405 {
406 GObjectClass *object_class = G_OBJECT_CLASS (klass);
407
408 object_class->dispose = gnome_bg_dispose;
409 object_class->finalize = gnome_bg_finalize;
410
411 signals[CHANGED] = g_signal_new ("changed",
412 G_OBJECT_CLASS_TYPE (object_class),
413 G_SIGNAL_RUN_LAST,
414 0,
415 NULL, NULL,
416 g_cclosure_marshal_VOID__VOID,
417 G_TYPE_NONE, 0);
418
419 signals[TRANSITIONED] = g_signal_new ("transitioned",
420 G_OBJECT_CLASS_TYPE (object_class),
421 G_SIGNAL_RUN_LAST,
422 0,
423 NULL, NULL,
424 g_cclosure_marshal_VOID__VOID,
425 G_TYPE_NONE, 0);
426 }
427
428 GnomeBG *
gnome_bg_new(void)429 gnome_bg_new (void)
430 {
431 return g_object_new (GNOME_TYPE_BG, NULL);
432 }
433
434 void
gnome_bg_set_rgba(GnomeBG * bg,GDesktopBackgroundShading type,GdkRGBA * primary,GdkRGBA * secondary)435 gnome_bg_set_rgba (GnomeBG *bg,
436 GDesktopBackgroundShading type,
437 GdkRGBA *primary,
438 GdkRGBA *secondary)
439 {
440 g_return_if_fail (bg != NULL);
441 g_return_if_fail (primary != NULL);
442
443 if (bg->color_type != type ||
444 !gdk_rgba_equal (&bg->primary, primary) ||
445 (secondary && !gdk_rgba_equal (&bg->secondary, secondary))) {
446
447 bg->color_type = type;
448 bg->primary = *primary;
449 if (secondary) {
450 bg->secondary = *secondary;
451 }
452
453 queue_changed (bg);
454 }
455 }
456
457 void
gnome_bg_set_placement(GnomeBG * bg,GDesktopBackgroundStyle placement)458 gnome_bg_set_placement (GnomeBG *bg,
459 GDesktopBackgroundStyle placement)
460 {
461 g_return_if_fail (bg != NULL);
462
463 if (bg->placement != placement) {
464 bg->placement = placement;
465
466 queue_changed (bg);
467 }
468 }
469
470 GDesktopBackgroundStyle
gnome_bg_get_placement(GnomeBG * bg)471 gnome_bg_get_placement (GnomeBG *bg)
472 {
473 g_return_val_if_fail (bg != NULL, -1);
474
475 return bg->placement;
476 }
477
478 void
gnome_bg_get_rgba(GnomeBG * bg,GDesktopBackgroundShading * type,GdkRGBA * primary,GdkRGBA * secondary)479 gnome_bg_get_rgba (GnomeBG *bg,
480 GDesktopBackgroundShading *type,
481 GdkRGBA *primary,
482 GdkRGBA *secondary)
483 {
484 g_return_if_fail (bg != NULL);
485
486 if (type)
487 *type = bg->color_type;
488
489 if (primary)
490 *primary = bg->primary;
491
492 if (secondary)
493 *secondary = bg->secondary;
494 }
495
496 const gchar *
gnome_bg_get_filename(GnomeBG * bg)497 gnome_bg_get_filename (GnomeBG *bg)
498 {
499 g_return_val_if_fail (bg != NULL, NULL);
500
501 return bg->filename;
502 }
503
504 static inline gchar *
get_wallpaper_cache_dir(void)505 get_wallpaper_cache_dir (void)
506 {
507 return g_build_filename (g_get_user_cache_dir(), "wallpaper", NULL);
508 }
509
510 static inline gchar *
get_wallpaper_cache_prefix_name(gint num_monitor,GDesktopBackgroundStyle placement,gint width,gint height)511 get_wallpaper_cache_prefix_name (gint num_monitor,
512 GDesktopBackgroundStyle placement,
513 gint width,
514 gint height)
515 {
516 return g_strdup_printf ("%i_%i_%i_%i", num_monitor, (gint) placement, width, height);
517 }
518
519 static char *
get_wallpaper_cache_filename(const char * filename,gint num_monitor,GDesktopBackgroundStyle placement,gint width,gint height)520 get_wallpaper_cache_filename (const char *filename,
521 gint num_monitor,
522 GDesktopBackgroundStyle placement,
523 gint width,
524 gint height)
525 {
526 gchar *cache_filename;
527 gchar *cache_prefix_name;
528 gchar *md5_filename;
529 gchar *cache_basename;
530 gchar *cache_dir;
531
532 md5_filename = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) filename, strlen (filename));
533 cache_prefix_name = get_wallpaper_cache_prefix_name (num_monitor, placement, width, height);
534 cache_basename = g_strdup_printf ("%s_%s", cache_prefix_name, md5_filename);
535 cache_dir = get_wallpaper_cache_dir ();
536 cache_filename = g_build_filename (cache_dir, cache_basename, NULL);
537
538 g_free (cache_prefix_name);
539 g_free (md5_filename);
540 g_free (cache_basename);
541 g_free (cache_dir);
542
543 return cache_filename;
544 }
545
546 static void
cleanup_cache_for_monitor(gchar * cache_dir,gint num_monitor)547 cleanup_cache_for_monitor (gchar *cache_dir,
548 gint num_monitor)
549 {
550 GDir *g_cache_dir;
551 gchar *monitor_prefix;
552 const gchar *file;
553
554 g_cache_dir = g_dir_open (cache_dir, 0, NULL);
555 monitor_prefix = g_strdup_printf ("%i_", num_monitor);
556
557 file = g_dir_read_name (g_cache_dir);
558 while (file != NULL) {
559 gchar *path;
560
561 path = g_build_filename (cache_dir, file, NULL);
562 /* purge files with same monitor id */
563 if (g_str_has_prefix (file, monitor_prefix) &&
564 g_file_test (path, G_FILE_TEST_IS_REGULAR))
565 g_unlink (path);
566
567 g_free (path);
568
569 file = g_dir_read_name (g_cache_dir);
570 }
571
572 g_free (monitor_prefix);
573 g_dir_close (g_cache_dir);
574 }
575
576 static gboolean
cache_file_is_valid(const char * filename,const char * cache_filename)577 cache_file_is_valid (const char *filename,
578 const char *cache_filename)
579 {
580 time_t mtime;
581 time_t cache_mtime;
582
583 if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
584 return FALSE;
585
586 mtime = get_mtime (filename);
587 cache_mtime = get_mtime (cache_filename);
588
589 return (mtime < cache_mtime);
590 }
591
592 static void
refresh_cache_file(GnomeBG * bg,GdkPixbuf * new_pixbuf,gint num_monitor,gint width,gint height)593 refresh_cache_file (GnomeBG *bg,
594 GdkPixbuf *new_pixbuf,
595 gint num_monitor,
596 gint width,
597 gint height)
598 {
599 gchar *cache_filename;
600 gchar *cache_dir;
601 GdkPixbufFormat *format;
602 gchar *format_name;
603
604 if ((num_monitor == -1) || (width <= 300) || (height <= 300))
605 return;
606
607 cache_filename = get_wallpaper_cache_filename (bg->filename, num_monitor, bg->placement, width, height);
608 cache_dir = get_wallpaper_cache_dir ();
609
610 /* Only refresh scaled file on disk if useful (and don't cache slideshow) */
611 if (!cache_file_is_valid (bg->filename, cache_filename)) {
612 format = gdk_pixbuf_get_file_info (bg->filename, NULL, NULL);
613
614 if (format != NULL) {
615 if (!g_file_test (cache_dir, G_FILE_TEST_IS_DIR)) {
616 g_mkdir_with_parents (cache_dir, 0700);
617 } else {
618 cleanup_cache_for_monitor (cache_dir, num_monitor);
619 }
620
621 format_name = gdk_pixbuf_format_get_name (format);
622
623 if (strcmp (format_name, "jpeg") == 0)
624 gdk_pixbuf_save (new_pixbuf, cache_filename, format_name, NULL, "quality", "100", NULL);
625 else
626 gdk_pixbuf_save (new_pixbuf, cache_filename, format_name, NULL, NULL);
627
628 g_free (format_name);
629 }
630 }
631
632 g_free (cache_filename);
633 g_free (cache_dir);
634 }
635
636 static void
file_changed(GFileMonitor * file_monitor,GFile * child,GFile * other_file,GFileMonitorEvent event_type,gpointer user_data)637 file_changed (GFileMonitor *file_monitor,
638 GFile *child,
639 GFile *other_file,
640 GFileMonitorEvent event_type,
641 gpointer user_data)
642 {
643 GnomeBG *bg = GNOME_BG (user_data);
644
645 clear_cache (bg);
646 queue_changed (bg);
647 }
648
649 void
gnome_bg_set_filename(GnomeBG * bg,const char * filename)650 gnome_bg_set_filename (GnomeBG *bg,
651 const char *filename)
652 {
653 g_return_if_fail (bg != NULL);
654
655 if (is_different (bg, filename)) {
656 g_free (bg->filename);
657
658 bg->filename = g_strdup (filename);
659 bg->file_mtime = get_mtime (bg->filename);
660
661 if (bg->file_monitor) {
662 g_object_unref (bg->file_monitor);
663 bg->file_monitor = NULL;
664 }
665
666 if (bg->filename) {
667 GFile *f = g_file_new_for_path (bg->filename);
668
669 bg->file_monitor = g_file_monitor_file (f, 0, NULL, NULL);
670 g_signal_connect (bg->file_monitor, "changed",
671 G_CALLBACK (file_changed), bg);
672
673 g_object_unref (f);
674 }
675
676 clear_cache (bg);
677
678 queue_changed (bg);
679 }
680 }
681
682 static void
draw_color_area(GnomeBG * bg,GdkPixbuf * dest,GdkRectangle * rect)683 draw_color_area (GnomeBG *bg,
684 GdkPixbuf *dest,
685 GdkRectangle *rect)
686 {
687 guint32 pixel;
688 GdkRectangle extent;
689
690 extent.x = 0;
691 extent.y = 0;
692 extent.width = gdk_pixbuf_get_width (dest);
693 extent.height = gdk_pixbuf_get_height (dest);
694
695 gdk_rectangle_intersect (rect, &extent, rect);
696
697 switch (bg->color_type) {
698 case G_DESKTOP_BACKGROUND_SHADING_SOLID:
699 /* not really a big deal to ignore the area of interest */
700 pixel = ((int) (0.5 + bg->primary.red * 255) << 24) |
701 ((int) (0.5 + bg->primary.green * 255) << 16) |
702 ((int) (0.5 + bg->primary.blue * 255) << 8) |
703 (0xff);
704
705 gdk_pixbuf_fill (dest, pixel);
706 break;
707
708 case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL:
709 pixbuf_draw_gradient (dest, TRUE, &(bg->primary), &(bg->secondary), rect);
710 break;
711
712 case G_DESKTOP_BACKGROUND_SHADING_VERTICAL:
713 pixbuf_draw_gradient (dest, FALSE, &(bg->primary), &(bg->secondary), rect);
714 break;
715
716 default:
717 break;
718 }
719 }
720
721 static void
draw_color(GnomeBG * bg,GdkPixbuf * dest)722 draw_color (GnomeBG *bg,
723 GdkPixbuf *dest)
724 {
725 GdkRectangle rect;
726 rect.x = 0;
727 rect.y = 0;
728 rect.width = gdk_pixbuf_get_width (dest);
729 rect.height = gdk_pixbuf_get_height (dest);
730 draw_color_area (bg, dest, &rect);
731 }
732
733 static GdkPixbuf *
pixbuf_clip_to_fit(GdkPixbuf * src,int max_width,int max_height)734 pixbuf_clip_to_fit (GdkPixbuf *src,
735 int max_width,
736 int max_height)
737 {
738 int src_width, src_height;
739 int w, h;
740 int src_x, src_y;
741 GdkPixbuf *pixbuf;
742
743 src_width = gdk_pixbuf_get_width (src);
744 src_height = gdk_pixbuf_get_height (src);
745
746 if (src_width < max_width && src_height < max_height)
747 return g_object_ref (src);
748
749 w = MIN(src_width, max_width);
750 h = MIN(src_height, max_height);
751
752 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
753 gdk_pixbuf_get_has_alpha (src),
754 8, w, h);
755
756 src_x = (src_width - w) / 2;
757 src_y = (src_height - h) / 2;
758 gdk_pixbuf_copy_area (src,
759 src_x, src_y,
760 w, h,
761 pixbuf,
762 0, 0);
763 return pixbuf;
764 }
765
766 static GdkPixbuf *
get_scaled_pixbuf(GDesktopBackgroundStyle placement,GdkPixbuf * pixbuf,int width,int height,int * x,int * y,int * w,int * h)767 get_scaled_pixbuf (GDesktopBackgroundStyle placement,
768 GdkPixbuf *pixbuf,
769 int width, int height,
770 int *x, int *y,
771 int *w, int *h)
772 {
773 GdkPixbuf *new;
774
775 #if 0
776 g_print ("original_width: %d %d\n",
777 gdk_pixbuf_get_width (pixbuf),
778 gdk_pixbuf_get_height (pixbuf));
779 #endif
780
781 switch (placement) {
782 case G_DESKTOP_BACKGROUND_STYLE_SPANNED:
783 new = pixbuf_scale_to_fit (pixbuf, width, height);
784 break;
785 case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
786 new = pixbuf_scale_to_min (pixbuf, width, height);
787 break;
788
789 case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
790 new = gdk_pixbuf_scale_simple (pixbuf, width, height,
791 GDK_INTERP_BILINEAR);
792 break;
793
794 case G_DESKTOP_BACKGROUND_STYLE_SCALED:
795 new = pixbuf_scale_to_fit (pixbuf, width, height);
796 break;
797
798 case G_DESKTOP_BACKGROUND_STYLE_NONE:
799 /* This shouldn’t be true, but if it is, assert and
800 * fall through, in case assertions are disabled.
801 */
802 g_assert_not_reached ();
803 case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
804 case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
805 default:
806 new = pixbuf_clip_to_fit (pixbuf, width, height);
807 break;
808 }
809
810 *w = gdk_pixbuf_get_width (new);
811 *h = gdk_pixbuf_get_height (new);
812 *x = (width - *w) / 2;
813 *y = (height - *h) / 2;
814
815 return new;
816 }
817
818 static void
draw_image_area(GnomeBG * bg,gint num_monitor,GdkPixbuf * pixbuf,GdkPixbuf * dest,GdkRectangle * area)819 draw_image_area (GnomeBG *bg,
820 gint num_monitor,
821 GdkPixbuf *pixbuf,
822 GdkPixbuf *dest,
823 GdkRectangle *area)
824 {
825 int dest_width = area->width;
826 int dest_height = area->height;
827 int x, y, w, h;
828 GdkPixbuf *scaled;
829
830 if (!pixbuf)
831 return;
832
833 scaled = get_scaled_pixbuf (bg->placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
834
835 switch (bg->placement) {
836 case G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
837 pixbuf_tile (scaled, dest);
838 break;
839 case G_DESKTOP_BACKGROUND_STYLE_ZOOM:
840 case G_DESKTOP_BACKGROUND_STYLE_CENTERED:
841 case G_DESKTOP_BACKGROUND_STYLE_STRETCHED:
842 case G_DESKTOP_BACKGROUND_STYLE_SCALED:
843 pixbuf_blend (scaled, dest, 0, 0, w, h, x + area->x, y + area->y, 1.0);
844 break;
845 case G_DESKTOP_BACKGROUND_STYLE_SPANNED:
846 pixbuf_blend (scaled, dest, 0, 0, w, h, x, y, 1.0);
847 break;
848 case G_DESKTOP_BACKGROUND_STYLE_NONE:
849 default:
850 g_assert_not_reached ();
851 break;
852 }
853
854 refresh_cache_file (bg, scaled, num_monitor, dest_width, dest_height);
855
856 g_object_unref (scaled);
857 }
858
859 static void
draw_image_for_thumb(GnomeBG * bg,GdkPixbuf * pixbuf,GdkPixbuf * dest)860 draw_image_for_thumb (GnomeBG *bg,
861 GdkPixbuf *pixbuf,
862 GdkPixbuf *dest)
863 {
864 GdkRectangle rect;
865
866 rect.x = 0;
867 rect.y = 0;
868 rect.width = gdk_pixbuf_get_width (dest);
869 rect.height = gdk_pixbuf_get_height (dest);
870
871 draw_image_area (bg, -1, pixbuf, dest, &rect);
872 }
873
874 static void
draw_once(GnomeBG * bg,GdkPixbuf * dest)875 draw_once (GnomeBG *bg,
876 GdkPixbuf *dest)
877 {
878 GdkRectangle rect;
879 GdkPixbuf *pixbuf;
880 gint num_monitor;
881
882 /* we just draw on the whole screen */
883 num_monitor = 0;
884
885 rect.x = 0;
886 rect.y = 0;
887 rect.width = gdk_pixbuf_get_width (dest);
888 rect.height = gdk_pixbuf_get_height (dest);
889
890 pixbuf = get_pixbuf_for_size (bg, num_monitor, rect.width, rect.height);
891 if (pixbuf) {
892 GdkPixbuf *rotated;
893
894 rotated = gdk_pixbuf_apply_embedded_orientation (pixbuf);
895 if (rotated != NULL) {
896 g_object_unref (pixbuf);
897 pixbuf = rotated;
898 }
899
900 draw_image_area (bg,
901 num_monitor,
902 pixbuf,
903 dest,
904 &rect);
905 g_object_unref (pixbuf);
906 }
907 }
908
909 void
gnome_bg_draw(GnomeBG * bg,GdkPixbuf * dest)910 gnome_bg_draw (GnomeBG *bg,
911 GdkPixbuf *dest)
912 {
913 draw_color (bg, dest);
914 if (bg->placement != G_DESKTOP_BACKGROUND_STYLE_NONE) {
915 draw_once (bg, dest);
916 }
917 }
918
919 gboolean
gnome_bg_has_multiple_sizes(GnomeBG * bg)920 gnome_bg_has_multiple_sizes (GnomeBG *bg)
921 {
922 GnomeBGSlideShow *show;
923 gboolean ret;
924
925 g_return_val_if_fail (bg != NULL, FALSE);
926
927 ret = FALSE;
928
929 show = get_as_slideshow (bg, bg->filename);
930 if (show) {
931 ret = gnome_bg_slide_show_get_has_multiple_sizes (show);
932 g_object_unref (show);
933 }
934
935 return ret;
936 }
937
938 static void
gnome_bg_get_pixmap_size(GnomeBG * bg,int width,int height,int * pixmap_width,int * pixmap_height)939 gnome_bg_get_pixmap_size (GnomeBG *bg,
940 int width,
941 int height,
942 int *pixmap_width,
943 int *pixmap_height)
944 {
945 int dummy;
946
947 if (!pixmap_width)
948 pixmap_width = &dummy;
949 if (!pixmap_height)
950 pixmap_height = &dummy;
951
952 *pixmap_width = width;
953 *pixmap_height = height;
954
955 if (!bg->filename) {
956 switch (bg->color_type) {
957 case G_DESKTOP_BACKGROUND_SHADING_SOLID:
958 *pixmap_width = 1;
959 *pixmap_height = 1;
960 break;
961
962 case G_DESKTOP_BACKGROUND_SHADING_HORIZONTAL:
963 case G_DESKTOP_BACKGROUND_SHADING_VERTICAL:
964 default:
965 break;
966 }
967
968 return;
969 }
970 }
971
972 /**
973 * gnome_bg_create_surface:
974 * @bg: GnomeBG
975 * @window:
976 * @width:
977 * @height:
978 *
979 * Create a surface that can be set as background for @window.
980 *
981 * Returns: %NULL on error (e.g. out of X connections)
982 **/
983 cairo_surface_t *
gnome_bg_create_surface(GnomeBG * bg,GdkWindow * window,int width,int height)984 gnome_bg_create_surface (GnomeBG *bg,
985 GdkWindow *window,
986 int width,
987 int height)
988 {
989 gint scale;
990 int pm_width, pm_height;
991 cairo_surface_t *surface;
992 cairo_t *cr;
993
994 g_return_val_if_fail (bg != NULL, NULL);
995 g_return_val_if_fail (window != NULL, NULL);
996
997 scale = gdk_window_get_scale_factor (window);
998
999 if (bg->pixbuf_cache &&
1000 gdk_pixbuf_get_width (bg->pixbuf_cache) != width &&
1001 gdk_pixbuf_get_height (bg->pixbuf_cache) != height) {
1002 g_object_unref (bg->pixbuf_cache);
1003 bg->pixbuf_cache = NULL;
1004 }
1005
1006 /* has the side effect of loading and caching pixbuf only when in tile mode */
1007 gnome_bg_get_pixmap_size (bg, width, height, &pm_width, &pm_height);
1008 surface = gdk_window_create_similar_surface (window,
1009 CAIRO_CONTENT_COLOR,
1010 pm_width, pm_height);
1011
1012 if (surface == NULL)
1013 return NULL;
1014
1015 cr = cairo_create (surface);
1016 if (!bg->filename && bg->color_type == G_DESKTOP_BACKGROUND_SHADING_SOLID) {
1017 gdk_cairo_set_source_rgba (cr, &(bg->primary));
1018 }
1019 else {
1020 GdkPixbuf *pixbuf;
1021 cairo_surface_t *pixbuf_surface;
1022
1023 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
1024 scale * width, scale * height);
1025 gnome_bg_draw (bg, pixbuf);
1026
1027 pixbuf_surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, 0, window);
1028 cairo_set_source_surface (cr, pixbuf_surface, 0, 0);
1029
1030 cairo_surface_destroy (pixbuf_surface);
1031 g_object_unref (pixbuf);
1032 }
1033
1034 cairo_paint (cr);
1035
1036 cairo_destroy (cr);
1037
1038 return surface;
1039 }
1040
1041
1042 /* determine if a background is darker or lighter than average, to help
1043 * clients know what colors to draw on top with
1044 */
1045 gboolean
gnome_bg_is_dark(GnomeBG * bg,int width,int height)1046 gnome_bg_is_dark (GnomeBG *bg,
1047 int width,
1048 int height)
1049 {
1050 GdkRGBA color;
1051 gdouble intensity;
1052 GdkPixbuf *pixbuf;
1053
1054 g_return_val_if_fail (bg != NULL, FALSE);
1055
1056 if (bg->color_type == G_DESKTOP_BACKGROUND_SHADING_SOLID) {
1057 color = bg->primary;
1058 } else {
1059 color.red = (bg->primary.red + bg->secondary.red) / 2;
1060 color.green = (bg->primary.green + bg->secondary.green) / 2;
1061 color.blue = (bg->primary.blue + bg->secondary.blue) / 2;
1062 }
1063 pixbuf = get_pixbuf_for_size (bg, -1, width, height);
1064 if (pixbuf) {
1065 GdkRGBA average;
1066
1067 pixbuf_average_value (pixbuf, &average);
1068
1069 color.red = color.red * (1.0 - average.alpha) + average.red * average.alpha;
1070 color.green = color.green * (1.0 - average.alpha) + average.green * average.alpha;
1071 color.blue = color.blue * (1.0 - average.alpha) + average.blue * average.alpha;
1072 g_object_unref (pixbuf);
1073 }
1074
1075 intensity = color.red * 77 +
1076 color.green * 150 +
1077 color.blue * 28;
1078
1079 return intensity < 160; /* biased slightly to be dark */
1080 }
1081
1082 static gboolean
get_original_size(const char * filename,int * orig_width,int * orig_height)1083 get_original_size (const char *filename,
1084 int *orig_width,
1085 int *orig_height)
1086 {
1087 gboolean result;
1088
1089 if (gdk_pixbuf_get_file_info (filename, orig_width, orig_height))
1090 result = TRUE;
1091 else
1092 result = FALSE;
1093
1094 return result;
1095 }
1096
1097 static const char *
get_filename_for_size(GnomeBG * bg,gint best_width,gint best_height)1098 get_filename_for_size (GnomeBG *bg, gint best_width, gint best_height)
1099 {
1100 GnomeBGSlideShow *show;
1101 const char *file = NULL;
1102
1103 if (!bg->filename)
1104 return NULL;
1105
1106 show = get_as_slideshow (bg, bg->filename);
1107 if (!show) {
1108 return bg->filename;
1109 }
1110
1111 gnome_bg_slide_show_get_current_slide (show, best_width, best_height, NULL, NULL, NULL, &file, NULL);
1112 g_object_unref (show);
1113
1114 return file;
1115 }
1116
1117 gboolean
gnome_bg_get_image_size(GnomeBG * bg,GnomeDesktopThumbnailFactory * factory,int best_width,int best_height,int * width,int * height)1118 gnome_bg_get_image_size (GnomeBG *bg,
1119 GnomeDesktopThumbnailFactory *factory,
1120 int best_width,
1121 int best_height,
1122 int *width,
1123 int *height)
1124 {
1125 GdkPixbuf *thumb;
1126 gboolean result = FALSE;
1127 const gchar *filename;
1128
1129 g_return_val_if_fail (bg != NULL, FALSE);
1130 g_return_val_if_fail (factory != NULL, FALSE);
1131
1132 if (!bg->filename)
1133 return FALSE;
1134
1135 filename = get_filename_for_size (bg, best_width, best_height);
1136 thumb = create_thumbnail_for_filename (factory, filename);
1137 if (thumb) {
1138 if (get_thumb_annotations (thumb, width, height))
1139 result = TRUE;
1140
1141 g_object_unref (thumb);
1142 }
1143
1144 if (!result) {
1145 if (get_original_size (filename, width, height))
1146 result = TRUE;
1147 }
1148
1149 return result;
1150 }
1151
1152 static double
fit_factor(int from_width,int from_height,int to_width,int to_height)1153 fit_factor (int from_width, int from_height,
1154 int to_width, int to_height)
1155 {
1156 return MIN (to_width / (double) from_width, to_height / (double) from_height);
1157 }
1158
1159 /**
1160 * gnome_bg_create_thumbnail:
1161 *
1162 * Returns: (transfer full): a #GdkPixbuf showing the background as a thumbnail
1163 */
1164 GdkPixbuf *
gnome_bg_create_thumbnail(GnomeBG * bg,GnomeDesktopThumbnailFactory * factory,GdkScreen * screen,int dest_width,int dest_height)1165 gnome_bg_create_thumbnail (GnomeBG *bg,
1166 GnomeDesktopThumbnailFactory *factory,
1167 GdkScreen *screen,
1168 int dest_width,
1169 int dest_height)
1170 {
1171 GdkPixbuf *result;
1172 GdkPixbuf *thumb;
1173
1174 g_return_val_if_fail (bg != NULL, NULL);
1175
1176 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest_width, dest_height);
1177
1178 draw_color (bg, result);
1179
1180 if (bg->placement != G_DESKTOP_BACKGROUND_STYLE_NONE) {
1181 thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, -1);
1182
1183 if (thumb) {
1184 draw_image_for_thumb (bg, thumb, result);
1185 g_object_unref (thumb);
1186 }
1187 }
1188
1189 return result;
1190 }
1191
1192 /* Implementation of the pixbuf cache */
1193 struct _SlideShow
1194 {
1195 gint ref_count;
1196 double start_time;
1197 double total_duration;
1198
1199 GQueue *slides;
1200
1201 gboolean has_multiple_sizes;
1202
1203 /* used during parsing */
1204 struct tm start_tm;
1205 GQueue *stack;
1206 };
1207
1208
1209 static GdkPixbuf *
blend(GdkPixbuf * p1,GdkPixbuf * p2,double alpha)1210 blend (GdkPixbuf *p1,
1211 GdkPixbuf *p2,
1212 double alpha)
1213 {
1214 GdkPixbuf *result = gdk_pixbuf_copy (p1);
1215 GdkPixbuf *tmp;
1216
1217 if (gdk_pixbuf_get_width (p2) != gdk_pixbuf_get_width (p1) ||
1218 gdk_pixbuf_get_height (p2) != gdk_pixbuf_get_height (p1)) {
1219 tmp = gdk_pixbuf_scale_simple (p2,
1220 gdk_pixbuf_get_width (p1),
1221 gdk_pixbuf_get_height (p1),
1222 GDK_INTERP_BILINEAR);
1223 }
1224 else {
1225 tmp = g_object_ref (p2);
1226 }
1227
1228 pixbuf_blend (tmp, result, 0, 0, -1, -1, 0, 0, alpha);
1229
1230 g_object_unref (tmp);
1231
1232 return result;
1233 }
1234
1235 typedef enum {
1236 PIXBUF,
1237 SLIDESHOW,
1238 THUMBNAIL
1239 } FileType;
1240
1241 struct FileCacheEntry
1242 {
1243 FileType type;
1244 char *filename;
1245 union {
1246 GdkPixbuf *pixbuf;
1247 GnomeBGSlideShow *slideshow;
1248 GdkPixbuf *thumbnail;
1249 } u;
1250 };
1251
1252 static void
file_cache_entry_delete(FileCacheEntry * ent)1253 file_cache_entry_delete (FileCacheEntry *ent)
1254 {
1255 g_free (ent->filename);
1256
1257 switch (ent->type) {
1258 case PIXBUF:
1259 g_object_unref (ent->u.pixbuf);
1260 break;
1261 case SLIDESHOW:
1262 g_object_unref (ent->u.slideshow);
1263 break;
1264 case THUMBNAIL:
1265 g_object_unref (ent->u.thumbnail);
1266 break;
1267 default:
1268 break;
1269 }
1270
1271 g_free (ent);
1272 }
1273
1274 static void
bound_cache(GnomeBG * bg)1275 bound_cache (GnomeBG *bg)
1276 {
1277 while (g_list_length (bg->file_cache) >= CACHE_SIZE) {
1278 GList *last_link = g_list_last (bg->file_cache);
1279 FileCacheEntry *ent = last_link->data;
1280
1281 file_cache_entry_delete (ent);
1282
1283 bg->file_cache = g_list_delete_link (bg->file_cache, last_link);
1284 }
1285 }
1286
1287 static const FileCacheEntry *
file_cache_lookup(GnomeBG * bg,FileType type,const char * filename)1288 file_cache_lookup (GnomeBG *bg, FileType type, const char *filename)
1289 {
1290 GList *list;
1291
1292 for (list = bg->file_cache; list != NULL; list = list->next) {
1293 FileCacheEntry *ent = list->data;
1294
1295 if (ent && ent->type == type &&
1296 strcmp (ent->filename, filename) == 0) {
1297 return ent;
1298 }
1299 }
1300
1301 return NULL;
1302 }
1303
1304 static FileCacheEntry *
file_cache_entry_new(GnomeBG * bg,FileType type,const char * filename)1305 file_cache_entry_new (GnomeBG *bg,
1306 FileType type,
1307 const char *filename)
1308 {
1309 FileCacheEntry *ent = g_new0 (FileCacheEntry, 1);
1310
1311 g_assert (!file_cache_lookup (bg, type, filename));
1312
1313 ent->type = type;
1314 ent->filename = g_strdup (filename);
1315
1316 bg->file_cache = g_list_prepend (bg->file_cache, ent);
1317
1318 bound_cache (bg);
1319
1320 return ent;
1321 }
1322
1323 static void
file_cache_add_pixbuf(GnomeBG * bg,const char * filename,GdkPixbuf * pixbuf)1324 file_cache_add_pixbuf (GnomeBG *bg,
1325 const char *filename,
1326 GdkPixbuf *pixbuf)
1327 {
1328 FileCacheEntry *ent = file_cache_entry_new (bg, PIXBUF, filename);
1329 ent->u.pixbuf = g_object_ref (pixbuf);
1330 }
1331
1332 static void
file_cache_add_thumbnail(GnomeBG * bg,const char * filename,GdkPixbuf * pixbuf)1333 file_cache_add_thumbnail (GnomeBG *bg,
1334 const char *filename,
1335 GdkPixbuf *pixbuf)
1336 {
1337 FileCacheEntry *ent = file_cache_entry_new (bg, THUMBNAIL, filename);
1338 ent->u.thumbnail = g_object_ref (pixbuf);
1339 }
1340
1341 static void
file_cache_add_slide_show(GnomeBG * bg,const char * filename,GnomeBGSlideShow * show)1342 file_cache_add_slide_show (GnomeBG *bg,
1343 const char *filename,
1344 GnomeBGSlideShow *show)
1345 {
1346 FileCacheEntry *ent = file_cache_entry_new (bg, SLIDESHOW, filename);
1347 ent->u.slideshow = g_object_ref (show);
1348 }
1349
1350 static GdkPixbuf *
load_from_cache_file(GnomeBG * bg,const char * filename,gint num_monitor,gint best_width,gint best_height)1351 load_from_cache_file (GnomeBG *bg,
1352 const char *filename,
1353 gint num_monitor,
1354 gint best_width,
1355 gint best_height)
1356 {
1357 GdkPixbuf *pixbuf = NULL;
1358 gchar *cache_filename;
1359
1360 cache_filename = get_wallpaper_cache_filename (filename, num_monitor, bg->placement, best_width, best_height);
1361 if (cache_file_is_valid (filename, cache_filename))
1362 pixbuf = gdk_pixbuf_new_from_file (cache_filename, NULL);
1363 g_free (cache_filename);
1364
1365 return pixbuf;
1366 }
1367
1368 static GdkPixbuf *
get_as_pixbuf_for_size(GnomeBG * bg,const char * filename,gint num_monitor,gint best_width,gint best_height)1369 get_as_pixbuf_for_size (GnomeBG *bg,
1370 const char *filename,
1371 gint num_monitor,
1372 gint best_width,
1373 gint best_height)
1374 {
1375 const FileCacheEntry *ent;
1376 if ((ent = file_cache_lookup (bg, PIXBUF, filename))) {
1377 return g_object_ref (ent->u.pixbuf);
1378 }
1379 else {
1380 GdkPixbufFormat *format;
1381 GdkPixbuf *pixbuf;
1382 gchar *tmp;
1383 pixbuf = NULL;
1384
1385 /* Try to hit local cache first if relevant */
1386 if (num_monitor != -1)
1387 pixbuf = load_from_cache_file (bg, filename, num_monitor, best_width, best_height);
1388
1389 if (!pixbuf) {
1390 /* If scalable choose maximum size */
1391 format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
1392
1393 if (format != NULL) {
1394 tmp = gdk_pixbuf_format_get_name (format);
1395 } else {
1396 tmp = NULL;
1397 }
1398
1399 if (tmp != NULL &&
1400 strcmp (tmp, "svg") == 0 &&
1401 (best_width > 0 && best_height > 0) &&
1402 (bg->placement == G_DESKTOP_BACKGROUND_STYLE_STRETCHED ||
1403 bg->placement == G_DESKTOP_BACKGROUND_STYLE_SCALED ||
1404 bg->placement == G_DESKTOP_BACKGROUND_STYLE_ZOOM))
1405 pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
1406 else
1407 pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
1408 g_free (tmp);
1409 }
1410
1411 if (pixbuf)
1412 file_cache_add_pixbuf (bg, filename, pixbuf);
1413
1414 return pixbuf;
1415 }
1416 }
1417
1418 static GnomeBGSlideShow *
get_as_slideshow(GnomeBG * bg,const char * filename)1419 get_as_slideshow (GnomeBG *bg, const char *filename)
1420 {
1421 const FileCacheEntry *ent;
1422 if ((ent = file_cache_lookup (bg, SLIDESHOW, filename))) {
1423 return g_object_ref (ent->u.slideshow);
1424 }
1425 else {
1426 GnomeBGSlideShow *show = read_slideshow_file (filename, NULL);
1427
1428 if (show)
1429 file_cache_add_slide_show (bg, filename, show);
1430
1431 return show;
1432 }
1433 }
1434
1435 static GdkPixbuf *
get_as_thumbnail(GnomeBG * bg,GnomeDesktopThumbnailFactory * factory,const char * filename)1436 get_as_thumbnail (GnomeBG *bg, GnomeDesktopThumbnailFactory *factory, const char *filename)
1437 {
1438 const FileCacheEntry *ent;
1439 if ((ent = file_cache_lookup (bg, THUMBNAIL, filename))) {
1440 return g_object_ref (ent->u.thumbnail);
1441 }
1442 else {
1443 GdkPixbuf *thumb = create_thumbnail_for_filename (factory, filename);
1444
1445 if (thumb)
1446 file_cache_add_thumbnail (bg, filename, thumb);
1447
1448 return thumb;
1449 }
1450 }
1451
1452 static gboolean
blow_expensive_caches(gpointer data)1453 blow_expensive_caches (gpointer data)
1454 {
1455 GnomeBG *bg = data;
1456 GList *list, *next;
1457
1458 bg->blow_caches_id = 0;
1459
1460 for (list = bg->file_cache; list != NULL; list = next) {
1461 FileCacheEntry *ent = list->data;
1462 next = list->next;
1463
1464 if (ent->type == PIXBUF) {
1465 file_cache_entry_delete (ent);
1466 bg->file_cache = g_list_delete_link (bg->file_cache,
1467 list);
1468 }
1469 }
1470
1471 if (bg->pixbuf_cache) {
1472 g_object_unref (bg->pixbuf_cache);
1473 bg->pixbuf_cache = NULL;
1474 }
1475
1476 return FALSE;
1477 }
1478
1479 static void
blow_expensive_caches_in_idle(GnomeBG * bg)1480 blow_expensive_caches_in_idle (GnomeBG *bg)
1481 {
1482 if (bg->blow_caches_id == 0) {
1483 bg->blow_caches_id =
1484 g_idle_add (blow_expensive_caches,
1485 bg);
1486 }
1487 }
1488
1489
1490 static gboolean
on_timeout(gpointer data)1491 on_timeout (gpointer data)
1492 {
1493 GnomeBG *bg = data;
1494
1495 bg->timeout_id = 0;
1496
1497 queue_transitioned (bg);
1498
1499 return FALSE;
1500 }
1501
1502 static double
get_slide_timeout(gboolean is_fixed,gdouble duration)1503 get_slide_timeout (gboolean is_fixed,
1504 gdouble duration)
1505 {
1506 double timeout;
1507 if (is_fixed) {
1508 timeout = duration;
1509 } else {
1510 /* Maybe the number of steps should be configurable? */
1511
1512 /* In the worst case we will do a fade from 0 to 256, which mean
1513 * we will never use more than 255 steps, however in most cases
1514 * the first and last value are similar and users can't percieve
1515 * changes in pixel values as small as 1/255th. So, lets not waste
1516 * CPU cycles on transitioning to often.
1517 *
1518 * 64 steps is enough for each step to be just detectable in a 16bit
1519 * color mode in the worst case, so we'll use this as an approximation
1520 * of whats detectable.
1521 */
1522 timeout = duration / 64.0;
1523 }
1524 return timeout;
1525 }
1526
1527 static void
ensure_timeout(GnomeBG * bg,gdouble timeout)1528 ensure_timeout (GnomeBG *bg,
1529 gdouble timeout)
1530 {
1531 if (!bg->timeout_id) {
1532 /* G_MAXUINT means "only one slide" */
1533 if (timeout < G_MAXUINT) {
1534 bg->timeout_id = g_timeout_add_full (
1535 G_PRIORITY_LOW,
1536 timeout * 1000, on_timeout, bg, NULL);
1537 }
1538 }
1539 }
1540
1541 static time_t
get_mtime(const char * filename)1542 get_mtime (const char *filename)
1543 {
1544 GFile *file;
1545 GFileInfo *info;
1546 time_t mtime;
1547
1548 mtime = (time_t)-1;
1549
1550 if (filename) {
1551 file = g_file_new_for_path (filename);
1552 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED,
1553 G_FILE_QUERY_INFO_NONE, NULL, NULL);
1554 if (info) {
1555 mtime = g_file_info_get_attribute_uint64 (info,
1556 G_FILE_ATTRIBUTE_TIME_MODIFIED);
1557 g_object_unref (info);
1558 }
1559 g_object_unref (file);
1560 }
1561
1562 return mtime;
1563 }
1564
1565 static GdkPixbuf *
scale_thumbnail(GDesktopBackgroundStyle placement,const char * filename,GdkPixbuf * thumb,GdkScreen * screen,int dest_width,int dest_height)1566 scale_thumbnail (GDesktopBackgroundStyle placement,
1567 const char *filename,
1568 GdkPixbuf *thumb,
1569 GdkScreen *screen,
1570 int dest_width,
1571 int dest_height)
1572 {
1573 int o_width;
1574 int o_height;
1575
1576 if (placement != G_DESKTOP_BACKGROUND_STYLE_WALLPAPER &&
1577 placement != G_DESKTOP_BACKGROUND_STYLE_CENTERED) {
1578
1579 /* In this case, the pixbuf will be scaled to fit the screen anyway,
1580 * so just return the pixbuf here
1581 */
1582 return g_object_ref (thumb);
1583 }
1584
1585 if (get_thumb_annotations (thumb, &o_width, &o_height) ||
1586 (filename && get_original_size (filename, &o_width, &o_height))) {
1587
1588 int scr_height = gdk_screen_get_height (screen);
1589 int scr_width = gdk_screen_get_width (screen);
1590 int thumb_width = gdk_pixbuf_get_width (thumb);
1591 int thumb_height = gdk_pixbuf_get_height (thumb);
1592 double screen_to_dest = fit_factor (scr_width, scr_height,
1593 dest_width, dest_height);
1594 double thumb_to_orig = fit_factor (thumb_width, thumb_height,
1595 o_width, o_height);
1596 double f = thumb_to_orig * screen_to_dest;
1597 int new_width, new_height;
1598
1599 new_width = floor (thumb_width * f + 0.5);
1600 new_height = floor (thumb_height * f + 0.5);
1601
1602 if (placement == G_DESKTOP_BACKGROUND_STYLE_WALLPAPER) {
1603 /* Heuristic to make sure tiles don't become so small that
1604 * they turn into a blur.
1605 *
1606 * This is strictly speaking incorrect, but the resulting
1607 * thumbnail gives a much better idea what the background
1608 * will actually look like.
1609 */
1610
1611 if ((new_width < 32 || new_height < 32) &&
1612 (new_width < o_width / 4 || new_height < o_height / 4)) {
1613 new_width = o_width / 4;
1614 new_height = o_height / 4;
1615 }
1616 }
1617
1618 thumb = gdk_pixbuf_scale_simple (thumb, new_width, new_height,
1619 GDK_INTERP_BILINEAR);
1620 }
1621 else
1622 g_object_ref (thumb);
1623
1624 return thumb;
1625 }
1626
1627 /* frame_num determines which slide to thumbnail.
1628 * -1 means 'current slide'.
1629 */
1630 static GdkPixbuf *
create_img_thumbnail(GnomeBG * bg,GnomeDesktopThumbnailFactory * factory,GdkScreen * screen,int dest_width,int dest_height,int frame_num)1631 create_img_thumbnail (GnomeBG *bg,
1632 GnomeDesktopThumbnailFactory *factory,
1633 GdkScreen *screen,
1634 int dest_width,
1635 int dest_height,
1636 int frame_num)
1637 {
1638 if (bg->filename) {
1639 GdkPixbuf *thumb;
1640
1641 thumb = get_as_thumbnail (bg, factory, bg->filename);
1642
1643 if (thumb) {
1644 GdkPixbuf *result;
1645 result = scale_thumbnail (bg->placement,
1646 bg->filename,
1647 thumb,
1648 screen,
1649 dest_width,
1650 dest_height);
1651 g_object_unref (thumb);
1652 return result;
1653 }
1654 else {
1655 GnomeBGSlideShow *show = get_as_slideshow (bg, bg->filename);
1656
1657 if (show) {
1658 double alpha;
1659 double duration;
1660 gboolean is_fixed;
1661 const char *file1;
1662 const char *file2;
1663 GdkPixbuf *tmp;
1664
1665 if (frame_num == -1)
1666 gnome_bg_slide_show_get_current_slide (show,
1667 dest_width,
1668 dest_height,
1669 &alpha,
1670 &duration,
1671 &is_fixed,
1672 &file1,
1673 &file2);
1674 else
1675 gnome_bg_slide_show_get_slide (show,
1676 frame_num,
1677 dest_width,
1678 dest_height,
1679 &alpha,
1680 &duration,
1681 &is_fixed,
1682 &file1,
1683 &file2);
1684
1685 if (is_fixed) {
1686 tmp = get_as_thumbnail (bg, factory, file1);
1687 if (tmp) {
1688 thumb = scale_thumbnail (bg->placement,
1689 file1,
1690 tmp,
1691 screen,
1692 dest_width,
1693 dest_height);
1694 g_object_unref (tmp);
1695 }
1696 }
1697 else {
1698 GdkPixbuf *p1, *p2;
1699 p1 = get_as_thumbnail (bg, factory, file1);
1700 p2 = get_as_thumbnail (bg, factory, file2);
1701
1702 if (p1 && p2) {
1703 GdkPixbuf *thumb1, *thumb2;
1704
1705 thumb1 = scale_thumbnail (bg->placement,
1706 file1,
1707 p1,
1708 screen,
1709 dest_width,
1710 dest_height);
1711
1712 thumb2 = scale_thumbnail (bg->placement,
1713 file2,
1714 p2,
1715 screen,
1716 dest_width,
1717 dest_height);
1718
1719 thumb = blend (thumb1, thumb2, alpha);
1720
1721 g_object_unref (thumb1);
1722 g_object_unref (thumb2);
1723 }
1724 if (p1)
1725 g_object_unref (p1);
1726 if (p2)
1727 g_object_unref (p2);
1728 }
1729
1730 ensure_timeout (bg, (guint)get_slide_timeout (is_fixed, duration));
1731
1732 g_object_unref (show);
1733 }
1734 }
1735
1736 return thumb;
1737 }
1738
1739 return NULL;
1740 }
1741
1742 static GdkPixbuf *
get_pixbuf_for_size(GnomeBG * bg,gint num_monitor,gint best_width,gint best_height)1743 get_pixbuf_for_size (GnomeBG *bg,
1744 gint num_monitor,
1745 gint best_width,
1746 gint best_height)
1747 {
1748 guint time_until_next_change;
1749 gboolean hit_cache = FALSE;
1750
1751 /* only hit the cache if the aspect ratio matches */
1752 if (bg->pixbuf_cache) {
1753 int width, height;
1754 width = gdk_pixbuf_get_width (bg->pixbuf_cache);
1755 height = gdk_pixbuf_get_height (bg->pixbuf_cache);
1756 hit_cache = 0.2 > fabs ((best_width / (double)best_height) - (width / (double)height));
1757 if (!hit_cache) {
1758 g_object_unref (bg->pixbuf_cache);
1759 bg->pixbuf_cache = NULL;
1760 }
1761 }
1762
1763 if (!hit_cache && bg->filename) {
1764 bg->file_mtime = get_mtime (bg->filename);
1765
1766 bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, num_monitor, best_width, best_height);
1767 time_until_next_change = G_MAXUINT;
1768 if (!bg->pixbuf_cache) {
1769 GnomeBGSlideShow *show = get_as_slideshow (bg, bg->filename);
1770
1771 if (show) {
1772 double alpha;
1773 double duration;
1774 gboolean is_fixed;
1775 const char *file1;
1776 const char *file2;
1777
1778 g_object_ref (show);
1779
1780 gnome_bg_slide_show_get_current_slide (show,
1781 best_width,
1782 best_height,
1783 &alpha,
1784 &duration,
1785 &is_fixed,
1786 &file1,
1787 &file2);
1788 time_until_next_change = (guint)get_slide_timeout (is_fixed, duration);
1789 if (is_fixed) {
1790 bg->pixbuf_cache = get_as_pixbuf_for_size (bg, file1, num_monitor, best_width, best_height);
1791 }
1792 else {
1793 GdkPixbuf *p1, *p2;
1794 p1 = get_as_pixbuf_for_size (bg, file1, num_monitor, best_width, best_height);
1795 p2 = get_as_pixbuf_for_size (bg, file2, num_monitor, best_width, best_height);
1796
1797 if (p1 && p2) {
1798 bg->pixbuf_cache = blend (p1, p2, alpha);
1799 }
1800 if (p1)
1801 g_object_unref (p1);
1802 if (p2)
1803 g_object_unref (p2);
1804 }
1805
1806 ensure_timeout (bg, time_until_next_change);
1807
1808 g_object_unref (show);
1809 }
1810 }
1811
1812 /* If the next slideshow step is a long time away then
1813 we blow away the expensive stuff (large pixbufs) from
1814 the cache */
1815 if (time_until_next_change > KEEP_EXPENSIVE_CACHE_SECS)
1816 blow_expensive_caches_in_idle (bg);
1817 }
1818
1819 if (bg->pixbuf_cache)
1820 g_object_ref (bg->pixbuf_cache);
1821
1822 return bg->pixbuf_cache;
1823 }
1824
1825 static gboolean
is_different(GnomeBG * bg,const char * filename)1826 is_different (GnomeBG *bg,
1827 const char *filename)
1828 {
1829 if (!filename && bg->filename) {
1830 return TRUE;
1831 }
1832 else if (filename && !bg->filename) {
1833 return TRUE;
1834 }
1835 else if (!filename && !bg->filename) {
1836 return FALSE;
1837 }
1838 else {
1839 time_t mtime = get_mtime (filename);
1840
1841 if (mtime != bg->file_mtime)
1842 return TRUE;
1843
1844 if (strcmp (filename, bg->filename) != 0)
1845 return TRUE;
1846
1847 return FALSE;
1848 }
1849 }
1850
1851 static void
clear_cache(GnomeBG * bg)1852 clear_cache (GnomeBG *bg)
1853 {
1854 GList *list;
1855
1856 if (bg->file_cache) {
1857 for (list = bg->file_cache; list != NULL; list = list->next) {
1858 FileCacheEntry *ent = list->data;
1859
1860 file_cache_entry_delete (ent);
1861 }
1862 g_list_free (bg->file_cache);
1863 bg->file_cache = NULL;
1864 }
1865
1866 if (bg->pixbuf_cache) {
1867 g_object_unref (bg->pixbuf_cache);
1868
1869 bg->pixbuf_cache = NULL;
1870 }
1871
1872 if (bg->timeout_id) {
1873 g_source_remove (bg->timeout_id);
1874
1875 bg->timeout_id = 0;
1876 }
1877 }
1878
1879 /* Pixbuf utilities */
1880 static void
pixbuf_average_value(GdkPixbuf * pixbuf,GdkRGBA * result)1881 pixbuf_average_value (GdkPixbuf *pixbuf,
1882 GdkRGBA *result)
1883 {
1884 guint64 a_total, r_total, g_total, b_total;
1885 guint row, column;
1886 int row_stride;
1887 const guchar *pixels, *p;
1888 int r, g, b, a;
1889 guint64 dividend;
1890 guint width, height;
1891 gdouble dd;
1892
1893 width = gdk_pixbuf_get_width (pixbuf);
1894 height = gdk_pixbuf_get_height (pixbuf);
1895 row_stride = gdk_pixbuf_get_rowstride (pixbuf);
1896 pixels = gdk_pixbuf_get_pixels (pixbuf);
1897
1898 /* iterate through the pixbuf, counting up each component */
1899 a_total = 0;
1900 r_total = 0;
1901 g_total = 0;
1902 b_total = 0;
1903
1904 if (gdk_pixbuf_get_has_alpha (pixbuf)) {
1905 for (row = 0; row < height; row++) {
1906 p = pixels + (row * row_stride);
1907 for (column = 0; column < width; column++) {
1908 r = *p++;
1909 g = *p++;
1910 b = *p++;
1911 a = *p++;
1912
1913 a_total += a;
1914 r_total += r * a;
1915 g_total += g * a;
1916 b_total += b * a;
1917 }
1918 }
1919 dividend = height * width * 0xFF;
1920 a_total *= 0xFF;
1921 } else {
1922 for (row = 0; row < height; row++) {
1923 p = pixels + (row * row_stride);
1924 for (column = 0; column < width; column++) {
1925 r = *p++;
1926 g = *p++;
1927 b = *p++;
1928
1929 r_total += r;
1930 g_total += g;
1931 b_total += b;
1932 }
1933 }
1934 dividend = height * width;
1935 a_total = dividend * 0xFF;
1936 }
1937
1938 dd = dividend * 0xFF;
1939 result->alpha = a_total / dd;
1940 result->red = r_total / dd;
1941 result->green = g_total / dd;
1942 result->blue = b_total / dd;
1943 }
1944
1945 static GdkPixbuf *
pixbuf_scale_to_fit(GdkPixbuf * src,int max_width,int max_height)1946 pixbuf_scale_to_fit (GdkPixbuf *src, int max_width, int max_height)
1947 {
1948 double factor;
1949 int src_width, src_height;
1950 int new_width, new_height;
1951
1952 src_width = gdk_pixbuf_get_width (src);
1953 src_height = gdk_pixbuf_get_height (src);
1954
1955 factor = MIN (max_width / (double) src_width, max_height / (double) src_height);
1956
1957 new_width = floor (src_width * factor + 0.5);
1958 new_height = floor (src_height * factor + 0.5);
1959
1960 return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);
1961 }
1962
1963 static GdkPixbuf *
pixbuf_scale_to_min(GdkPixbuf * src,int min_width,int min_height)1964 pixbuf_scale_to_min (GdkPixbuf *src, int min_width, int min_height)
1965 {
1966 double factor;
1967 int src_width, src_height;
1968 int new_width, new_height;
1969 GdkPixbuf *dest;
1970
1971 src_width = gdk_pixbuf_get_width (src);
1972 src_height = gdk_pixbuf_get_height (src);
1973
1974 factor = MAX (min_width / (double) src_width, min_height / (double) src_height);
1975
1976 new_width = floor (src_width * factor + 0.5);
1977 new_height = floor (src_height * factor + 0.5);
1978
1979 dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
1980 gdk_pixbuf_get_has_alpha (src),
1981 8, min_width, min_height);
1982 if (!dest)
1983 return NULL;
1984
1985 /* crop the result */
1986 gdk_pixbuf_scale (src, dest,
1987 0, 0,
1988 min_width, min_height,
1989 (new_width - min_width) / -2,
1990 (new_height - min_height) / -2,
1991 factor,
1992 factor,
1993 GDK_INTERP_BILINEAR);
1994 return dest;
1995 }
1996
1997 static guchar *
create_gradient(const GdkRGBA * primary,const GdkRGBA * secondary,int n_pixels)1998 create_gradient (const GdkRGBA *primary,
1999 const GdkRGBA *secondary,
2000 int n_pixels)
2001 {
2002 guchar *result = g_malloc (n_pixels * 3);
2003 int i;
2004
2005 for (i = 0; i < n_pixels; ++i) {
2006 double ratio = (i + 0.5) / n_pixels;
2007
2008 result[3 * i + 0] = (int) (0.5 + (primary->red * (1 - ratio) + secondary->red * ratio) * 255);
2009 result[3 * i + 1] = (int) (0.5 + (primary->green * (1 - ratio) + secondary->green * ratio) * 255);
2010 result[3 * i + 2] = (int) (0.5 + (primary->blue * (1 - ratio) + secondary->blue * ratio) * 255);
2011 }
2012
2013 return result;
2014 }
2015
2016 static void
pixbuf_draw_gradient(GdkPixbuf * pixbuf,gboolean horizontal,GdkRGBA * primary,GdkRGBA * secondary,GdkRectangle * rect)2017 pixbuf_draw_gradient (GdkPixbuf *pixbuf,
2018 gboolean horizontal,
2019 GdkRGBA *primary,
2020 GdkRGBA *secondary,
2021 GdkRectangle *rect)
2022 {
2023 int width;
2024 int height;
2025 int rowstride;
2026 guchar *dst;
2027 int n_channels = 3;
2028
2029 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2030 width = rect->width;
2031 height = rect->height;
2032 dst = gdk_pixbuf_get_pixels (pixbuf) + rect->x * n_channels + rowstride * rect->y;
2033
2034 if (horizontal) {
2035 guchar *gradient = create_gradient (primary, secondary, width);
2036 int copy_bytes_per_row = width * n_channels;
2037 int i;
2038
2039 for (i = 0; i < height; i++) {
2040 guchar *d;
2041 d = dst + rowstride * i;
2042 memcpy (d, gradient, copy_bytes_per_row);
2043 }
2044 g_free (gradient);
2045 } else {
2046 guchar *gb, *gradient;
2047 int i;
2048
2049 gradient = create_gradient (primary, secondary, height);
2050 for (i = 0; i < height; i++) {
2051 int j;
2052 guchar *d;
2053
2054 d = dst + rowstride * i;
2055 gb = gradient + n_channels * i;
2056 for (j = width; j > 0; j--) {
2057 int k;
2058
2059 for (k = 0; k < n_channels; k++) {
2060 *(d++) = gb[k];
2061 }
2062 }
2063 }
2064
2065 g_free (gradient);
2066 }
2067 }
2068
2069 static void
pixbuf_blend(GdkPixbuf * src,GdkPixbuf * dest,int src_x,int src_y,int src_width,int src_height,int dest_x,int dest_y,double alpha)2070 pixbuf_blend (GdkPixbuf *src,
2071 GdkPixbuf *dest,
2072 int src_x,
2073 int src_y,
2074 int src_width,
2075 int src_height,
2076 int dest_x,
2077 int dest_y,
2078 double alpha)
2079 {
2080 int dest_width = gdk_pixbuf_get_width (dest);
2081 int dest_height = gdk_pixbuf_get_height (dest);
2082 int offset_x = dest_x - src_x;
2083 int offset_y = dest_y - src_y;
2084
2085 if (src_width < 0)
2086 src_width = gdk_pixbuf_get_width (src);
2087
2088 if (src_height < 0)
2089 src_height = gdk_pixbuf_get_height (src);
2090
2091 if (dest_x < 0)
2092 dest_x = 0;
2093
2094 if (dest_y < 0)
2095 dest_y = 0;
2096
2097 if (dest_x + src_width > dest_width) {
2098 src_width = dest_width - dest_x;
2099 }
2100
2101 if (dest_y + src_height > dest_height) {
2102 src_height = dest_height - dest_y;
2103 }
2104
2105 gdk_pixbuf_composite (src, dest,
2106 dest_x, dest_y,
2107 src_width, src_height,
2108 offset_x, offset_y,
2109 1, 1, GDK_INTERP_NEAREST,
2110 alpha * 0xFF + 0.5);
2111 }
2112
2113 static void
pixbuf_tile(GdkPixbuf * src,GdkPixbuf * dest)2114 pixbuf_tile (GdkPixbuf *src, GdkPixbuf *dest)
2115 {
2116 int x, y;
2117 int tile_width, tile_height;
2118 int dest_width = gdk_pixbuf_get_width (dest);
2119 int dest_height = gdk_pixbuf_get_height (dest);
2120 tile_width = gdk_pixbuf_get_width (src);
2121 tile_height = gdk_pixbuf_get_height (src);
2122
2123 for (y = 0; y < dest_height; y += tile_height) {
2124 for (x = 0; x < dest_width; x += tile_width) {
2125 pixbuf_blend (src, dest, 0, 0,
2126 tile_width, tile_height, x, y, 1.0);
2127 }
2128 }
2129 }
2130
2131 static GnomeBGSlideShow *
read_slideshow_file(const char * filename,GError ** err)2132 read_slideshow_file (const char *filename,
2133 GError **err)
2134 {
2135 GnomeBGSlideShow *show;
2136
2137 show = gnome_bg_slide_show_new (filename);
2138
2139 if (!gnome_bg_slide_show_load (show, err)) {
2140 g_object_unref (show);
2141 return NULL;
2142 }
2143
2144 return show;
2145 }
2146
2147 /* Thumbnail utilities */
2148 static GdkPixbuf *
create_thumbnail_for_filename(GnomeDesktopThumbnailFactory * factory,const char * filename)2149 create_thumbnail_for_filename (GnomeDesktopThumbnailFactory *factory,
2150 const char *filename)
2151 {
2152 char *thumb;
2153 time_t mtime;
2154 GdkPixbuf *orig, *result = NULL;
2155 char *uri;
2156
2157 mtime = get_mtime (filename);
2158
2159 if (mtime == (time_t)-1)
2160 return NULL;
2161
2162 uri = g_filename_to_uri (filename, NULL, NULL);
2163
2164 if (uri == NULL)
2165 return NULL;
2166
2167 thumb = gnome_desktop_thumbnail_factory_lookup (factory, uri, mtime);
2168
2169 if (thumb) {
2170 result = gdk_pixbuf_new_from_file (thumb, NULL);
2171 g_free (thumb);
2172 }
2173 else {
2174 orig = gdk_pixbuf_new_from_file (filename, NULL);
2175 if (orig) {
2176 int orig_width, orig_height;
2177 GdkPixbuf *rotated;
2178
2179 rotated = gdk_pixbuf_apply_embedded_orientation (orig);
2180 if (rotated != NULL) {
2181 g_object_unref (orig);
2182 orig = rotated;
2183 }
2184
2185 orig_width = gdk_pixbuf_get_width (orig);
2186 orig_height = gdk_pixbuf_get_height (orig);
2187
2188 result = pixbuf_scale_to_fit (orig, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
2189
2190 g_object_set_data_full (G_OBJECT (result), "gnome-thumbnail-height",
2191 g_strdup_printf ("%d", orig_height), g_free);
2192 g_object_set_data_full (G_OBJECT (result), "gnome-thumbnail-width",
2193 g_strdup_printf ("%d", orig_width), g_free);
2194
2195 g_object_unref (orig);
2196
2197 gnome_desktop_thumbnail_factory_save_thumbnail (factory, result, uri, mtime);
2198 }
2199 else {
2200 gnome_desktop_thumbnail_factory_create_failed_thumbnail (factory, uri, mtime);
2201 }
2202 }
2203
2204 g_free (uri);
2205
2206 return result;
2207 }
2208
2209 static gboolean
get_thumb_annotations(GdkPixbuf * thumb,int * orig_width,int * orig_height)2210 get_thumb_annotations (GdkPixbuf *thumb,
2211 int *orig_width,
2212 int *orig_height)
2213 {
2214 char *end;
2215 const char *wstr, *hstr;
2216
2217 wstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Width");
2218 hstr = gdk_pixbuf_get_option (thumb, "tEXt::Thumb::Image::Height");
2219
2220 if (hstr && wstr) {
2221 *orig_width = strtol (wstr, &end, 10);
2222 if (*end != 0)
2223 return FALSE;
2224
2225 *orig_height = strtol (hstr, &end, 10);
2226 if (*end != 0)
2227 return FALSE;
2228
2229 return TRUE;
2230 }
2231
2232 return FALSE;
2233 }
2234
2235 /*
2236 * Returns whether the background is a slideshow.
2237 */
2238 gboolean
gnome_bg_changes_with_time(GnomeBG * bg)2239 gnome_bg_changes_with_time (GnomeBG *bg)
2240 {
2241 GnomeBGSlideShow *show;
2242 gboolean ret = FALSE;
2243
2244 g_return_val_if_fail (bg != NULL, FALSE);
2245
2246 if (!bg->filename)
2247 return FALSE;
2248
2249 show = get_as_slideshow (bg, bg->filename);
2250 if (show) {
2251 ret = gnome_bg_slide_show_get_num_slides (show) > 1;
2252 g_object_unref (show);
2253 }
2254
2255 return ret;
2256 }
2257
2258 /**
2259 * gnome_bg_create_frame_thumbnail:
2260 *
2261 * Creates a thumbnail for a certain frame, where 'frame' is somewhat
2262 * vaguely defined as 'suitable point to show while single-stepping
2263 * through the slideshow'.
2264 *
2265 * Returns: (transfer full): the newly created thumbnail or
2266 * or NULL if frame_num is out of bounds.
2267 */
2268 GdkPixbuf *
gnome_bg_create_frame_thumbnail(GnomeBG * bg,GnomeDesktopThumbnailFactory * factory,GdkScreen * screen,int dest_width,int dest_height,int frame_num)2269 gnome_bg_create_frame_thumbnail (GnomeBG *bg,
2270 GnomeDesktopThumbnailFactory *factory,
2271 GdkScreen *screen,
2272 int dest_width,
2273 int dest_height,
2274 int frame_num)
2275 {
2276 GnomeBGSlideShow *show;
2277 GdkPixbuf *result;
2278 GdkPixbuf *thumb;
2279 int skipped;
2280 gboolean is_fixed;
2281
2282 g_return_val_if_fail (bg != NULL, FALSE);
2283
2284 show = get_as_slideshow (bg, bg->filename);
2285
2286 if (!show)
2287 return NULL;
2288
2289
2290 if (frame_num < 0 || frame_num >= gnome_bg_slide_show_get_num_slides (show))
2291 return NULL;
2292
2293
2294 gnome_bg_slide_show_get_slide (show, frame_num, dest_width, dest_height, NULL, NULL, &is_fixed, NULL, NULL);
2295
2296 skipped = 0;
2297 while (!is_fixed) {
2298 skipped++;
2299 gnome_bg_slide_show_get_slide (show, frame_num, dest_width, dest_height, NULL, NULL, &is_fixed, NULL, NULL);
2300 }
2301
2302 result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest_width, dest_height);
2303
2304 draw_color (bg, result);
2305
2306 if (bg->placement != G_DESKTOP_BACKGROUND_STYLE_NONE) {
2307 thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, frame_num + skipped);
2308
2309 if (thumb) {
2310 draw_image_for_thumb (bg, thumb, result);
2311 g_object_unref (thumb);
2312 }
2313 }
2314
2315 return result;
2316 }
2317
2318