Description: cache wallpapers resized and transformed are stored in ~/.cache/wallpaper/.
the cache is refreshed for each new current wallpaper depending on the resolution or transformation,
and each time the source image is refreshed. The source image format is preserved.
Origin: Ubuntu
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=608419
Author: Didier Roche <didrocks@ubuntu.com>

diff -Nur -x '*.orig' -x '*~' gnome-desktop-2.29.90/libgnome-desktop/gnome-bg.c gnome-desktop-2.29.90.new/libgnome-desktop/gnome-bg.c
--- gnome-desktop-2.29.90/libgnome-desktop/gnome-bg.c	2009-12-17 12:27:18.000000000 +0100
+++ gnome-desktop-2.29.90.new/libgnome-desktop/gnome-bg.c	2010-02-17 12:18:16.806247737 +0100
@@ -33,6 +33,7 @@
 #include <stdlib.h>
 
 #include <gio/gio.h>
+#include <glib/gstdio.h>
 
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
@@ -576,6 +577,112 @@
 	return bg->filename;
 }
 
+static char*
+get_wallpaper_cache_dir ()
+{
+	return g_build_filename (g_get_user_cache_dir(), "wallpaper", NULL);
+}
+
+static char *
+get_wallpaper_cache_prefix_name (GnomeBGPlacement   placement,
+				 int                width,
+				 int                height)
+{
+	const char* placement_name;
+
+	placement_name = placement_to_string (placement);
+	return g_strdup_printf ("%s_%i_%i", placement_name, width, height);
+}
+
+static char *
+get_wallpaper_cache_filename (GnomeBGPlacement	 placement,
+                              const char        *filename,
+                              int                width,
+                              int                height)
+{
+	gchar *cache_basename;
+	gchar *cache_filename;
+	gchar *cache_dir;
+	gchar *cache_prefix_name;
+
+	cache_prefix_name = get_wallpaper_cache_prefix_name (placement, width, height);
+	cache_basename = g_strdup_printf ("%s_%s", cache_prefix_name, filename);
+	cache_basename = g_strdelimit (cache_basename, G_DIR_SEPARATOR_S, '_');
+	cache_dir = get_wallpaper_cache_dir ();
+	cache_filename = g_build_filename (cache_dir, cache_basename, NULL);
+
+	g_free (cache_prefix_name);
+	g_free (cache_basename);
+	g_free (cache_dir);
+
+	return cache_filename;
+
+}
+
+static gboolean
+cache_file_is_valid (const char *filename,
+		     const char *cache_filename)
+{
+	time_t mtime = get_mtime (filename);
+	time_t cache_mtime = get_mtime (cache_filename);
+
+	if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
+		return FALSE;
+	return (mtime < cache_mtime);
+}
+
+static void
+refresh_cache_file (GnomeBG     *bg,
+                    GdkPixbuf   *new_pixbuf,
+                    int          width,
+                    int          height)
+{
+	gchar *cache_filename;
+	gchar *cache_dir;
+	GdkPixbufFormat *format;
+	gchar* format_name;
+	GDir* g_cache_dir;
+	const gchar* file;
+        gchar *cache_prefix_name;
+
+        cache_prefix_name = get_wallpaper_cache_prefix_name (bg->placement, width, height);
+	cache_filename = get_wallpaper_cache_filename (bg->placement, bg->filename, width, height);
+	cache_dir = get_wallpaper_cache_dir ();
+
+	/* Only refresh scaled file on disk if useful (and don't cache slideshow) */
+	if (!cache_file_is_valid (bg->filename, cache_filename)) {
+		format = gdk_pixbuf_get_file_info (bg->filename, NULL, NULL);
+		if (format != NULL) {
+			if (!g_file_test (cache_dir, G_FILE_TEST_IS_DIR))
+				g_mkdir (cache_dir, 0700);
+			else {
+				g_cache_dir = g_dir_open (cache_dir, 0, NULL);
+				file = g_dir_read_name (g_cache_dir);
+				gchar *path;
+				while (file != NULL) {
+					path = g_build_filename (cache_dir, file, NULL);
+					/* only purge files with same transformation and resolution */
+					if (strstr (file, cache_prefix_name) && g_file_test (path, G_FILE_TEST_IS_REGULAR))
+						g_unlink (path);
+					file = g_dir_read_name (g_cache_dir);
+					g_free (path);
+				}
+				g_dir_close (g_cache_dir);
+			}
+			format_name = gdk_pixbuf_format_get_name (format);
+			if (strcmp (format_name, "jpeg") == 0)
+				gdk_pixbuf_save (new_pixbuf, cache_filename, format_name, NULL, "quality", "100", NULL);
+			else
+				gdk_pixbuf_save (new_pixbuf, cache_filename, format_name, NULL, NULL);
+			g_free (format_name);
+		}
+	}
+
+	g_free (cache_prefix_name);
+	g_free(cache_filename);
+	g_free(cache_dir);
+}
+
 static void
 file_changed (GFileMonitor *file_monitor,
 	      GFile *child,
@@ -762,7 +869,7 @@
 }
 
 static void
