Re: patch nag: Multithreaded thumbnail loading



Am Montag, den 03.09.2007, 13:15 +0200 schrieb Alexander Larsson:
> There is a limited amount of threads loading the thumbnails, therefore
> it makes sense to load the ones that are actually visible first. We
> might not actually use the precise code in
> nautilus_thumbnail_prioritize(), but at least it would hooks into the
> same places.

Thanks for your fruitful comments. I've implemented the relevant changes
you requested.


In my local tree, I added an async variant of
nautilus_thumbnail_load_image(). We only seem to be able to set the
priority when the thumbnail is requested, and lack a way to modify the
priority of running asyncronous GnomeVFS request ("running" as of "has a
handle", but still I/O to do).

If we have such a job run-time priorization mechanism (GVFS?), we should
be able to just change the thumbnailing priority in a function that can
be called from the _prioritize() function. Are you OK with that?


Once we have this one in, I'll try to implement user-driven thumbnailing
cancellation. I remember I once provided a patch that never made it into
trunk, for displaying status bar messages on file hovering. I'll
probably recycle the patch, and use it to notify the users that we're
loading thumbnails - otherwise users cannot be sure that the folder
itself has been loaded already.


It may be a good idea to add a status bar message priority system, to
implement notifications regarding folder operations (loading etc.). One
would would be able to push messages on a stack and pop it from it, and
give them a priority, and we can have separate priorities for basic
file/folder information (_LOW), file operations (_NORMAL) and tooltips
(_HIGH), that decide which one is actually shown. It would be some form
of gtk_statusbar_push and gtk_statusbar_pop wrapper that reorders the
messages according to their priority.

-- 
Christian Neumair <cneumair gnome org>
Index: libnautilus-private/nautilus-icon-factory.c
===================================================================
--- libnautilus-private/nautilus-icon-factory.c	(Revision 13124)
+++ libnautilus-private/nautilus-icon-factory.c	(Arbeitskopie)
@@ -29,6 +29,7 @@
 #include "nautilus-icon-factory.h"
 
 #include "nautilus-default-file-icon.h"
+#include "nautilus-directory-notify.h"
 #include "nautilus-file-attributes.h"
 #include "nautilus-file-private.h"
 #include "nautilus-file-utilities.h"
@@ -154,6 +155,7 @@ typedef struct {
 	CacheIcon *fallback_icon;
 	GHashTable *image_mime_types;
 
+	GList *async_thumbnail_load_handles;
 } NautilusIconFactory;
 
 #define NAUTILUS_ICON_FACTORY(obj) \
@@ -347,6 +349,64 @@ load_thumbnail_frame (NautilusIconFactor
 	g_free (image_path);
 }
 
+typedef struct {
+	NautilusFile *file;
+	char *modifier;
+	guint nominal_size;
+	gboolean force_nominal;
+} AsnycThumbnailLoadFuncData;
+
+static void
+async_thumbnail_load_func (NautilusThumbnailAsyncLoadHandle *handle,
+			   const char *path,
+			   GdkPixbuf  *pixbuf,
+			   double scale_x,
+			   double scale_y,
+			   gpointer user_data)
+{
+	NautilusIconFactory *factory;
+	GHashTable *hash_table;
+	CacheKey *key;
+	CacheIcon *cached_icon;
+	struct stat statbuf;
+	AsnycThumbnailLoadFuncData *data = user_data;
+
+	factory = get_icon_factory ();
+	hash_table = factory->icon_cache;
+
+	nautilus_file_set_is_thumbnailing (data->file, FALSE);
+	factory->async_thumbnail_load_handles = 
+		g_list_remove (factory->async_thumbnail_load_handles, handle);
+
+	if (stat (path, &statbuf) != 0 ||
+	    !S_ISREG (statbuf.st_mode)) {
+		g_message ("NautilusIconFactory: Failed to determine mtime for %s. Aborting thumbnailing request.", path);
+		goto out;
+	}
+
+	cached_icon = cache_icon_new (pixbuf, NULL, scale_x, scale_y);
+	cached_icon->mtime = statbuf.st_mtime;
+
+	if (cached_icon != NULL) {
+		key = g_new (CacheKey, 1);
+		key->name = g_strdup (path);
+		key->modifier = g_strdup (data->modifier);
+		key->nominal_size = data->nominal_size;
+		key->force_nominal = data->force_nominal;
+
+		g_hash_table_insert (hash_table, key, cached_icon);
+
+		nautilus_file_changed (data->file);
+	}
+
+out:
+	nautilus_file_unref (data->file);
+	g_free (data->modifier);
+	g_free (data);
+}
+
+
+
 static void
 nautilus_icon_factory_instance_init (NautilusIconFactory *factory)
 {
@@ -685,12 +745,23 @@ nautilus_icon_factory_clear (void)
 }
 
 static void
+cancel_thumbnail_read_foreach (gpointer data,
+			       gpointer user_data)
+{
+	NautilusThumbnailAsyncLoadHandle *handle = data;
+	nautilus_thumbnail_load_image_cancel (handle);
+}
+
+static void
 nautilus_icon_factory_finalize (GObject *object)
 {
 	NautilusIconFactory *factory;
 
 	factory = NAUTILUS_ICON_FACTORY (object);
 
+	g_list_foreach (factory->async_thumbnail_load_handles, cancel_thumbnail_read_foreach, NULL);
+	g_list_free (factory->async_thumbnail_load_handles);
+
 	if (factory->icon_cache) {
 		g_hash_table_destroy (factory->icon_cache);
 		factory->icon_cache = NULL;
@@ -1300,6 +1371,38 @@ create_normal_cache_icon (const char *ic
 	return cache_icon;
 }
 
+static CacheIcon *
+lookup_icon_from_cache (const char *icon,
+			const char *modifier,
+			guint       nominal_size,
+			gboolean    force_nominal)
+{
+	NautilusIconFactory *factory;
+	GHashTable *hash_table;
+	CacheKey lookup_key, *key;
+	CacheIcon *value;
+
+	lookup_key.name = (char *)icon;
+	lookup_key.modifier = (char *)modifier;
+	lookup_key.nominal_size = nominal_size;
+	lookup_key.force_nominal = force_nominal;
+
+	factory = get_icon_factory ();
+	hash_table = factory->icon_cache;
+
+	if (g_hash_table_lookup_extended (hash_table, &lookup_key,
+					  (gpointer *) &key, (gpointer *) &value)) {
+		/* Found it in the table. */
+		g_assert (key != NULL);
+		g_assert (value != NULL);
+	} else {
+		key = NULL;
+		value = NULL;
+	}
+
+	return value;
+}
+			
 
 /* Get the icon, handling the caching.
  * If @picky is true, then only an unscaled icon is acceptable.
@@ -1316,34 +1419,17 @@ get_icon_from_cache (const char *icon,
 {
 	NautilusIconFactory *factory;
 	GHashTable *hash_table;
-	CacheKey lookup_key;
 	CacheKey *key;
 	CacheIcon *cached_icon;
-	gpointer key_in_table, value;
 	struct stat statbuf;
 	
 	g_return_val_if_fail (icon != NULL, NULL);
-
-	key = NULL;
-	cached_icon = NULL;
 	
 	factory = get_icon_factory ();
 	hash_table = factory->icon_cache;
 
 	/* Check to see if it's already in the table. */
-	lookup_key.name = (char *)icon;
-	lookup_key.modifier = (char *)modifier;
-	lookup_key.nominal_size = nominal_size;
-	lookup_key.force_nominal = force_nominal;
-
-	if (g_hash_table_lookup_extended (hash_table, &lookup_key,
-					  &key_in_table, &value)) {
-		/* Found it in the table. */
-		g_assert (key_in_table != NULL);
-		g_assert (value != NULL);
-		key = key_in_table;
-		cached_icon = value;
-	}
+	cached_icon = lookup_icon_from_cache (icon, modifier, nominal_size, force_nominal);
 
 	/* Make sure that thumbnails and image-as-itself icons gets
 	   reloaded when they change: */
@@ -1547,10 +1633,85 @@ nautilus_get_relative_icon_size_for_zoom
 	return (float)nautilus_get_icon_size_for_zoom_level (zoom_level) / NAUTILUS_ICON_SIZE_STANDARD;
 }
 
-
-
 /* Convenience cover for nautilus_icon_factory_get_icon_for_file
  * and nautilus_icon_factory_get_pixbuf_for_icon.
+ *
+ * If a file has an associated thumbnail, the thumb is loaded asynchronously,
+ * a loading thumbnail image is returned
+ * and the file will receive a "changed" event once the thumbnail has been loaded.
+ *
+ * The "file" parameter is only used for thumbnailing,
+ * for the file change notification once the actual thumbnail
+ * has been loaded.
+ */
+GdkPixbuf *
+nautilus_icon_factory_get_pixbuf_for_file_with_icon (NautilusFile                *file,
+						     const char                  *icon,
+						     const char                  *modifier,
+						     guint                        size_in_pixels,
+						     NautilusEmblemAttachPoints  *attach_points,
+						     GdkRectangle                *embedded_text_rect,
+						     gboolean                     force_size,
+						     gboolean                     wants_default,
+						     char                       **display_name)
+{
+	GdkPixbuf *pixbuf;
+	NautilusIconFactory *factory;
+	gboolean is_thumbnail;
+
+	factory = get_icon_factory ();
+
+	is_thumbnail = strstr (icon, "/.thumbnails/") != NULL;
+
+	if (is_thumbnail &&
+	    !lookup_icon_from_cache (icon, modifier, size_in_pixels, force_size)) {
+		AsnycThumbnailLoadFuncData *data;
+
+		/* Asynchronous thumbnail loading.
+ 		 * 
+		 * This heavily improves performance for folders containing lots of
+		 * previously thumbnailed files.
+		 *
+		 * Note: We do not pass the additional thumbnail parameters (attach points etc.)
+		 * to the thread as we don't need them for the cache. The API user may herself
+		 * re-request the loaded thumbnail with the correct parameters, which will be set
+		 * accordingly in nautilus_icon_factory_get_pixbuf_for_icon() on cache hit
+		 * once it is filled.
+		 */
+
+		data = g_new (AsnycThumbnailLoadFuncData, 1);
+		data->file = nautilus_file_ref (file);
+		data->modifier = g_strdup (modifier);
+		data->nominal_size = size_in_pixels;
+		data->force_nominal = force_size;
+
+		nautilus_file_set_is_thumbnailing (file, TRUE);
+
+		factory->async_thumbnail_load_handles = g_list_prepend (
+			factory->async_thumbnail_load_handles,
+			nautilus_thumbnail_load_image_async (icon,
+							     0, /* base_size */
+							     size_in_pixels,
+							     force_size,
+							     async_thumbnail_load_func,
+							     data));
+
+		icon = ICON_NAME_THUMBNAIL_LOADING;
+	}
+
+
+	pixbuf = nautilus_icon_factory_get_pixbuf_for_icon (icon,
+							    modifier, size_in_pixels,
+							    attach_points, embedded_text_rect,
+							    force_size,
+							    wants_default, display_name);
+
+	return pixbuf;
+}
+
+/*
+ * like nautilus_icon_factory_get_pixbuf_for_file_with_icon() but does the icon lookup itself,
+ * doesn't allow emblem and text rect fetching.
  */
 GdkPixbuf *
 nautilus_icon_factory_get_pixbuf_for_file (NautilusFile *file,
@@ -1558,9 +1719,11 @@ nautilus_icon_factory_get_pixbuf_for_fil
 					   guint size_in_pixels,
 					   gboolean force_size)
 {
-	char *icon;
 	GdkPixbuf *pixbuf;
+	NautilusIconFactory *factory;
+	char *icon;
 
+	factory = get_icon_factory ();
 
 	/* Get the pixbuf for this file. */
 	icon = nautilus_icon_factory_get_icon_for_file (file, FALSE);
@@ -1568,12 +1731,12 @@ nautilus_icon_factory_get_pixbuf_for_fil
 		return NULL;
 	}
 
-	pixbuf = nautilus_icon_factory_get_pixbuf_for_icon (icon, modifier,
-							    size_in_pixels,
-							    NULL, NULL,
-							    force_size,
-							    TRUE, NULL);
-	
+	pixbuf = nautilus_icon_factory_get_pixbuf_for_file_with_icon (file,
+								      icon, modifier,
+								      size_in_pixels,
+								      NULL, NULL,
+								      force_size,
+								      TRUE, NULL);
 	g_free (icon);
 
 	return pixbuf;
@@ -1586,7 +1749,7 @@ nautilus_icon_factory_get_pixbuf_for_fil
 {
 	return nautilus_icon_factory_get_pixbuf_for_file (file, modifier,
 							  gtk_icon_size_to_nominal_size (stock_size),
-							  TRUE); /* force_size */
+							  TRUE /* force_size */);
 
 }
 
Index: libnautilus-private/nautilus-icon-factory.h
===================================================================
--- libnautilus-private/nautilus-icon-factory.h	(Revision 13124)
+++ libnautilus-private/nautilus-icon-factory.h	(Arbeitskopie)
@@ -151,6 +151,16 @@ GdkPixbuf *nautilus_icon_factory_get_pix
 								      const char   *modifier,
 								      GtkIconSize   stock_size);
 
