Blob Blame History Raw
diff --git a/libgnome-desktop/gnome-bg.c b/libgnome-desktop/gnome-bg.c
index a882551..a539e06 100644
--- a/libgnome-desktop/gnome-bg.c
+++ b/libgnome-desktop/gnome-bg.c
@@ -144,10 +144,11 @@ static GdkPixbuf *pixbuf_scale_to_fit  (GdkPixbuf  *src,
 static GdkPixbuf *pixbuf_scale_to_min  (GdkPixbuf  *src,
 					int         min_width,
 					int         min_height);
-static void       pixbuf_draw_gradient (GdkPixbuf  *pixbuf,
-					gboolean    horizontal,
-					GdkColor   *c1,
-					GdkColor   *c2);
+static void       pixbuf_draw_gradient (GdkPixbuf    *pixbuf,
+					gboolean      horizontal,
+					GdkColor     *c1,
+					GdkColor     *c2,
+					GdkRectangle *rect);
 static void       pixbuf_tile          (GdkPixbuf  *src,
 					GdkPixbuf  *dest);
 static void       pixbuf_blend         (GdkPixbuf  *src,
@@ -168,7 +169,9 @@ static gboolean   get_thumb_annotations (GdkPixbuf             *thumb,
 					 int                   *orig_height);
 
 /* Cache */
-static GdkPixbuf *get_pixbuf           (GnomeBG               *bg);
+static GdkPixbuf *get_pixbuf_for_size  (GnomeBG               *bg,
+					int                    width,
+					int                    height);
 static void       clear_cache          (GnomeBG               *bg);
 static gboolean   is_different         (GnomeBG               *bg,
 					const char            *filename);
@@ -183,7 +186,11 @@ static SlideShow * get_as_slideshow    (GnomeBG               *bg,
 					const char 	      *filename);
 static Slide *     get_current_slide   (SlideShow 	      *show,
 		   			double    	      *alpha);
-static gboolean    slideshow_changes_with_size (SlideShow *show);
+static gboolean    slideshow_has_multiple_sizes (SlideShow *show);
+
+static FileSize   *find_best_size      (GSList                *sizes,
+					gint                   width,
+					gint                   height);
 
 static void
 color_from_string (const char *string,
@@ -613,13 +620,15 @@ gnome_bg_set_filename (GnomeBG     *bg,
 }
 
 static void
-draw_color (GnomeBG *bg, GdkPixbuf *dest)
+draw_color_area (GnomeBG *bg,
+		 GdkPixbuf *dest,
+		 GdkRectangle *rect)
 {
 	guint32 pixel;
 	
-	switch (bg->color_type)
-	{
+	switch (bg->color_type) {
 	case GNOME_BG_COLOR_SOLID:
+		/* not really a big deal to ignore the area of interest */
 		pixel = ((bg->primary.red >> 8) << 24)      |
 			((bg->primary.green >> 8) << 16)    |
 			((bg->primary.blue >> 8) << 8)      |
@@ -629,11 +638,11 @@ draw_color (GnomeBG *bg, GdkPixbuf *dest)
 		break;
 		
 	case GNOME_BG_COLOR_H_GRADIENT:
-		pixbuf_draw_gradient (dest, TRUE, &(bg->primary), &(bg->secondary));
+		pixbuf_draw_gradient (dest, TRUE, &(bg->primary), &(bg->secondary), rect);
 		break;
 		
 	case GNOME_BG_COLOR_V_GRADIENT:
-		pixbuf_draw_gradient (dest, FALSE, &(bg->primary), &(bg->secondary));
+		pixbuf_draw_gradient (dest, FALSE, &(bg->primary), &(bg->secondary), rect);
 		break;
 		
 	default:
@@ -641,14 +650,77 @@ draw_color (GnomeBG *bg, GdkPixbuf *dest)
 	}
 }
 
+static void
+draw_color (GnomeBG *bg,
+	    GdkPixbuf *dest,
+	    GdkScreen *screen)
+{
+	GdkRectangle rect;
+	rect.x = 0;
+	rect.y = 0;
+	rect.width = gdk_pixbuf_get_width (dest);
+	rect.height = gdk_pixbuf_get_height (dest);
+	draw_color_area (bg, dest, &rect);
+}
+
+static void
+draw_color_each_monitor (GnomeBG *bg,
+			 GdkPixbuf *dest,
+			 GdkScreen *screen)
+{
+	GdkRectangle rect;
+	gint num_monitors;
+	int monitor;
+
+	num_monitors = gdk_screen_get_n_monitors (screen);
+	for (monitor = 0; monitor < num_monitors; monitor++) {
+		gdk_screen_get_monitor_geometry (screen, monitor, &rect);
+		draw_color_area (bg, dest, &rect);
+	}
+}
+
+static GdkPixbuf *
+pixbuf_clip_to_fit (GdkPixbuf *src,
+		    int        max_width,
+		    int        max_height)
+{
+	int src_width, src_height;
+	int w, h;
+	int src_x, src_y;
+	GdkPixbuf *pixbuf;
+
+	src_width = gdk_pixbuf_get_width (src);
+	src_height = gdk_pixbuf_get_height (src);
+
+	if (src_width < max_width && src_height < max_height)
+		return g_object_ref (src);
+
+	w = MIN(src_width, max_width);
+	h = MIN(src_height, max_height);
+
+	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+				 gdk_pixbuf_get_has_alpha (src),
+				 8, w, h);
+
+	src_x = (src_width - w) / 2;
+	src_y = (src_height - h) / 2;
+	gdk_pixbuf_copy_area (src,
+			      src_x, src_y,
+			      w, h,
+			      pixbuf,
+			      0, 0);
+	return pixbuf;
+}
+
 static GdkPixbuf *
 get_scaled_pixbuf (GnomeBGPlacement placement,
 		   GdkPixbuf *pixbuf,
 		   int width, int height,
-		   int *x, int *y, int *w, int *h)
+		   int *x, int *y,
+		   int *w, int *h)
 {
 	GdkPixbuf *new;
-	
+
 #if 0
 	g_print ("original_width: %d %d\n",
 		 gdk_pixbuf_get_width (pixbuf),
@@ -672,7 +744,7 @@ get_scaled_pixbuf (GnomeBGPlacement placement,
 	case GNOME_BG_PLACEMENT_CENTERED:
 	case GNOME_BG_PLACEMENT_TILED:
 	default:
-		new = g_object_ref (pixbuf);
+		new = pixbuf_clip_to_fit (pixbuf, width, height);
 		break;
 	}
 	
@@ -685,20 +757,20 @@ get_scaled_pixbuf (GnomeBGPlacement placement,
 }
 
 static void
-draw_image (GnomeBGPlacement  placement,
-	    GdkPixbuf        *pixbuf,
-	    GdkPixbuf        *dest)
+draw_image_area (GnomeBGPlacement  placement,
+		 GdkPixbuf        *pixbuf,
+		 GdkPixbuf        *dest,
+		 GdkRectangle     *area)
 {
-	int dest_width = gdk_pixbuf_get_width (dest);
-	int dest_height = gdk_pixbuf_get_height (dest);
+	int dest_width = area->width;
+	int dest_height = area->height;
 	int x, y, w, h;
 	GdkPixbuf *scaled;
 	
 	if (!pixbuf)
 		return;
-	
-	scaled = get_scaled_pixbuf (
-		placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
+
+	scaled = get_scaled_pixbuf (placement, pixbuf, dest_width, dest_height, &x, &y, &w, &h);
 
 	switch (placement) {
 	case GNOME_BG_PLACEMENT_TILED:
@@ -708,7 +780,7 @@ draw_image (GnomeBGPlacement  placement,
 	case GNOME_BG_PLACEMENT_CENTERED:
 	case GNOME_BG_PLACEMENT_FILL_SCREEN:
 	case GNOME_BG_PLACEMENT_SCALED:
-		pixbuf_blend (scaled, dest, 0, 0, w, h, x, y, 1.0);
+		pixbuf_blend (scaled, dest, 0, 0, w, h, x + area->x, y + area->y, 1.0);
 		break;
 	default:
 		g_assert_not_reached ();
@@ -718,54 +790,97 @@ draw_image (GnomeBGPlacement  placement,
 	g_object_unref (scaled);
 }
 
+static void
+draw_image (GnomeBGPlacement  placement,
+	    GdkPixbuf        *pixbuf,
+	    GdkPixbuf        *dest)
+{
+	GdkRectangle rect;
+
+	rect.x = 0;
+	rect.y = 0;
+	rect.width = gdk_pixbuf_get_width (dest);
+	rect.height = gdk_pixbuf_get_height (dest);
+
+	draw_image_area (placement, pixbuf, dest, &rect);
+}
+
+static void
+draw_once (GnomeBG   *bg,
+	   GdkPixbuf *dest,
+	   GdkScreen *screen)
+{
+	GdkRectangle rect;
+
+	rect.x = 0;
+	rect.y = 0;
+	rect.width = gdk_pixbuf_get_width (dest);
+	rect.height = gdk_pixbuf_get_height (dest);
+
+	draw_image_area (bg->placement,
+			 get_pixbuf_for_size (bg, gdk_pixbuf_get_width (dest), gdk_pixbuf_get_height (dest)),
+			 dest,
+			 &rect);
+}
+
+static void
+draw_each_monitor (GnomeBG   *bg,
+		   GdkPixbuf *dest,
+		   GdkScreen *screen)
+{
+	GdkRectangle rect;
+	gint num_monitors;
+	int monitor;
+
+	num_monitors = gdk_screen_get_n_monitors (screen);
+	for (monitor = 0; monitor < num_monitors; monitor++) {
+		gdk_screen_get_monitor_geometry (screen, monitor, &rect);
+		draw_image_area (bg->placement,
+				 get_pixbuf_for_size (bg, rect.width, rect.height),
+				 dest, &rect);
+	}
+}
+
 void
-gnome_bg_draw (GnomeBG *bg, GdkPixbuf *dest)
+gnome_bg_draw (GnomeBG *bg,
+	       GdkPixbuf *dest,
+	       GdkScreen *screen,
+	       gboolean is_root)
 {
 	if (!bg)
 		return;
-	
-	draw_color (bg, dest);
-	
-	draw_image (bg->placement, get_pixbuf (bg), dest);
+
+	if (is_root) {
+		draw_color_each_monitor (bg, dest, screen);
+		draw_each_monitor (bg, dest, screen);
+	} else {
+		draw_color (bg, dest, screen);
+		draw_once (bg, dest, screen);
+	}
 }
 
 gboolean
-gnome_bg_changes_with_size (GnomeBG *bg)
+gnome_bg_has_multiple_sizes (GnomeBG *bg)
 {
 	SlideShow *show;
 
 	g_return_val_if_fail (bg != NULL, FALSE);
 
 	show = get_as_slideshow (bg, bg->filename);
-	if (show) 
-		return slideshow_changes_with_size (show);
-	
-	if (bg->color_type != GNOME_BG_COLOR_SOLID) {
-		if (!get_pixbuf (bg))
-			return TRUE;
-		if (gdk_pixbuf_get_has_alpha (get_pixbuf (bg)))
-			return TRUE;
-		if (bg->placement == GNOME_BG_PLACEMENT_CENTERED)
-			return TRUE;
-		return FALSE;
-	}
-	else if (bg->placement == GNOME_BG_PLACEMENT_TILED) {
-		return FALSE;
-	}
-	else {
-		return TRUE;
-	}
+	if (show)
+		return slideshow_has_multiple_sizes (show);
+
+	return FALSE;
 }
 
 static void
-gnome_bg_get_pixmap_size (GnomeBG  *bg,
-			  int       width,
-			  int       height,
-			  int      *pixmap_width,
-			  int      *pixmap_height)
+gnome_bg_get_pixmap_size (GnomeBG   *bg,
+			  int        width,
+			  int        height,
+			  int       *pixmap_width,
+			  int       *pixmap_height)
 {
 	int dummy;
-	int pb_width, pb_height;
 	
 	if (!pixmap_width)
 		pixmap_width = &dummy;
@@ -774,8 +889,8 @@ gnome_bg_get_pixmap_size (GnomeBG  *bg,
 	
 	*pixmap_width = width;
 	*pixmap_height = height;
-	
-	if (!get_pixbuf (bg)) {
+
+	if (!bg->filename) {
 		switch (bg->color_type) {
 		case GNOME_BG_COLOR_SOLID:
 			*pixmap_width = 1;
@@ -783,44 +898,12 @@ gnome_bg_get_pixmap_size (GnomeBG  *bg,
 			break;
 			
 		case GNOME_BG_COLOR_H_GRADIENT:
-			*pixmap_width = width;
-			*pixmap_height = GRADIENT_PIXMAP_TILE_SIZE;
-			break;
-			
 		case GNOME_BG_COLOR_V_GRADIENT:
-			*pixmap_width = GRADIENT_PIXMAP_TILE_SIZE;
-			*pixmap_height = height;
 			break;
 		}
 		
 		return;
 	}
-	
-	pb_width = gdk_pixbuf_get_width (get_pixbuf (bg));
-	pb_height = gdk_pixbuf_get_height (get_pixbuf (bg));
-	
-	if (bg->placement == GNOME_BG_PLACEMENT_TILED) {
-		if (gdk_pixbuf_get_has_alpha (get_pixbuf (bg)) &&
-		    bg->color_type != GNOME_BG_COLOR_SOLID) {
-			if (bg->color_type == GNOME_BG_COLOR_H_GRADIENT) {
-				/* FIXME: Should this be
-				 * MAX (GRADIENT_TILE_SIZE, pb_height)?
-				 */
-				*pixmap_height = pb_height; 
-				*pixmap_width = width;
-			}
-			else {
-				/* FIXME: Should this be
-				 * MAX (GRAIDENT_TILE_SIZE, pb_width? */
-				*pixmap_width = pb_width;
-				*pixmap_height = height;
-			}
-		}
-		else {
-			*pixmap_width = pb_width;
-			*pixmap_height = pb_height;
-		}
-	}
 }
 
 /**
@@ -842,7 +925,7 @@ gnome_bg_create_pixmap (GnomeBG	    *bg,
 			GdkWindow   *window,
 			int	     width,
 			int	     height,
-			gboolean     root)
+			gboolean     is_root)
 {
 	int pm_width, pm_height;
 	GdkPixmap *pixmap;
@@ -860,9 +943,10 @@ gnome_bg_create_pixmap (GnomeBG	    *bg,
 	bg->last_pixmap_width = width;
 	bg->last_pixmap_height = height;
 
+	/* has the side effect of loading and caching pixbuf only when in tile mode */
 	gnome_bg_get_pixmap_size (bg, width, height, &pm_width, &pm_height);
 	
-	if (root) {
+	if (is_root) {
 		pixmap = make_root_pixmap (gdk_drawable_get_screen (window),
 					   pm_width, pm_height);
 	}
@@ -870,7 +954,7 @@ gnome_bg_create_pixmap (GnomeBG	    *bg,
 		pixmap = gdk_pixmap_new (window, pm_width, pm_height, -1);
 	}
 	
-	if (!get_pixbuf (bg) && bg->color_type == GNOME_BG_COLOR_SOLID) {
+	if (!bg->filename && bg->color_type == GNOME_BG_COLOR_SOLID) {
 		GdkGC *gc = gdk_gc_new (pixmap);
 		gdk_gc_set_rgb_fg_color (gc, &(bg->primary));
 		
@@ -883,7 +967,7 @@ gnome_bg_create_pixmap (GnomeBG	    *bg,
 		
 		pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
 					 width, height);
-		gnome_bg_draw (bg, pixbuf);
+		gnome_bg_draw (bg, pixbuf, gdk_drawable_get_screen (GDK_DRAWABLE (window)), is_root);
 		gdk_draw_pixbuf (pixmap, NULL, pixbuf,
 				 0, 0,
 				 0, 0, width, height,
@@ -899,10 +983,13 @@ gnome_bg_create_pixmap (GnomeBG	    *bg,
  * clients know what colors to draw on top with
  */
 gboolean
-gnome_bg_is_dark (GnomeBG *bg)
+gnome_bg_is_dark (GnomeBG *bg,
+		  int      width,
+		  int      height)
 {
 	GdkColor color;
 	int intensity;
+	GdkPixbuf *pixbuf;
 	
 	g_return_val_if_fail (bg != NULL, FALSE);
 	
@@ -913,9 +1000,9 @@ gnome_bg_is_dark (GnomeBG *bg)
 		color.green = (bg->primary.green + bg->secondary.green) / 2;
 		color.blue = (bg->primary.blue + bg->secondary.blue) / 2;
 	}
-	
-	if (get_pixbuf (bg)) {
-		guint32 argb = pixbuf_average_value (get_pixbuf (bg));
+	pixbuf = get_pixbuf_for_size (bg, width, height);
+	if (pixbuf) {
+		guint32 argb = pixbuf_average_value (pixbuf);
 		guchar a = (argb >> 24) & 0xff;
 		guchar r = (argb >> 16) & 0xff;
 		guchar g = (argb >>  8) & 0xff;
@@ -1001,9 +1088,31 @@ get_original_size (const char *filename,
 	return result;
 }
 
+static const char *
+get_filename_for_size (GnomeBG *bg, gint best_width, gint best_height)
+{
+	SlideShow *show;
+	Slide *slide;
+	FileSize *size;
+
+	if (!bg->filename)
+		return NULL;
+
+	show = get_as_slideshow (bg, bg->filename);
+	if (!show) {
+		return bg->filename;
+	}
+
+	slide = get_current_slide (show, NULL);
+	size = find_best_size (slide->file1, best_width, best_height);
+	return size->file;
+}
+
 gboolean
 gnome_bg_get_image_size (GnomeBG	       *bg,
 			 GnomeDesktopThumbnailFactory *factory,
+			 int                    best_width,
+			 int                    best_height,
 			 int		       *width,
 			 int		       *height)
 {
@@ -1017,21 +1126,8 @@ gnome_bg_get_image_size (GnomeBG	       *bg,
 	if (!bg->filename)
 		return FALSE;
 	
-	filename = bg->filename;
+	filename = get_filename_for_size (bg, best_width, best_height);
 	thumb = create_thumbnail_for_filename (factory, filename);
-	
-	if (!thumb) {
-		SlideShow *show = get_as_slideshow (bg, bg->filename);
-		if (show) {
-			double alpha;
-			FileSize *fs;
-			Slide *slide = get_current_slide (show, &alpha);
-			fs = slide->file1->data;
-			filename = fs->file;
-			thumb = create_thumbnail_for_filename (factory, filename);
-		}
-	}
-
 	if (thumb) {
 		if (get_thumb_annotations (thumb, width, height))
 			result = TRUE;
@@ -1068,7 +1164,7 @@ gnome_bg_create_thumbnail (GnomeBG               *bg,
 	
 	result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest_width, dest_height);
 	
-	draw_color (bg, result);
+	draw_color (bg, result, screen);
 	
 	thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, -1);
 	
@@ -1316,7 +1412,7 @@ struct _SlideShow
 
 	GQueue *slides;
 	
-	gboolean changes_with_size;
+	gboolean has_multiple_sizes;
 
 	/* used during parsing */
 	struct tm start_tm;
@@ -1356,7 +1452,8 @@ get_current_slide (SlideShow *show,
 		Slide *slide = list->data;
 
 		if (elapsed + slide->duration > delta) {
-			*alpha = (delta - elapsed) / (double)slide->duration;
+			if (alpha)
+				*alpha = (delta - elapsed) / (double)slide->duration;
 			return slide;
 		}
 
@@ -1512,7 +1609,10 @@ file_cache_add_slide_show (GnomeBG *bg,
 }
 
 static GdkPixbuf *
-get_as_pixbuf (GnomeBG *bg, const char *filename)
+get_as_pixbuf_for_size (GnomeBG    *bg,
+			const char *filename,
+			int         best_width,
+			int         best_height)
 {
 	const FileCacheEntry *ent;
 	if ((ent = file_cache_lookup (bg, PIXBUF, filename))) {
@@ -1523,14 +1623,14 @@ get_as_pixbuf (GnomeBG *bg, const char *filename)
 		GdkPixbuf *pixbuf;
 
 		/* If scalable choose maximum size */
-		format = gdk_pixbuf_get_file_info (bg->filename, NULL, NULL);
+		format = gdk_pixbuf_get_file_info (filename, NULL, NULL);
 		if (format != NULL &&
 		    strcmp (gdk_pixbuf_format_get_name (format), "svg") == 0 &&
-		    (bg->last_pixmap_width > 0 && bg->last_pixmap_height > 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, bg->last_pixmap_width, bg->last_pixmap_height, NULL);
+			pixbuf = gdk_pixbuf_new_from_file_at_size (filename, best_width, best_height, NULL);
 		else
 			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
 
@@ -1763,9 +1863,8 @@ create_img_thumbnail (GnomeBG                      *bg,
 		GdkPixbuf *thumb = get_as_thumbnail (bg, factory, bg->filename);
 
 		if (thumb) {
-			return scale_thumbnail (
-				bg->placement, bg->filename,
-				thumb, screen, dest_width, dest_height);
+			return scale_thumbnail (bg->placement, bg->filename,
+						thumb, screen, dest_width, dest_height);
 		}
 		else {
 			SlideShow *show = get_as_slideshow (bg, bg->filename);
@@ -1784,36 +1883,29 @@ create_img_thumbnail (GnomeBG                      *bg,
 				if (slide->fixed) {
 					GdkPixbuf *tmp;
 					FileSize *fs;
-					
-					fs = slide->file1->data;
+					fs = find_best_size (slide->file1, dest_width, dest_height);
 					tmp = get_as_thumbnail (bg, factory, fs->file);
-
-					thumb = scale_thumbnail (
-						bg->placement, fs->file,
-						tmp, screen, dest_width, dest_height);
+					if (tmp)
+						thumb = scale_thumbnail (bg->placement, fs->file,
+									 tmp, screen, dest_width, dest_height);
 				}
 				else {
-					FileSize *fs;
+					FileSize *fs1, *fs2;
 					GdkPixbuf *p1, *p2;
+					fs1 = find_best_size (slide->file1, dest_width, dest_height);
+					p1 = get_as_thumbnail (bg, factory, fs1->file);
 
-					fs = slide->file1->data;
-					p1 = get_as_thumbnail (bg, factory, fs->file);
-
-					fs = slide->file2->data;
-					p2 = get_as_thumbnail (bg, factory, fs->file);
+					fs2 = find_best_size (slide->file2, dest_width, dest_height);
+					p2 = get_as_thumbnail (bg, factory, fs2->file);
 
 					if (p1 && p2) {
 						GdkPixbuf *thumb1, *thumb2;
 
-						fs = slide->file1->data;
-						thumb1 = scale_thumbnail (
-							bg->placement, fs->file,
-							p1, screen, dest_width, dest_height);
+						thumb1 = scale_thumbnail (bg->placement, fs1->file,
+									  p1, screen, dest_width, dest_height);
 
-						fs = slide->file2->data;
-						thumb2 = scale_thumbnail (
-							bg->placement, fs->file,
-							p2, screen, dest_width, dest_height);
+						thumb2 = scale_thumbnail (bg->placement, fs2->file,
+									  p2, screen, dest_width, dest_height);
 
 						thumb = blend (thumb1, thumb2, alpha);
 
@@ -1880,18 +1972,26 @@ find_best_size (GSList *sizes, gint width, gint height)
 }
 
 static GdkPixbuf *
-get_pixbuf (GnomeBG *bg)
+get_pixbuf_for_size (GnomeBG *bg, gint best_width, gint best_height)
 {
 	/* FIXME: this ref=TRUE/FALSE stuff is crazy */
-	
 	guint time_until_next_change;
 	gboolean ref = FALSE;
-	
-	if (!bg->pixbuf_cache && bg->filename) {
+	gboolean hit_cache = FALSE;
+
+	/* only hit the cache if the aspect ratio matches */
+	if (bg->pixbuf_cache) {
+		int width, height;
+		width = gdk_pixbuf_get_width (bg->pixbuf_cache);
+		height = gdk_pixbuf_get_height (bg->pixbuf_cache);
+		hit_cache = 0.2 > fabs ((best_width / (double)best_height) - (width / (double)height));
+	}
+
+	if (!hit_cache && bg->filename) {
 		ref = TRUE;
 		bg->file_mtime = get_mtime (bg->filename);
 		
-		bg->pixbuf_cache = get_as_pixbuf (bg, bg->filename);
+		bg->pixbuf_cache = get_as_pixbuf_for_size (bg, bg->filename, best_width, best_height);
 		time_until_next_change = G_MAXUINT;
 		if (!bg->pixbuf_cache) {
 			SlideShow *show = get_as_slideshow (bg, bg->filename);
@@ -1906,16 +2006,16 @@ get_pixbuf (GnomeBG *bg)
 				time_until_next_change = (guint)get_slide_timeout (slide);
 				if (slide->fixed) {
 					FileSize *size;
-					size = find_best_size (slide->file1, bg->last_pixmap_width, bg->last_pixmap_height);
-					bg->pixbuf_cache = get_as_pixbuf (bg, size->file);
+					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);
 				}
 				else {
 					FileSize *size;
 					GdkPixbuf *p1, *p2;
-					size = find_best_size (slide->file1, bg->last_pixmap_width, bg->last_pixmap_height);
-					p1 = get_as_pixbuf (bg, size->file);
-					size = find_best_size (slide->file2, bg->last_pixmap_width, bg->last_pixmap_height);
-					p2 = get_as_pixbuf (bg, size->file);
+					size = find_best_size (slide->file1, best_width, best_height);
+					p1 = get_as_pixbuf_for_size (bg, size->file, 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);
 
 
 					if (p1 && p2) {
@@ -2084,22 +2184,38 @@ pixbuf_scale_to_min (GdkPixbuf *src, int min_width, int min_height)
 	double factor;
 	int src_width, src_height;
 	int new_width, new_height;
-	
+	GdkPixbuf *dest;
+
 	src_width = gdk_pixbuf_get_width (src);
 	src_height = gdk_pixbuf_get_height (src);
-	
+
 	factor = MAX (min_width / (double) src_width, min_height / (double) src_height);
-	
+
 	new_width = floor (src_width * factor + 0.5);
 	new_height = floor (src_height * factor + 0.5);
-	
-	return gdk_pixbuf_scale_simple (src, new_width, new_height, GDK_INTERP_BILINEAR);
+
+	dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+			       gdk_pixbuf_get_has_alpha (src),
+			       8, min_width, min_height);
+	if (!dest)
+		return NULL;
+
+	/* crop the result */
+	gdk_pixbuf_scale (src, dest,
+			  0, 0,
+			  min_width, min_height,
+			  (new_width - min_width) / -2,
+			  (new_height - min_height) / -2,
+			  factor,
+			  factor,
+			  GDK_INTERP_BILINEAR);
+	return dest;
 }
 
 static guchar *
 create_gradient (const GdkColor *primary,
 		 const GdkColor *secondary,
-		 int	          n_pixels)
+		 int	         n_pixels)
 {
 	guchar *result = g_malloc (n_pixels * 3);
 	int i;
@@ -2116,43 +2232,56 @@ create_gradient (const GdkColor *primary,
 }	
 
 static void
-pixbuf_draw_gradient (GdkPixbuf *pixbuf,
-		      gboolean   horizontal,
-		      GdkColor  *primary,
-		      GdkColor  *secondary)
-{
-	int width  = gdk_pixbuf_get_width (pixbuf);
-	int height = gdk_pixbuf_get_height (pixbuf);
-	int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
-	guchar *dst = gdk_pixbuf_get_pixels (pixbuf);
-	guchar *dst_limit = dst + height * rowstride;
-	
+pixbuf_draw_gradient (GdkPixbuf    *pixbuf,
+		      gboolean      horizontal,
+		      GdkColor     *primary,
+		      GdkColor     *secondary,
+		      GdkRectangle *rect)
+{
+	int width;
+	int height;
+	int rowstride;
+	guchar *dst;
+	guchar *dst_limit;
+	int n_channels = 3;
+
+	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+	width = rect->width;
+	height = rect->height;
+	dst = gdk_pixbuf_get_pixels (pixbuf) + rect->x * n_channels + rowstride * rect->y;
+	dst_limit = dst + height * rowstride;
+
 	if (horizontal) {
 		guchar *gradient = create_gradient (primary, secondary, width);
-		int copy_bytes_per_row = width * 3;
-		
-		while (dst < dst_limit) {
-			memcpy (dst, gradient, copy_bytes_per_row);
-			dst += rowstride;
+		int copy_bytes_per_row = width * n_channels;
+		int i;
+
+		for (i = 0; i < height; i++) {
+			guchar *d;
+			d = dst + rowstride * i;
+			memcpy (d, gradient, copy_bytes_per_row);
 		}
 		g_free (gradient);
 	} else {
 		guchar *gb, *gradient;
-		
-		gb = gradient = create_gradient (primary, secondary, height);
-		while (dst < dst_limit) {
-			int i;
-			guchar *d = dst;
-			guchar r = *gb++;
-			guchar g = *gb++;
-			guchar b = *gb++;
-			for (i = 0; i < width; i++) {
-				*d++ = r;
-				*d++ = g;
-				*d++ = b;
+		int i;
+
+		gradient = create_gradient (primary, secondary, height);
+		for (i = 0; i < height; i++) {
+			int j;
+			guchar *d;
+
+			d = dst + rowstride * i;
+			gb = gradient + n_channels * i;
+			for (j = width; j > 0; j--) {
+				int k;
+
+				for (k = 0; k < n_channels; k++) {
+					*(d++) = gb[k];
+				}
 			}
-			dst += rowstride;
 		}
+
 		g_free (gradient);
 	}
 }
@@ -2162,8 +2291,8 @@ pixbuf_blend (GdkPixbuf *src,
 	      GdkPixbuf *dest,
 	      int	 src_x,
 	      int	 src_y,
-	      int	 width,
-	      int        height,
+	      int	 src_width,
+	      int        src_height,
 	      int	 dest_x,
 	      int	 dest_y,
 	      double	 alpha)
@@ -2173,11 +2302,11 @@ pixbuf_blend (GdkPixbuf *src,
 	int offset_x = dest_x - src_x;
 	int offset_y = dest_y - src_y;
 
-	if (width < 0)
-		width = gdk_pixbuf_get_width (src);
+	if (src_width < 0)
+		src_width = gdk_pixbuf_get_width (src);
 
-	if (height < 0)
-		height = gdk_pixbuf_get_height (src);
+	if (src_height < 0)
+		src_height = gdk_pixbuf_get_height (src);
 	
 	if (dest_x < 0)
 		dest_x = 0;
@@ -2185,17 +2314,17 @@ pixbuf_blend (GdkPixbuf *src,
 	if (dest_y < 0)
 		dest_y = 0;
 	
-	if (dest_x + width > dest_width) {
-		width = dest_width - dest_x;
+	if (dest_x + src_width > dest_width) {
+		src_width = dest_width - dest_x;
 	}
 	
-	if (dest_y + height > dest_height) {
-		height = dest_height - dest_y;
+	if (dest_y + src_height > dest_height) {
+		src_height = dest_height - dest_y;
 	}
 
 	gdk_pixbuf_composite (src, dest,
 			      dest_x, dest_y,
-			      width, height,
+			      src_width, src_height,
 			      offset_x, offset_y,
 			      1, 1, GDK_INTERP_NEAREST,
 			      alpha * 0xFF + 0.5);
@@ -2368,14 +2497,14 @@ handle_text (GMarkupParseContext *context,
 		fs->file = g_strdup (text);
 		slide->file1 = g_slist_prepend (slide->file1, fs);
 		if (slide->file1->next != NULL)
-			parser->changes_with_size = TRUE;                       
+			parser->has_multiple_sizes = TRUE;                       
 	}
 	else if (stack_is (parser, "size", "file", "static", "background", NULL) ||
 		 stack_is (parser, "size", "from", "transition", "background", NULL)) {
 		fs = slide->file1->data;
 		fs->file = g_strdup (text);
 		if (slide->file1->next != NULL)
-			parser->changes_with_size = TRUE; 
+			parser->has_multiple_sizes = TRUE; 
 	}
 	else if (stack_is (parser, "to", "transition", "background", NULL)) {
 		for (i = 0; text[i]; i++) {
@@ -2390,13 +2519,13 @@ handle_text (GMarkupParseContext *context,
 		fs->file = g_strdup (text);
 		slide->file2 = g_slist_prepend (slide->file2, fs);
 		if (slide->file2->next != NULL)
-			parser->changes_with_size = TRUE;                       
+			parser->has_multiple_sizes = TRUE;                       
 	}
 	else if (stack_is (parser, "size", "to", "transition", "background", NULL)) {
 		fs = slide->file2->data;
 		fs->file = g_strdup (text);
 		if (slide->file2->next != NULL)
-			parser->changes_with_size = TRUE;
+			parser->has_multiple_sizes = TRUE;
 	}
 }
 
@@ -2639,9 +2768,9 @@ get_thumb_annotations (GdkPixbuf *thumb,
 }
 
 static gboolean
-slideshow_changes_with_size (SlideShow *show)
+slideshow_has_multiple_sizes (SlideShow *show)
 {
-	return show->changes_with_size;
+	return show->has_multiple_sizes;
 }
 
 /*
@@ -2712,7 +2841,7 @@ gnome_bg_create_frame_thumbnail (GnomeBG			*bg,
 
 	result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest_width, dest_height);
 
-	draw_color (bg, result);
+	draw_color (bg, result, screen);
 
 	thumb = create_img_thumbnail (bg, factory, screen, dest_width, dest_height, frame_num + skipped);
 
diff --git a/libgnome-desktop/libgnomeui/gnome-bg.h b/libgnome-desktop/libgnomeui/gnome-bg.h
index 993173a..6ab2cb0 100644
--- a/libgnome-desktop/libgnomeui/gnome-bg.h
+++ b/libgnome-desktop/libgnomeui/gnome-bg.h
@@ -87,7 +87,9 @@ const gchar *    gnome_bg_get_filename          (GnomeBG               *bg);
 
 /* Drawing and thumbnailing */
 void             gnome_bg_draw                  (GnomeBG               *bg,
-						 GdkPixbuf             *dest);
+						 GdkPixbuf             *dest,
+						 GdkScreen	       *screen,
+                                                 gboolean               is_root);
 GdkPixmap *      gnome_bg_create_pixmap         (GnomeBG               *bg,
 						 GdkWindow             *window,
 						 int                    width,
@@ -95,6 +97,8 @@ GdkPixmap *      gnome_bg_create_pixmap         (GnomeBG               *bg,
 						 gboolean               root);
 gboolean         gnome_bg_get_image_size        (GnomeBG               *bg,
 						 GnomeDesktopThumbnailFactory *factory,
+                                                 int                    best_width,
+                                                 int                    best_height,
 						 int                   *width,
 						 int                   *height);
 GdkPixbuf *      gnome_bg_create_thumbnail      (GnomeBG               *bg,
@@ -102,8 +106,10 @@ GdkPixbuf *      gnome_bg_create_thumbnail      (GnomeBG               *bg,
 						 GdkScreen             *screen,
 						 int                    dest_width,
 						 int                    dest_height);
-gboolean         gnome_bg_is_dark               (GnomeBG               *bg);
-gboolean         gnome_bg_changes_with_size     (GnomeBG               *bg);
+gboolean         gnome_bg_is_dark               (GnomeBG               *bg,
+                                                 int                    dest_width,
+						 int                    dest_height);
+gboolean         gnome_bg_has_multiple_sizes    (GnomeBG               *bg);
 gboolean         gnome_bg_changes_with_time     (GnomeBG               *bg);
 GdkPixbuf *      gnome_bg_create_frame_thumbnail (GnomeBG              *bg,
 						 GnomeDesktopThumbnailFactory *factory,