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>

Index: gnome-desktop3-2.91.1/libgnome-desktop/gnome-bg.c
===================================================================
--- gnome-desktop3-2.91.1.orig/libgnome-desktop/gnome-bg.c	2010-11-09 19:22:22.000000000 +0100
+++ gnome-desktop3-2.91.1/libgnome-desktop/gnome-bg.c	2010-11-09 22:15:34.378621063 +0100
@@ -33,6 +33,7 @@
 
 #include <gio/gio.h>
 #include <gdesktop-enums.h>
+#include <glib/gstdio.h>
 
 #include <gdk/gdkx.h>
 #include <X11/Xlib.h>
@@ -168,6 +169,7 @@
 
 /* Cache */
 static GdkPixbuf *get_pixbuf_for_size  (GnomeBG               *bg,
+					gint                   num_monitor,
 					int                    width,
 					int                    height);
 static void       clear_cache          (GnomeBG               *bg);
@@ -529,6 +531,117 @@
 	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 (GDesktopBackgroundStyle  placement,
+								  gint              num_monitor,
+								  gint              width,
+								  gint              height)
+{
+	return g_strdup_printf ("%i_%i_%i_%i", num_monitor, (gint) placement, width, height);
+}
+
+static char *
+get_wallpaper_cache_filename (GDesktopBackgroundStyle  placement,
+							   const char       *filename,
+							   gint              num_monitor,
+							   gint              width,
+							   gint              height)
+{
+	gchar *cache_filename;
+	gchar *cache_prefix_name;
+	gchar *md5_filename;
+	gchar *cache_basename;
+	gchar *cache_dir;
+
+	cache_prefix_name = get_wallpaper_cache_prefix_name (placement, num_monitor, width, height);
+	md5_filename = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) filename, strlen (filename));
+	cache_basename = g_strdup_printf ("%s_%s", cache_prefix_name, md5_filename);
+	cache_dir = get_wallpaper_cache_dir ();
+	cache_filename = g_build_filename (cache_dir, cache_basename, NULL);
+
+	g_free (cache_prefix_name);
+	g_free (md5_filename);
+	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;
+	time_t cache_mtime;
+
+	if (!g_file_test (cache_filename, G_FILE_TEST_IS_REGULAR))
+		return FALSE;
+
+	mtime = get_mtime (filename);
+	cache_mtime = get_mtime (cache_filename);
+	return (mtime < cache_mtime);
+}
+
+static void
+refresh_cache_file (GnomeBG     *bg,
+					GdkPixbuf   *new_pixbuf,
+					gint         num_monitor,
+					gint         width,
+					gint         height)
+{
+	gchar           *cache_filename;
+	gchar           *cache_dir;
+	GdkPixbufFormat *format;
+	gchar           *format_name;
+	GDir            *g_cache_dir;
+	gchar           *monitor_prefix;
+	const gchar     *file;
+
+	cache_filename = get_wallpaper_cache_filename (bg->placement, bg->filename, num_monitor, 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_with_parents (cache_dir, 0700);
+			else {
+				g_cache_dir = g_dir_open (cache_dir, 0, NULL);
+				file = g_dir_read_name (g_cache_dir);
+				monitor_prefix = g_strdup_printf ("%i_", num_monitor);
+				gchar *path;
+				while (file != NULL) {
+					path = g_build_filename (cache_dir, file, NULL);
+					/* purge files with same monitor id */
+					if (g_str_has_prefix (file, monitor_prefix) && g_file_test (path, G_FILE_TEST_IS_REGULAR))
+						g_unlink (path);
+					g_free (path);
+					file = g_dir_read_name (g_cache_dir);
+				}
+				g_dir_close (g_cache_dir);
+				g_free (monitor_prefix);
+			}
+			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_filename);
+	g_free (cache_dir);
+}
+
 static void
 file_changed (GFileMonitor *file_monitor,
 	      GFile *child,
@@ -724,9 +837,10 @@
 }
 
 static void