-draw_image_area (GnomeBGPlacement  placement,
+draw_image_area (GnomeBG	  *bg,
 		 GdkPixbuf        *pixbuf,
 		 GdkPixbuf        *dest,
 		 GdkRectangle     *area)
@@ -775,9 +882,9 @@
 	if (!pixbuf)
 		return;
 
-	scaled = get_scaled_pixbuf (placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
+	scaled = get_scaled_pixbuf (bg->placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
 
-	switch (placement) {
+	switch (bg->placement) {
 	case GNOME_BG_PLACEMENT_TILED:
 		pixbuf_tile (scaled, dest);
 		break;
@@ -791,12 +898,15 @@
 		g_assert_not_reached ();
 		break;
 	}
+
+	if ((dest_width > 300) && (dest_height > 300))
+		refresh_cache_file (bg, scaled, dest_width, dest_height);
 	
 	g_object_unref (scaled);
 }
 
 static void
-draw_image (GnomeBGPlacement  placement,
+draw_image (GnomeBG          *bg,
 	    GdkPixbuf        *pixbuf,
 	    GdkPixbuf        *dest)
 {
@@ -807,7 +917,7 @@
 	rect.width = gdk_pixbuf_get_width (dest);
 	rect.height = gdk_pixbuf_get_height (dest);
 
-	draw_image_area (placement, pixbuf, dest, &rect);
+	draw_image_area (bg, pixbuf, dest, &rect);
 }
 
 static void
@@ -825,7 +935,7 @@
 
 	pixbuf = get_pixbuf_for_size (bg, gdk_pixbuf_get_width (dest), gdk_pixbuf_get_height (dest));
 	if (pixbuf) {
-		draw_image_area (bg->placement,
+		draw_image_area (bg,
 				 pixbuf,
 				 dest,
 				 &rect);
@@ -848,7 +958,7 @@
 		gdk_screen_get_monitor_geometry (screen, monitor, &rect);
 		pixbuf = get_pixbuf_for_size (bg, rect.width, rect.height);
 		if (pixbuf) {
-			draw_image_area (bg->placement,
+			draw_image_area (bg,
 					 pixbuf,
 					 dest, &rect);
 			g_object_unref (pixbuf);
@@ -1191,7 +1301,7 @@
 	thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, -1);
 	
 	if (thumb) {
-		draw_image (bg->placement, thumb, result);
+		draw_image (bg, thumb, result);
 		g_object_unref (thumb);
 	}
 	
@@ -1627,6 +1737,23 @@
 }
 
 static GdkPixbuf *
+load_from_cache_file (GnomeBG    *bg,
+		      const char *filename,
+		      int         best_width,
+		      int         best_height)
+{
+	GdkPixbuf *pixbuf = NULL;
+	gchar *cache_filename;
+
+	cache_filename = get_wallpaper_cache_filename (bg->placement, filename, best_width, best_height);
+	if (cache_file_is_valid (filename, cache_filename))
+		pixbuf = gdk_pixbuf_new_from_file (cache_filename, NULL);
+	g_free (cache_filename);
+
+	return pixbuf;
+}
+
+static GdkPixbuf *
 get_as_pixbuf_for_size (GnomeBG    *bg,
 			const char *filename,
 			int         best_width,
@@ -1638,19 +1765,23 @@
 	}
 	else {
 		GdkPixbufFormat *format;
-		GdkPixbuf *pixbuf;
+		GdkPixbuf *pixbuf = NULL;
 
-		/* If scalable choose maximum size */
-		format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
-		if (format != NULL &&
-		    strcmp (gdk_pixbuf_format_get_name (format), "svg") == 0 &&
-		    (best_width > 0 && best_height > 0) &&
-		    (bg->placement == GNOME_BG_PLACEMENT_FILL_SCREEN ||
-		     bg->placement == GNOME_BG_PLACEMENT_SCALED ||
-		     bg->placement == GNOME_BG_PLACEMENT_ZOOMED))
-			pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
-		else
-			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+		/* Try to hit local cache first */
+		pixbuf = load_from_cache_file(bg, filename, best_width, best_height);
+		if (!pixbuf) {
+			/* If scalable choose maximum size */
+			format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
+			if (format != NULL &&
+			    strcmp (gdk_pixbuf_format_get_name (format), "svg") == 0 &&
+			    (best_width > 0 && best_height > 0) &&
+			    (bg->placement == GNOME_BG_PLACEMENT_FILL_SCREEN ||
+			     bg->placement == GNOME_BG_PLACEMENT_SCALED ||
+			     bg->placement == GNOME_BG_PLACEMENT_ZOOMED))
+				pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
+			else
+				pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+		}
 
 		if (pixbuf)
 			file_cache_add_pixbuf (bg, filename, pixbuf);
@@ -2895,7 +3026,7 @@
 	thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, frame_num + skipped);
 
 	if (thumb) {
-		draw_image (bg->placement, thumb, result);
+		draw_image (bg, thumb, result);
 		g_object_unref (thumb);
 	}
 
