diff -r -U12 gnome-screensaver.orig/ChangeLog gnome-screensaver/ChangeLog
--- gnome-screensaver.orig/ChangeLog	2006-05-23 02:55:49.000000000 -0700
+++ gnome-screensaver/ChangeLog	2006-05-23 03:31:38.000000000 -0700
@@ -1,12 +1,22 @@
+2006-05-21  L. David Baron  <dbaron@dbaron.org>
+
+	* savers/gste-slideshow.c:
+	Cache the result of gdk_cairo_set_source_pixbuf as a pattern to
+	avoid calling it for each iteration of a fade.
+	Fixes #339090
+
+	* savers/gste-slideshow.c (draw_iter):
+	Make the fade effect linear by computing delta to use for alpha2.
+
 2006-05-18  William Jon McCann  <mccann@jhu.edu>
 
 	* src/gs-window-x11.c (gs_window_real_motion_notify_event):
 	Increase minimum distance the mouse must move to detect the
 	motion from 10 to 50 pixels.
 
 2006-05-18  William Jon McCann  <mccann@jhu.edu>
 
 	* src/gs-window-x11.c (get_env_vars): Add RUNNING_UNDER_GDM
 	to env var whitelist.  Found in downstream Ubuntu patches.
 
 2006-05-18  William Jon McCann  <mccann@jhu.edu>