-draw_image_area (GDesktopBackgroundStyle  placement,
+draw_image_area (GnomeBG         *bg,
 		 GdkPixbuf               *pixbuf,
 		 GdkPixbuf               *dest,
+		 gint                    *num_monitor,
 		 GdkRectangle            *area)
 {
 	int dest_width = area->width;
@@ -737,9 +851,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 G_DESKTOP_BACKGROUND_STYLE_WALLPAPER:
 		pixbuf_tile (scaled, dest);
 		break;
@@ -756,12 +870,15 @@
 		g_assert_not_reached ();
 		break;
 	}
-	
+
+	if ((num_monitor != -1) && (dest_width > 300) && (dest_height > 300))
+		refresh_cache_file (bg, scaled, num_monitor, dest_width, dest_height);
+
 	g_object_unref (scaled);
 }
 
 static void
-draw_image (GDesktopBackgroundStyle  placement,
+draw_image_for_thumb (GnomeBG       *bg,
 	    GdkPixbuf               *pixbuf,
 	    GdkPixbuf               *dest)
 {
@@ -772,7 +889,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, -1, &rect);
 }
 
 static void
@@ -782,17 +899,20 @@
 {
 	GdkRectangle rect;
 	GdkPixbuf   *pixbuf;
+	gint         num_monitor;
 
 	rect.x = 0;
 	rect.y = 0;
 	rect.width = gdk_pixbuf_get_width (dest);
 	rect.height = gdk_pixbuf_get_height (dest);
+	num_monitor = gdk_screen_get_n_monitors (screen);
 
-	pixbuf = get_pixbuf_for_size (bg, gdk_pixbuf_get_width (dest), gdk_pixbuf_get_height (dest));
+	pixbuf = get_pixbuf_for_size (bg, num_monitor, rect.width, rect.height);
 	if (pixbuf) {
-		draw_image_area (bg->placement,
+		draw_image_area (bg,
 				 pixbuf,
 				 dest,
+				 num_monitor,
 				 &rect);
 		g_object_unref (pixbuf);
 	}
@@ -811,11 +931,11 @@
 	for (monitor = 0; monitor < num_monitors; monitor++) {
 		GdkPixbuf *pixbuf;
 		gdk_screen_get_monitor_geometry (screen, monitor, &rect);
-		pixbuf = get_pixbuf_for_size (bg, rect.width, rect.height);
+		pixbuf = get_pixbuf_for_size (bg, monitor, rect.width, rect.height);
 		if (pixbuf) {
-			draw_image_area (bg->placement,
+			draw_image_area (bg,
 					 pixbuf,
-					 dest, &rect);
+					 dest, monitor, &rect);
 			g_object_unref (pixbuf);
 		}
 	}
@@ -985,7 +1105,7 @@
 		color.green = (bg->primary.green + bg->secondary.green) / 2;
 		color.blue = (bg->primary.blue + bg->secondary.blue) / 2;
 	}
-	pixbuf = get_pixbuf_for_size (bg, width, height);
+	pixbuf = get_pixbuf_for_size (bg, -1, width, height);
 	if (pixbuf) {
 		guint32 argb = pixbuf_average_value (pixbuf);
 		guchar a = (argb >> 24) & 0xff;
@@ -1155,7 +1275,7 @@
 		thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, -1);
 		
 		if (thumb) {
-			draw_image (bg->placement, thumb, result);
+			draw_image_for_thumb (bg, thumb, result);
 			g_object_unref (thumb);
 		}
 	}
@@ -1602,10 +1722,29 @@
 }
 
 static GdkPixbuf *
