[PATCH] Use GdkPixbufLoader for thumbnailer to reduce memory consumption



The attached patch ports the nautilus image loading code to
GdkPixbufLoader, which ensures that less memory is consumed for large
images. The old thumbnailing code was also not very traceable wrt frame
addition.

-- 
Christian Neumair <chris gnome-de org>
Index: libnautilus-private/nautilus-icon-factory.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-icon-factory.c,v
retrieving revision 1.325
diff -u -p -r1.325 nautilus-icon-factory.c
--- libnautilus-private/nautilus-icon-factory.c	22 Mar 2006 17:41:32 -0000	1.325
+++ libnautilus-private/nautilus-icon-factory.c	7 Apr 2006 13:30:05 -0000
@@ -1110,30 +1110,6 @@ path_represents_svg_image (const char *p
 }
 
 static GdkPixbuf *
-scale_icon (GdkPixbuf *pixbuf,
-	    double *scale)
-{
-	guint width, height;
-
-	width = gdk_pixbuf_get_width (pixbuf);
-	height = gdk_pixbuf_get_height (pixbuf);
-
-	if ((int) (width * *scale) > NAUTILUS_ICON_MAXIMUM_SIZE ||
-	    (int) (height * *scale) > NAUTILUS_ICON_MAXIMUM_SIZE) {
-		*scale = MIN ((double) NAUTILUS_ICON_MAXIMUM_SIZE / width,
-			      (double) NAUTILUS_ICON_MAXIMUM_SIZE / height);
-	}
-
-	width = floor (width * *scale + 0.5);
-	height = floor (height * *scale + 0.5);
-	
-	return gdk_pixbuf_scale_simple (pixbuf,
-					width == 0 ? 1 : width,
-					height == 0 ? 1 : height,
-					GDK_INTERP_BILINEAR);
-}
-
-static GdkPixbuf *
 load_icon_file (const char    *filename,
 		guint          base_size,
 		guint          nominal_size,
@@ -1141,9 +1117,8 @@ load_icon_file (const char    *filename,
 		double        *scale_x,
 		double        *scale_y)
 {
-	GdkPixbuf *pixbuf, *scaled_pixbuf;
-	int width, height, size;
-	double scale;
+	GdkPixbuf *pixbuf;
+	int size;
 	gboolean is_thumbnail;
         gboolean add_frame = FALSE;
 
@@ -1156,53 +1131,24 @@ load_icon_file (const char    *filename,
 					  force_nominal ? 0 : base_size,
 					  scale_x, scale_y);
 	} else {
-		is_thumbnail = strstr (filename, "/.thumbnails/")  != NULL;
-
 		/* FIXME: Maybe we shouldn't have to load the file each time
 		 * Not sure if that is important */
-		if (is_thumbnail) {
-			pixbuf = nautilus_thumbnail_load_framed_image (filename);
-		} else {
-			pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
-		}
+		pixbuf = nautilus_thumbnail_load_image (filename,
+							base_size,
+							nominal_size,
+							force_nominal,
+							scale_x,
+							scale_y);
+
+		is_thumbnail = strstr (filename, "/.thumbnails/") != NULL;
 
 		if (pixbuf == NULL) {
 			return NULL;
 		}
-		
-		if (force_nominal) {
-			width = gdk_pixbuf_get_width (pixbuf); 
-			height = gdk_pixbuf_get_height (pixbuf);
-			base_size = MAX (width, height);                        
-		} else if (base_size == 0) {
-			if (is_thumbnail) {
-				base_size = 128 * NAUTILUS_ICON_SIZE_STANDARD / NAUTILUS_ICON_SIZE_THUMBNAIL;
-			} else {
-				width = gdk_pixbuf_get_width (pixbuf); 
-				height = gdk_pixbuf_get_height (pixbuf);
-				size = MAX (width, height);
-                                if (size > NAUTILUS_ICON_SIZE_THUMBNAIL && !gdk_pixbuf_get_has_alpha(pixbuf)) {
-                                        add_frame=TRUE;
-                                }
-                                if (size >  nominal_size * NAUTILUS_ICON_SIZE_THUMBNAIL / NAUTILUS_ICON_SIZE_STANDARD) {
-                                        base_size = size * NAUTILUS_ICON_SIZE_STANDARD / NAUTILUS_ICON_SIZE_THUMBNAIL;
-                                }
-                                else if (size > NAUTILUS_ICON_SIZE_STANDARD) {
-					base_size = nominal_size;
-				} else {
-					/* Don't scale up small icons */
-					base_size = NAUTILUS_ICON_SIZE_STANDARD;
-				}
-			}
-		}
-		
-		if (base_size != nominal_size) {
-			scale = (double)nominal_size/base_size;
-			scaled_pixbuf = scale_icon (pixbuf, &scale);
-			*scale_x = scale;
-			*scale_y = scale;
-			g_object_unref (pixbuf);
-			pixbuf = scaled_pixbuf;
+
+		size = MAX (gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf));
+		if ((is_thumbnail || size > NAUTILUS_ICON_SIZE_THUMBNAIL) && !gdk_pixbuf_get_has_alpha(pixbuf)) {
+			add_frame=TRUE;
 		}
 	}
 
Index: libnautilus-private/nautilus-thumbnails.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-thumbnails.c,v
retrieving revision 1.56
diff -u -p -r1.56 nautilus-thumbnails.c
--- libnautilus-private/nautilus-thumbnails.c	23 Nov 2005 13:29:34 -0000	1.56
+++ libnautilus-private/nautilus-thumbnails.c	7 Apr 2006 13:30:06 -0000
@@ -30,6 +30,7 @@
 #include "nautilus-global-preferences.h"
 #include "nautilus-icon-factory-private.h"
 #include "nautilus-icon-factory.h"
+#include <math.h>
 #include <eel/eel-gdk-pixbuf-extensions.h>
 #include <eel/eel-graphic-effects.h>
 #include <eel/eel-string.h>
@@ -263,21 +264,138 @@ nautilus_thumbnail_frame_image (GdkPixbu
 	*pixbuf=pixbuf_with_frame;
 }
 
-/* routine to load an image from the passed-in path, and then embed it in
- * a frame if necessary
+typedef struct {
+	gboolean is_thumbnail;
+	guint base_size;
+	guint nominal_size;
+	gboolean force_nominal;
+	unsigned int original_height;
+	unsigned int original_width;
+	double *scale_x_out;
+	double *scale_y_out;
+} ThumbnailLoadArgs;
+
+static void
+thumbnail_loader_size_prepared (GdkPixbufLoader *loader,
+				unsigned int width,
+				unsigned int height,
+				ThumbnailLoadArgs *args)
+{
+	int size = MAX (width, height);
+
+	args->original_height = height;
+	args->original_width = width;
+
+	if (args->force_nominal) {
+		args->base_size = MAX (width, height);                        
+	} else if (args->base_size == 0) {
+		if (args->is_thumbnail) {
+			args->base_size = 128 * NAUTILUS_ICON_SIZE_STANDARD / NAUTILUS_ICON_SIZE_THUMBNAIL;
+		} else {
+			if (size > args->nominal_size * NAUTILUS_ICON_SIZE_THUMBNAIL / NAUTILUS_ICON_SIZE_STANDARD) {
+				args->base_size = size * NAUTILUS_ICON_SIZE_STANDARD / NAUTILUS_ICON_SIZE_THUMBNAIL;
+			} else if (size > NAUTILUS_ICON_SIZE_STANDARD) {
+				args->base_size = args->nominal_size;
+			} else {
+				/* Don't scale up small icons */
+				args->base_size = NAUTILUS_ICON_SIZE_STANDARD;
+			}
+		}
+	}
+
+	if (args->base_size != args->nominal_size) {
+		double scale;
+
+		scale = (double)args->nominal_size/args->base_size;
+
+		if ((int) (width * scale) > NAUTILUS_ICON_MAXIMUM_SIZE ||
+		    (int) (height * scale) > NAUTILUS_ICON_MAXIMUM_SIZE) {
+			scale = MIN ((double) NAUTILUS_ICON_MAXIMUM_SIZE / width,
+				     (double) NAUTILUS_ICON_MAXIMUM_SIZE / height);
+		}
+
+		width = MAX (1, floor (width * scale + 0.5));
+		height = MAX (1, floor (height * scale + 0.5));
+
+		gdk_pixbuf_loader_set_size (loader, (int) width, (int) height);
+	}
+
+}
+
+static void
+thumbnail_loader_area_prepared (GdkPixbufLoader *loader,
+				ThumbnailLoadArgs *args)
+{
+	GdkPixbuf *pixbuf;
+
+	pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
+
+	*args->scale_x_out = gdk_pixbuf_get_width (pixbuf) / args->original_width;
+	*args->scale_y_out = gdk_pixbuf_get_height (pixbuf) / args->original_height;
+}
+
+/* routine to load an image from the passed-in path
  */
 GdkPixbuf *