+GdkPixbuf * nautilus_icon_factory_get_pixbuf_for_file_with_icon (NautilusFile               *file,
+								 const char                 *icon,
+								 const char                 *modifier,
+								 guint                       size_in_pixels,
+								 NautilusEmblemAttachPoints *attach_points,
+								 GdkRectangle               *embedded_text_rect,
+								 gboolean                    force_size,
+								 gboolean                    wants_default,
+								 char                      **display_name);
+
 
 /* Convenience routine for getting a pixbuf from an icon name
  */
Index: libnautilus-private/nautilus-icon-container.c
===================================================================
--- libnautilus-private/nautilus-icon-container.c	(Revision 13124)
+++ libnautilus-private/nautilus-icon-container.c	(Arbeitskopie)
@@ -5678,6 +5678,90 @@ handle_vadjustment_changed (GtkAdjustmen
 	nautilus_icon_container_update_visible_icons (container);
 }
 
+/*
+ * used to resize ICON_NAME_THUMBNAIL_LOADING to the expected thumbnail size.
+ */
+static void
+sanitize_loading_thumbnail_image_size (NautilusIconContainer *container,
+				       const char *mime_type,
+				       GdkPixbuf **image,
+				       NautilusEmblemAttachPoints *attach_points,
+				       GdkRectangle *embedded_text_rect)
+{
+	NautilusIconContainerDetails *details;
+	double pixels_per_unit;
+
+	details = container->details;
+	pixels_per_unit = (double) nautilus_get_icon_size_for_zoom_level (container->details->zoom_level)
+		/ NAUTILUS_ICON_SIZE_STANDARD;
+
+	if (gdk_pixbuf_get_width (*image) < NAUTILUS_ICON_SIZE_THUMBNAIL * pixels_per_unit &&
+	    gdk_pixbuf_get_height (*image) < NAUTILUS_ICON_SIZE_THUMBNAIL * pixels_per_unit) {
+		/* TODO? this only handles icons smaller than the expected thumbnail size ATM.
+		 * Should not be a common problem, though */
+		GdkPixbuf *new_image;
+		double x_size;
+		double y_size;
+		double x_offset;
+		double y_offset;
+		int i;
+
+		if (g_str_has_prefix (mime_type, "video/")) {
+			/* assume 4:3 aspect ratio for videos i.e. we'll always occupy the full width. */
+			x_size = NAUTILUS_ICON_SIZE_THUMBNAIL * pixels_per_unit;
+			y_size = 3./4 * x_size;
+		} else {
+			/* scale up to the max. thumbnail size.
+			 * This is correct at least in one dimension, and prevents the icons from jumping
+			 * around as the thumbnail is created, if it is tall for text below icon, and if it
+			 * is wide for text beside icon.
+			 */
+			x_size = NAUTILUS_ICON_SIZE_THUMBNAIL * pixels_per_unit;
+			y_size = NAUTILUS_ICON_SIZE_THUMBNAIL * pixels_per_unit;
+		}
+
+		/* maybe the estimated size was smaller than the input pixbuf, so size the surrounding
+ 		 * image up. This only seems to be relevant in the 4:3 case, for y_size.
+		 */
+		x_size = MAX (x_size, gdk_pixbuf_get_width (*image));
+		y_size = MAX (y_size, gdk_pixbuf_get_height (*image));
+
+		x_offset = x_size - gdk_pixbuf_get_width (*image);
+		y_offset = y_size - gdk_pixbuf_get_height (*image);
+
+		/* center wrt "minor" dimension, i.e. horizontally for text below
+		 * and vertically for text besides icon */
+		if (details->label_position == NAUTILUS_ICON_LABEL_POSITION_BESIDE)
+			y_offset /= 2;
+		else 
+			x_offset /= 2;
+
+		new_image = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE,
+					    gdk_pixbuf_get_bits_per_sample (*image),
+					    x_size, y_size);
+
+		gdk_pixbuf_fill (new_image, 0x00000000);
+		gdk_pixbuf_copy_area (*image,
+				      0, 0,
+				      gdk_pixbuf_get_width (*image),
+				      gdk_pixbuf_get_height (*image),
+				      new_image,
+				      x_offset, y_offset);
+
+		g_object_unref (*image);
+		*image = new_image;
+
+		for (i = 0; i < attach_points->num_points; i++) {
+			attach_points->points[i].x += x_offset;
+			attach_points->points[i].y += y_offset;
+		}
+
+		embedded_text_rect->x += x_offset;
+		embedded_text_rect->y += y_offset;
+	}
+}
+
+
 void 
 nautilus_icon_container_update_icon (NautilusIconContainer *container,
 				     NautilusIcon *icon)