+load_from_cache_file (GnomeBG    *bg,
+					  const char *filename,
+					  gint        num_monitor,
+					  gint        best_width,
+					  gint        best_height)
+{
+	GdkPixbuf *pixbuf = NULL;
+	gchar *cache_filename;
+
+	cache_filename = get_wallpaper_cache_filename (bg->placement, filename, num_monitor, 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,
-			int         best_height)
+			gint        num_monitor,
+			gint        best_width,
+			gint        best_height)
 {
 	const FileCacheEntry *ent;
 	if ((ent = file_cache_lookup (bg, PIXBUF, filename))) {
@@ -1615,26 +1754,32 @@
 		GdkPixbufFormat *format;
 		GdkPixbuf *pixbuf;
                 gchar *tmp;
+		pixbuf = NULL;
 
-		/* If scalable choose maximum size */
-		format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
-
-                if (format != NULL) {
-                        tmp = gdk_pixbuf_format_get_name (format);
-                } else {
-                        tmp = NULL;
-                }
+		/* Try to hit local cache first if relevant */
+		if (num_monitor != -1)
+			pixbuf = load_from_cache_file (bg, filename, num_monitor, best_width, best_height);
+		if (!pixbuf) {
+			/* If scalable choose maximum size */
+			format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
+
+			if (format != NULL) {
+				tmp = gdk_pixbuf_format_get_name (format);
+			} else {
+				tmp = NULL;
+			}
 
-		if (tmp != NULL &&
-		    strcmp (tmp, "svg") == 0 &&
-		    (best_width > 0 && best_height > 0) &&
-		    (bg->placement == G_DESKTOP_BACKGROUND_STYLE_STRETCHED ||
-		     bg->placement == G_DESKTOP_BACKGROUND_STYLE_SCALED ||
-		     bg->placement == G_DESKTOP_BACKGROUND_STYLE_ZOOM))
-			pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
-		else
-			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
-                g_free (tmp);
+			if (tmp != NULL &&
+			    strcmp (tmp, "svg") == 0 &&
+			    (best_width > 0 && best_height > 0) &&
+			    (bg->placement == G_DESKTOP_BACKGROUND_STYLE_STRETCHED ||
+			     bg->placement == G_DESKTOP_BACKGROUND_STYLE_SCALED ||
+			     bg->placement == G_DESKTOP_BACKGROUND_STYLE_ZOOM))
+				pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
+			else
+				pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+			g_free (tmp);
+		}
 
 		if (pixbuf)
 			file_cache_add_pixbuf (bg, filename, pixbuf);
@@ -2000,8 +2145,9 @@
 
 static GdkPixbuf *
 get_pixbuf_for_size (GnomeBG *bg,
-		     gint best_width,
-		     gint best_height)
+					 gint num_monitor,
+					 gint best_width,
+					 gint best_height)
 {
 	guint time_until_next_change;
 	gboolean hit_cache = FALSE;
@@ -2021,7 +2167,7 @@
 	if (!hit_cache && bg->filename) {
 		bg->file_mtime = get_mtime (bg->filename);
 
-		bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, best_width, best_height);
+		bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, num_monitor, best_width, best_height);
 		time_until_next_change = G_MAXUINT;
 		if (!bg->pixbuf_cache) {
 			SlideShow *show = get_as_slideshow (bg, bg->filename);
@@ -2037,15 +2183,15 @@
 				if (slide->fixed) {
 					FileSize *size;
 					size = find_best_size (slide->file1, best_width, best_height);
-					bg->pixbuf_cache = get_as_pixbuf_for_size (bg, size->file, best_width, best_height);
+					bg->pixbuf_cache = get_as_pixbuf_for_size (bg, size->file, num_monitor, best_width, best_height);
 				}
 				else {
 					FileSize *size;
 					GdkPixbuf *p1, *p2;
 					size = find_best_size (slide->file1, best_width, best_height);
-					p1 = get_as_pixbuf_for_size (bg, size->file, best_width, best_height);
+					p1 = get_as_pixbuf_for_size (bg, size->file, num_monitor, best_width, best_height);
 					size = find_best_size (slide->file2, best_width, best_height);
-					p2 = get_as_pixbuf_for_size (bg, size->file, best_width, best_height);
+					p2 = get_as_pixbuf_for_size (bg, size->file, num_monitor, best_width, best_height);
 
 					if (p1 && p2) {
 						bg->pixbuf_cache = blend (p1, p2, alpha);
@@ -2883,7 +3029,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_for_thumb (bg, thumb, result);
 			g_object_unref (thumb);
 		}
 	}