-nautilus_thumbnail_load_framed_image (const char *path)
+nautilus_thumbnail_load_image (const char *path,
+			       guint base_size,
+			       guint nominal_size,
+			       gboolean force_nominal,
+			       double *scale_x_out,
+			       double *scale_y_out)
 {
+	guchar *buffer;
+	GdkPixbufLoader *loader;
 	GdkPixbuf *pixbuf;
-	
-	pixbuf = gdk_pixbuf_new_from_file (path, NULL);
-	if (pixbuf == NULL) {
+	GError *error = NULL;
+	gsize buflen;
+	ThumbnailLoadArgs args;
+
+	if (!g_file_get_contents (path, (gchar **) &buffer, &buflen, &error)) {
+		g_message ("Failed to load %s into memory: %s", path, error->message);
+		g_error_free (error);
 		return NULL;
 	}
-	if (!gdk_pixbuf_get_has_alpha (pixbuf)) {
-		nautilus_thumbnail_frame_image (&pixbuf);
+
+	loader = gdk_pixbuf_loader_new ();
+	g_signal_connect (loader, "size-prepared",
+			  G_CALLBACK (thumbnail_loader_size_prepared),
+			  &args);
+	g_signal_connect (loader, "area-prepared",
+			  G_CALLBACK (thumbnail_loader_area_prepared),
+			  &args);
+
+	args.is_thumbnail = strstr (path, "/.thumbnails/") != NULL;
+	args.base_size = base_size;
+	args.nominal_size = nominal_size;
+	args.force_nominal = force_nominal;
+	args.scale_x_out = scale_x_out;
+	args.scale_y_out = scale_y_out;
+
+	if (!gdk_pixbuf_loader_write (loader, buffer, buflen, &error)) {
+		/* error might be NULL although FALSE is returned, bug #337611 */
+		g_message ("Failed to write %s to thumbnail pixbuf loader: %s", path, error ? error->message : "");
+		gdk_pixbuf_loader_close (loader, NULL);
+		g_object_unref (G_OBJECT (loader));
+		if (error != NULL);
+			g_error_free (error);
+		g_free (buffer);
+		return NULL;
+	}
+
+	if (!gdk_pixbuf_loader_close (loader, &error)) {
+		g_message ("Failed to close thumbnail pixbuf loader for %s: %s", path, error->message);
+		g_object_unref (G_OBJECT (loader));
+		g_error_free (error);
+		g_free (buffer);
+		return NULL;
 	}
+
+	pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
+
+	g_object_unref (G_OBJECT (loader));
+	g_free (buffer);
+
         return pixbuf;
 }
 
Index: libnautilus-private/nautilus-thumbnails.h
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-private/nautilus-thumbnails.h,v
retrieving revision 1.12
diff -u -p -r1.12 nautilus-thumbnails.h
--- libnautilus-private/nautilus-thumbnails.h	19 Sep 2005 13:51:16 -0000	1.12
+++ libnautilus-private/nautilus-thumbnails.h	7 Apr 2006 13:30:06 -0000
@@ -31,7 +31,12 @@
 /* Returns NULL if there's no thumbnail yet. */
 void       nautilus_create_thumbnail                (NautilusFile *file);
 void       nautilus_thumbnail_frame_image           (GdkPixbuf **pixbuf);
-GdkPixbuf *nautilus_thumbnail_load_framed_image     (const char   *path);
+GdkPixbuf *nautilus_thumbnail_load_image            (const char *path,
+						     guint       base_size,
+						     guint       nominal_size,
+						     gboolean    force_nominal,
+						     double     *scale_x_out,
+						     double     *scale_y_out);
 void       nautilus_update_thumbnail_file_copied    (const char   *source_file_uri,
 						     const char   *destination_file_uri);
 void       nautilus_update_thumbnail_file_renamed   (const char   *source_file_uri,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]