@@ -5732,15 +5816,14 @@ nautilus_icon_container_update_icon (Nau
 		modifier = "accept";
 	}
 	
-	pixbuf = nautilus_icon_factory_get_pixbuf_for_icon
-		(icon_name,
+	pixbuf = nautilus_icon_factory_get_pixbuf_for_file_with_icon
+		((NautilusFile *) icon->data,
+		 icon_name,
 		 modifier,
 		 icon_size,
 		 &attach_points,
 		 &embedded_text_rect,
 		 FALSE, TRUE, NULL);
-	
-	g_free (icon_name);
 
 	if (embedded_text_rect.width > MINIMUM_EMBEDDED_TEXT_RECT_WIDTH &&
 	    embedded_text_rect.height > MINIMUM_EMBEDDED_TEXT_RECT_HEIGHT &&
@@ -5794,6 +5877,17 @@ nautilus_icon_container_update_icon (Nau
 			     "additional_text", additional_text,
 			     "highlighted_for_drop", icon == details->drop_target,
 			     NULL);
+
+	if (nautilus_file_is_thumbnailing ((NautilusFile *) icon->data)) {
+		char* mime_type;
+		mime_type = nautilus_file_get_mime_type ((NautilusFile *)icon->data);
+		sanitize_loading_thumbnail_image_size (container,
+						       mime_type,
+						       &pixbuf,
+						       &attach_points,
+						       &embedded_text_rect);
+		g_free (mime_type);
+	}
 	
 	nautilus_icon_canvas_item_set_image (icon->item, pixbuf);
 	nautilus_icon_canvas_item_set_attach_points (icon->item, &attach_points);
@@ -5807,6 +5901,8 @@ nautilus_icon_container_update_icon (Nau
 
 	g_free (editable_text);
 	g_free (additional_text);
+
+	g_free (icon_name);
 }
 
 static gboolean
Index: libnautilus-private/nautilus-thumbnails.c
===================================================================
--- libnautilus-private/nautilus-thumbnails.c	(Revision 13124)
+++ libnautilus-private/nautilus-thumbnails.c	(Arbeitskopie)
@@ -69,6 +69,16 @@ typedef struct {
 	time_t original_file_mtime;
 } NautilusThumbnailInfo;
 
+struct NautilusThumbnailAsyncLoadHandle {
+	EelReadFileHandle *eel_read_handle;
+	char *file_path;
+	guint base_size;
+	guint nominal_size;
+	gboolean force_nominal; 
+	NautilusThumbnailAsyncLoadFunc load_func;
+	gpointer load_func_user_data;
+};
+
 
 /*
  * Thumbnail thread state.
@@ -330,39 +340,26 @@ thumbnail_loader_area_prepared (GdkPixbu
 	*args->scale_y_out = (double) gdk_pixbuf_get_height (pixbuf) / args->original_height;
 }
 
-/* routine to load an image from the passed-in 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)
+static GdkPixbuf *
+get_pixbuf_from_data (const unsigned char *buffer,
+		      gsize buflen,
+		      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;
-	GError *error;
-	gsize buflen;
 	ThumbnailLoadArgs args;
-
-	error = NULL;
+	GError *error;
 
 	if (thumbnail_icon_size == 0) {
 		eel_preferences_add_auto_integer (NAUTILUS_PREFERENCES_ICON_VIEW_THUMBNAIL_SIZE,
 						  &thumbnail_icon_size);
 	}
 
-	
-	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;
-	}
-
 	loader = gdk_pixbuf_loader_new ();
 	g_signal_connect (loader, "size-prepared",
 			  G_CALLBACK (thumbnail_loader_size_prepared),
@@ -378,17 +375,20 @@ nautilus_thumbnail_load_image (const cha
 	args.scale_x_out = scale_x_out;
 	args.scale_y_out = scale_y_out;
 
+	error = NULL;
+
 	if (!gdk_pixbuf_loader_write (loader, buffer, buflen, &error)) {
 		g_message ("Failed to write %s to thumbnail pixbuf loader: %s", path, error->message);
 
 		gdk_pixbuf_loader_close (loader, NULL);
 		g_object_unref (G_OBJECT (loader));
 		g_error_free (error);
-		g_free (buffer);
 
 		return NULL;
 	}
 
+	error = NULL;
+
 	if (!gdk_pixbuf_loader_close (loader, &error) ||
 	    /* Seems we have to check this even if it returned TRUE (#403255) */
 	    error != NULL) {
@@ -396,7 +396,6 @@ nautilus_thumbnail_load_image (const cha
 
 		g_object_unref (G_OBJECT (loader));
 		g_error_free (error);
-		g_free (buffer);
 
 		return NULL;
 	}
@@ -404,11 +403,122 @@ nautilus_thumbnail_load_image (const cha
 	pixbuf = g_object_ref (gdk_pixbuf_loader_get_pixbuf (loader));
 
 	g_object_unref (G_OBJECT (loader));
+
+	return pixbuf;
+}
+
+
+/* routine to load an image from the passed-in 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)
+{
+	GdkPixbuf *pixbuf;
+	guchar *buffer;
+	gsize buflen;
+	GError *error;
+
+	error = NULL;
+	
+	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;
+	}
+
+	pixbuf = get_pixbuf_from_data (buffer, buflen, path,
+				       base_size, nominal_size, force_nominal,
+				       scale_x_out, scale_y_out);
+
 	g_free (buffer);
 
 	return pixbuf;
 }
 
+static void
+async_thumbnail_read_image (GnomeVFSResult result,
+			    GnomeVFSFileSize file_size,
+			    char *file_contents,
+			    gpointer callback_data)
+{
+	GdkPixbuf *pixbuf;
+	double scale_x, scale_y;
+
+	NautilusThumbnailAsyncLoadHandle *handle = callback_data;
+
+	pixbuf = NULL;
+	scale_x = scale_y = 1.0;
+
+	if (result == GNOME_VFS_OK) {
+		pixbuf = get_pixbuf_from_data (file_contents, file_size,
+					       handle->file_path,
+					       handle->base_size,
+					       handle->nominal_size,
+					       handle->force_nominal,
+					       &scale_x, &scale_y);
+	}
+
+	handle->load_func (handle,
+			   handle->file_path,
+			   pixbuf, scale_x, scale_y,
+			   handle->load_func_user_data);
+
+	gdk_pixbuf_unref (pixbuf);
+
+	g_free (handle->file_path);
+	g_free (handle);
+}
+
+NautilusThumbnailAsyncLoadHandle *
+nautilus_thumbnail_load_image_async (const char *path,
+				     guint base_size,
+				     guint nominal_size,
+				     gboolean force_nominal,
+				     NautilusThumbnailAsyncLoadFunc load_func,
+				     gpointer load_func_user_data)
+{
+	NautilusThumbnailAsyncLoadHandle *handle;
+	char *uri;
+
+	uri = gnome_vfs_get_uri_from_local_path (path);
+	if (uri == NULL) {
+		return NULL;
+	}
+
+	handle = g_new (NautilusThumbnailAsyncLoadHandle, 1);
+	handle->eel_read_handle =
+		eel_read_entire_file_async (uri, GNOME_VFS_PRIORITY_DEFAULT,
+					    (EelReadFileCallback) async_thumbnail_read_image,
+					   handle);
+	handle->file_path = g_strdup (path);
+	handle->base_size = base_size;
+	handle->nominal_size = nominal_size;
+	handle->force_nominal = force_nominal;
+	handle->load_func = load_func;
+	handle->load_func_user_data = load_func_user_data;
+
+	g_free (uri);
+
+	return handle;
+}
+
+void
+nautilus_thumbnail_load_image_cancel (NautilusThumbnailAsyncLoadHandle *handle)
+{
+	g_assert (handle != NULL);
+
+	eel_read_file_cancel (handle->eel_read_handle);
+	g_free (handle->file_path);
+	g_free (handle);
+}
+
 void
 nautilus_thumbnail_remove_from_queue (const char *file_uri)
 {
Index: libnautilus-private/nautilus-thumbnails.h
===================================================================
--- libnautilus-private/nautilus-thumbnails.h	(Revision 13124)
+++ libnautilus-private/nautilus-thumbnails.h	(Arbeitskopie)
@@ -28,6 +28,15 @@
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <libnautilus-private/nautilus-file.h>
 
+typedef struct NautilusThumbnailAsyncLoadHandle NautilusThumbnailAsyncLoadHandle;
+
+typedef void (* NautilusThumbnailAsyncLoadFunc) (NautilusThumbnailAsyncLoadHandle *handle,
+						 const char *path,
+						 GdkPixbuf  *pixbuf,
+						 double      scale_x,
+						 double      scale_y,
+						 gpointer    user_data);
+
 /* Returns NULL if there's no thumbnail yet. */
 void       nautilus_create_thumbnail                (NautilusFile *file);
 void       nautilus_thumbnail_frame_image           (GdkPixbuf **pixbuf);
@@ -37,6 +46,14 @@ GdkPixbuf *nautilus_thumbnail_load_image
 						     gboolean    force_nominal,
 						     double     *scale_x_out,
 						     double     *scale_y_out);
+NautilusThumbnailAsyncLoadHandle *
+	   nautilus_thumbnail_load_image_async	    (const char *path,
+						     guint       base_size,
+						     guint       nominal_size,
+						     gboolean    force_nominal,
+						     NautilusThumbnailAsyncLoadFunc load_func,
+						     gpointer    load_func_user_data);
+void       nautilus_thumbnail_load_image_cancel     (NautilusThumbnailAsyncLoadHandle *handle);
 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]