diff -r -U12 gnome-screensaver.orig/savers/gste-slideshow.c gnome-screensaver/savers/gste-slideshow.c
--- gnome-screensaver.orig/savers/gste-slideshow.c	2006-05-23 02:55:49.000000000 -0700
+++ gnome-screensaver/savers/gste-slideshow.c	2006-05-23 03:37:15.000000000 -0700
@@ -41,29 +41,31 @@
 #endif /* HAVE_EXIF */
 
 #include "gs-theme-engine.h"
 #include "gste-slideshow.h"
 
 static void     gste_slideshow_class_init (GSTESlideshowClass *klass);
 static void     gste_slideshow_init       (GSTESlideshow      *engine);
 static void     gste_slideshow_finalize   (GObject            *object);
 
 struct GSTESlideshowPrivate
 {
         /* Image at full opacity */
-        GdkPixbuf   *pixbuf1;
+        cairo_pattern_t *pat1;
         /* Image at partial opacity */
-        GdkPixbuf   *pixbuf2;
-        /* Alpha of pixbuf2 */
+        cairo_pattern_t *pat2;
+        /* Alpha of pat2 */
         gdouble      alpha2;
+        /* edges of pat2 */
+        int          pat2top, pat2bottom, pat2left, pat2right;
 
         gint64       fade_ticks;
 
         GThread     *load_thread;
         GAsyncQueue *op_q;
         GAsyncQueue *results_q;
 
         guint        results_pull_id;
         guint        update_image_id;
 
         GSList      *filename_list;
         char        *images_location;
@@ -338,123 +340,154 @@
         /* queue a new load */
         if (show->priv->update_image_id <= 0) {
                 show->priv->update_image_id = g_timeout_add_full (G_PRIORITY_LOW, timeout,
                                                                   (GSourceFunc)push_load_image_func,
                                                                   show, NULL);
         }
 }
 
 static void
 start_fade (GSTESlideshow *show,
             GdkPixbuf     *pixbuf)
 {
-        if (show->priv->pixbuf2) {
-                g_object_unref (show->priv->pixbuf2);
+        int      pw;
+        int      ph;
+        int      x;
+        int      y;
+        cairo_t *cr;
+        int       window_width;
+        int       window_height;
+
+        window_width = show->priv->window_width;
+        window_height = show->priv->window_height;
+
+        if (show->priv->pat2) {
+                cairo_pattern_destroy (show->priv->pat2);
         }
 
-        show->priv->pixbuf2 = g_object_ref (pixbuf);
+        pw = gdk_pixbuf_get_width (pixbuf);
+        ph = gdk_pixbuf_get_height (pixbuf);
+        x = (window_width - pw) / 2;
+        y = (window_height - ph) / 2;
+
+        cr = gdk_cairo_create (GTK_WIDGET (show)->window);
+
+        /* XXX Handle out of memory? */
+        gdk_cairo_set_source_pixbuf (cr, pixbuf, x, y);
+        show->priv->pat2 = cairo_pattern_reference (cairo_get_source (cr));
+        show->priv->pat2top = y;
+        show->priv->pat2bottom = y + ph;
+        show->priv->pat2left = x;
+        show->priv->pat2right = x + pw;
+
+        cairo_destroy (cr);
 
         show->priv->fade_ticks = 0;
 }
 
 static void
 finish_fade (GSTESlideshow *show)
 {
-        if (show->priv->pixbuf1) {
-                g_object_unref (show->priv->pixbuf1);
+        if (show->priv->pat1) {
+                cairo_pattern_destroy (show->priv->pat1);
         }
 
-        show->priv->pixbuf1 = show->priv->pixbuf2;
-        show->priv->pixbuf2 = NULL;
+        show->priv->pat1 = show->priv->pat2;
+        show->priv->pat2 = NULL;
 
         start_new_load (show, IMAGE_LOAD_TIMEOUT);
 }
 
 static void
 update_display (GSTESlideshow *show)
 {
-        int      pw;
-        int      ph;
-        int      x;
-        int      y;
         int      window_width;
         int      window_height;
         cairo_t *cr;
 
         cr = gdk_cairo_create (GTK_WIDGET (show)->window);
 
         gs_theme_engine_get_window_size (GS_THEME_ENGINE (show),
                                          &window_width,
                                          &window_height);
 
-        if (show->priv->pixbuf2) {
-                pw = gdk_pixbuf_get_width (show->priv->pixbuf2);
-                ph = gdk_pixbuf_get_height (show->priv->pixbuf2);
-
-                x = (window_width - pw) / 2;
-                y = (window_height - ph) / 2;
-
+        if (show->priv->pat2) {
                 /* fade out areas not covered by the new image */
                 /* top */
-                cairo_rectangle (cr, 0, 0, window_width, y);
+                cairo_rectangle (cr, 0, 0, window_width, show->priv->pat2top);
                 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
                 cairo_fill (cr);
-                /* left */
-                cairo_rectangle (cr, 0, 0, x, window_height);
+                /* left (excluding what's covered by top and bottom) */
+                cairo_rectangle (cr, 0, show->priv->pat2top,
+                                 show->priv->pat2left,
+                                 show->priv->pat2bottom - show->priv->pat2top);
                 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
                 cairo_fill (cr);
                 /* bottom */
-                cairo_rectangle (cr, 0, y + ph, window_width, window_height);
+                cairo_rectangle (cr, 0, show->priv->pat2bottom, window_width,
+                                 window_height - show->priv->pat2bottom);
                 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
                 cairo_fill (cr);
-                /* right */
-                cairo_rectangle (cr, x + pw, 0, window_width, window_height);
+                /* right (excluding what's covered by top and bottom) */
+                cairo_rectangle (cr, show->priv->pat2right,
+                                 show->priv->pat2top,
+                                 window_width - show->priv->pat2right,
+                                 show->priv->pat2bottom - show->priv->pat2top);
                 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, show->priv->alpha2);
                 cairo_fill (cr);
-                
-                gdk_cairo_set_source_pixbuf (cr,
-                                             show->priv->pixbuf2,
-                                             x, y);
 
+                cairo_set_source (cr, show->priv->pat2);
                 cairo_paint_with_alpha (cr, show->priv->alpha2);
         } else {
-                if (show->priv->pixbuf1) {
-                        pw = gdk_pixbuf_get_width (show->priv->pixbuf1);
-                        ph = gdk_pixbuf_get_height (show->priv->pixbuf1);
-                        x = (window_width - pw) / 2;
-                        y = (window_height - ph) / 2;
-                        
-                        gdk_cairo_set_source_pixbuf (cr,
-                                                     show->priv->pixbuf1,
-                                                     x, y);
+                if (show->priv->pat1) {
+                        cairo_set_source (cr, show->priv->pat1);
                         cairo_paint (cr);
                 }
         }
 
         cairo_destroy (cr);
 }
 
 static gboolean
 draw_iter (GSTESlideshow *show)
 {
-        gdouble      max_alpha = 1.0;
-
-        if (show->priv->pixbuf2) {
+        if (show->priv->pat2) {
                 /* we are in a fade */
                 show->priv->fade_ticks++;
-                show->priv->alpha2 = max_alpha * (double)show->priv->fade_ticks / (double)N_FADE_TICKS;
+
+                /*
+                 * We have currently drawn pat2 with old_opacity, and we
+                 * want to set alpha2 so that drawing pat2 at alpha2
+                 * yields it drawn with new_opacity
+                 *
+                 * Solving
+                 *   new_opacity = 1 - (1 - alpha2) * (1 - old_opacity)
+                 * yields
+                 *   alpha2 = 1 - (1 - new_opacity) / (1 - old_opacity)
+                 *
+                 * XXX This assumes that cairo doesn't correct alpha for
+                 * the color profile.  However, any error is guaranteed
+                 * to be cleaned up by the last iteration, where alpha2
+                 * becomes 1 because new_opacity is 1.
+                 */
+                double old_opacity = (double) (show->priv->fade_ticks - 1) /
+                                     (double) N_FADE_TICKS;
+                double new_opacity = (double) show->priv->fade_ticks /
+                                     (double) N_FADE_TICKS;
+                show->priv->alpha2 = 1.0 - (1.0 - new_opacity) /
+                                           (1.0 - old_opacity);
 
                 update_display (show);
 
-                if (show->priv->alpha2 >= max_alpha) {
+                if (show->priv->fade_ticks >= N_FADE_TICKS) {
                         finish_fade (show);
                 }
         }
 
         return TRUE;
 }
 
 static void
 process_new_pixbuf (GSTESlideshow *show,
                     GdkPixbuf     *pixbuf)
 {
         if (pixbuf) {
