diff src/thumb.c @ 9:d907d608745f

Sync to GQview 1.5.9 release. ######## DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS! This CVS is never up to date with current development and is provided solely for reference purposes, please use the latest official release package when making any changes or translation updates. ########
author gqview
date Sat, 26 Feb 2005 00:13:35 +0000
parents c0e337a01cb7
children 25335c62cd9b
line wrap: on
line diff
--- a/src/thumb.c	Sat Feb 26 00:07:07 2005 +0000
+++ b/src/thumb.c	Sat Feb 26 00:13:35 2005 +0000
@@ -1,19 +1,33 @@
 /*
- * GQview image viewer
- * (C)2000 John Ellis
+ * GQview
+ * (C) 2004 John Ellis
  *
  * Author: John Ellis
  *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
  */
 
+
 #include "gqview.h"
-#include "icons/img_unknown.xpm" /* fixme! duplicate, included in image.c too */
+#include "thumb.h"
 
-#define THUMBNAIL_CACHE_DIR "/.gqview_thmb"
+#include "cache.h"
+#include "image-load.h"
+#include "pixbuf_util.h"
+#include "thumb_standard.h"
+#include "ui_fileops.h"
 
-static guchar *load_xv_thumbnail(gchar *filename, gint *widthp, gint *heightp);
-static void normalize_thumb(gint *width, gint *height);
-static gint get_xv_thumbnail(gchar *thumb_filename, GdkPixmap **thumb_pixmap, GdkBitmap **thumb_mask);
+#include <utime.h>
+
+
+static void thumb_loader_error_cb(ImageLoader *il, gpointer data);
+static void thumb_loader_setup(ThumbLoader *tl, gchar *path);
+
+static gint normalize_thumb(gint *width, gint *height, gint max_w, gint max_h);
+static GdkPixbuf *get_xv_thumbnail(gchar *thumb_filename, gint max_w, gint max_h);
+
 
 /*
  *-----------------------------------------------------------------------------
@@ -21,245 +35,498 @@
  *-----------------------------------------------------------------------------
  */
 
-gint create_thumbnail(gchar *path, GdkPixmap **thumb_pixmap, GdkBitmap **thumb_mask)
+static gint thumb_loader_save_to_cache(ThumbLoader *tl)
 {
-	gint width, height;
-	gint space;
-	GdkImlibImage *thumb = NULL;
-	GdkImlibImage *image = NULL;
-	gint cached = FALSE;
-
-	if (debug) printf("Gen thumbnail:%s\n",path);
+	gchar *cache_dir;
+	gint success = FALSE;
+	mode_t mode = 0755;
 
-	/* if xvpics enabled, check for that first */
-	if (use_xvpics_thumbnails)
-		{
-		space = get_xv_thumbnail(path, thumb_pixmap, thumb_mask);
-		if (space != -1)
-			{
-			if (debug) printf("XV thumbnail found, loaded\n");
-			return space;
-			}
-		}
+	if (!tl || !tl->pixbuf) return FALSE;
 
-	/* start load from cache */
+	cache_dir = cache_get_location(CACHE_TYPE_THUMB, tl->path, FALSE, &mode);
 
-	if (enable_thumb_caching)
+	if (cache_ensure_dir_exists(cache_dir, mode))
 		{
 		gchar *cache_path;
-		cache_path = g_strconcat(homedir(), THUMBNAIL_CACHE_DIR, path, ".png", NULL);
+		gchar *pathl;
+
+		cache_path = g_strconcat(cache_dir, "/", filename_from_path(tl->path),
+					 GQVIEW_CACHE_EXT_THUMB, NULL);
 
-		if (isfile(cache_path) && filetime(cache_path) >= filetime(path))
+		if (debug) printf("Saving thumb: %s\n", cache_path);
+
+		pathl = path_from_utf8(cache_path);
+		success = pixbuf_to_file_as_png(tl->pixbuf, pathl);
+		if (success)
 			{
-			if (debug) printf("Found in cache:%s\n", path);
-			image = gdk_imlib_load_image(cache_path);
-			if (image && image->rgb_width != thumb_max_width && image->rgb_height != thumb_max_height)
-				{
-				if (debug) printf("Thumbnail size may have changed, reloading:%s\n", path);
-				unlink(cache_path);
-				gdk_imlib_destroy_image(image);
-				image = gdk_imlib_load_image(path);
-				}
-			else
+			struct utimbuf ut;
+			/* set thumb time to that of source file */
+
+			ut.actime = ut.modtime = filetime(tl->path);
+			if (ut.modtime > 0)
 				{
-				cached = TRUE;
-				}
-			}
-		else
-			image = gdk_imlib_load_image(path);
-		
-		}
-	else
-		image = gdk_imlib_load_image(path);
-
-	if (!image)
-		{
-		image = gdk_imlib_create_image_from_xpm_data((gchar **)img_unknown_xpm);
-		cached = TRUE; /* no need to save a thumbnail of the unknown pixmap */
-		}
-
-	if (image)
-		{
-		if (image->rgb_width > thumb_max_width || image->rgb_height > thumb_max_height)
-			{
-			if (((float)thumb_max_width / image->rgb_width) < ((float)thumb_max_height / image->rgb_height))
-				{
-				width = thumb_max_width;
-				height = (float)width / image->rgb_width * image->rgb_height;
-				if (height < 1) height = 1;
-				}
-			else
-				{
-				height = thumb_max_height;
-				width = (float)height / image->rgb_height * image->rgb_width;
-				if (width < 1) width = 1;
+				utime(pathl, &ut);
 				}
 			}
 		else
 			{
-			width = image->rgb_width;
-			height = image->rgb_height;
-			cached = TRUE; /* don't cache images smaller than thumbnail size */
+			if (debug) printf("Saving failed: %s\n", pathl);
 			}
-		if (*thumb_pixmap) gdk_imlib_free_pixmap(*thumb_pixmap);
-		*thumb_pixmap = NULL;
-		*thumb_mask = NULL;
+
+		g_free(pathl);
+		g_free(cache_path);
+		}
+
+	g_free(cache_dir);
+
+	return success;
+}
 
-	/* start save cache */
+static gint thumb_loader_mark_failure(ThumbLoader *tl)
+{
+	gchar *cache_dir;
+	gint success = FALSE;
+	mode_t mode = 0755;
+	
+	if (!tl) return FALSE;
+
+	cache_dir = cache_get_location(CACHE_TYPE_THUMB, tl->path, FALSE, &mode);
 
-		if (enable_thumb_caching && !cached)
+	if (cache_ensure_dir_exists(cache_dir, mode))
+		{
+		gchar *cache_path;
+		gchar *pathl;
+		FILE *f;
+
+		cache_path = g_strconcat(cache_dir, "/", filename_from_path(tl->path),
+					 GQVIEW_CACHE_EXT_THUMB, NULL);
+
+		if (debug) printf("marking thumb failure: %s\n", cache_path);
+
+		pathl = path_from_utf8(cache_path);
+		f = fopen(pathl, "w");
+		if (f)
 			{
-			gchar *thumb_path;
-			gchar *base_dir;
-			gchar *thumb_dir;
-			gchar *image_dir;
+			struct utimbuf ut;
+
+			fclose (f);
 
-			/* attempt at speed-up? move this here */
-			thumb = gdk_imlib_clone_scaled_image(image, width, height);
-			gdk_imlib_destroy_image(image);
-			image = NULL;
-
-			base_dir = g_strconcat(homedir(), THUMBNAIL_CACHE_DIR, NULL);
-			if (!isdir(base_dir))
+			ut.actime = ut.modtime = filetime(tl->path);
+			if (ut.modtime > 0)
 				{
-				if (debug) printf("creating thumbnail dir:%s\n", base_dir);
-				if (mkdir(base_dir, 0755) < 0)
-					printf(_("create dir failed: %s\n"), base_dir);
+				utime(pathl, &ut);
 				}
 
-			image_dir = remove_level_from_path(path);
-			thumb_dir = g_strconcat(base_dir, image_dir, NULL);
-			g_free(image_dir);
-			if (!isdir(thumb_dir))
-				{
-				gchar *p = thumb_dir;
-				while (p[0] != '\0')
-					{
-					p++;
-					if (p[0] == '/' || p[0] == '\0')
-						{
-						gint end = TRUE;
-						if (p[0] != '\0')
-							{
-							p[0] = '\0';
-							end = FALSE;
-							}
-						if (!isdir(thumb_dir))
-							{
-							if (debug) printf("creating sub dir:%s\n",thumb_dir);
-							if (mkdir(thumb_dir, 0755) < 0)
-								printf(_("create dir failed: %s\n"), thumb_dir);
-							}
-						if (!end) p[0] = '/';
-						}
-					}
-				}
-			g_free(thumb_dir);
+			success = TRUE;
+			}
+
+		g_free(pathl);
+		g_free(cache_path);
+		}
+
+	g_free(cache_dir);
+	return success;
+}
+
+static void thumb_loader_percent_cb(ImageLoader *il, gdouble percent, gpointer data)
+{
+	ThumbLoader *tl = data;
+
+	tl->percent_done = percent;
+
+	if (tl->func_progress) tl->func_progress(tl, tl->data);
+}
+
+static void thumb_loader_done_cb(ImageLoader *il, gpointer data)
+{
+	ThumbLoader *tl = data;
+	GdkPixbuf *pixbuf;
+	gint pw, ph;
+	gint save;
+
+	if (debug) printf("thumb done: %s\n", tl->path);
 
-			thumb_path = g_strconcat(base_dir, path, ".png", NULL);
-			if (debug) printf("Saving thumb: %s\n",thumb_path);
+	pixbuf = image_loader_get_pixbuf(tl->il);
+	if (!pixbuf)
+		{
+		if (debug) printf("...but no pixbuf: %s\n", tl->path);
+		thumb_loader_error_cb(tl->il, tl);
+		return;
+		}
+
+	pw = gdk_pixbuf_get_width(pixbuf);
+	ph = gdk_pixbuf_get_height(pixbuf);
+
+	if (tl->cache_hit && pw != tl->max_w && ph != tl->max_h)
+		{
+		/* requested thumbnail size may have changed, load original */
+		if (debug) printf("thumbnail size mismatch, regenerating: %s\n", tl->path);
+		tl->cache_hit = FALSE;
+
+		thumb_loader_setup(tl, tl->path);
 
-			gdk_imlib_save_image(thumb, thumb_path, NULL);
+		if (!image_loader_start(tl->il, thumb_loader_done_cb, tl))
+			{
+			image_loader_free(tl->il);
+			tl->il = NULL;
+
+			if (debug) printf("regeneration failure: %s\n", tl->path);
+			thumb_loader_error_cb(tl->il, tl);
+			}
+		return;
+		}
 
-			g_free(base_dir);
-			g_free(thumb_path);
+	/* scale ?? */
+
+	if (pw > tl->max_w || ph > tl->max_h)
+		{
+		gint w, h;
+
+		if (((float)tl->max_w / pw) < ((float)tl->max_h / ph))
+			{
+			w = tl->max_w;
+			h = (float)w / pw * ph;
+			if (h < 1) h = 1;
 			}
 		else
 			{
-			thumb = image;
+			h = tl->max_h;
+			w = (float)h / ph * pw;
+			if (w < 1) w = 1;
 			}
 
-	/* end save cache */
-
-		gdk_imlib_render(thumb, width, height);
-		*thumb_pixmap = gdk_imlib_move_image(thumb);
-		*thumb_mask = gdk_imlib_move_mask(thumb);
-		if (*thumb_pixmap)
-			space = thumb_max_width - width;
-		gdk_imlib_destroy_image(thumb);
-		thumb = NULL;
+		tl->pixbuf = gdk_pixbuf_scale_simple(pixbuf, w, h, (GdkInterpType)thumbnail_quality);
+		save = TRUE;
 		}
 	else
 		{
-		space = -1;
+		tl->pixbuf = pixbuf;
+		gdk_pixbuf_ref(tl->pixbuf);
+		save = FALSE;
+		}
+
+	/* save it ? */
+	if (tl->cache_enable && save)
+		{
+		thumb_loader_save_to_cache(tl);
+		}
+
+	if (tl->func_done) tl->func_done(tl, tl->data);
+}
+
+static void thumb_loader_error_cb(ImageLoader *il, gpointer data)
+{
+	ThumbLoader *tl = data;
+
+	/* if at least some of the image is available, go to done_cb */
+	if (image_loader_get_pixbuf(tl->il) != NULL)
+		{
+		thumb_loader_done_cb(il, data);
+		return;
 		}
-	return space;
+
+	if (debug) printf("thumb error: %s\n", tl->path);
+
+	image_loader_free(tl->il);
+	tl->il = NULL;
+
+	if (tl->func_error) tl->func_error(tl, tl->data);
+}
+
+static gint thumb_loader_done_delay_cb(gpointer data)
+{
+	ThumbLoader *tl = data;
+
+	tl->idle_done_id = -1;
+
+	if (tl->func_done) tl->func_done(tl, tl->data);
+
+	return FALSE;
+}
+
+static void thumb_loader_delay_done(ThumbLoader *tl)
+{
+	if (tl->idle_done_id == -1) tl->idle_done_id = g_idle_add(thumb_loader_done_delay_cb, tl);
+}
+
+static void thumb_loader_setup(ThumbLoader *tl, gchar *path)
+{
+	image_loader_free(tl->il);
+	tl->il = image_loader_new(path);
+
+#if 0
+	/* this will speed up jpegs by up to 3x in some cases */
+	image_loader_set_requested_size(tl->max_w, tl->max_h);
+#endif
+
+	image_loader_set_error_func(tl->il, thumb_loader_error_cb, tl);
+	if (tl->func_progress) image_loader_set_percent_func(tl->il, thumb_loader_percent_cb, tl);
+}
+
+void thumb_loader_set_callbacks(ThumbLoader *tl,
+				ThumbLoaderFunc func_done,
+				ThumbLoaderFunc func_error,
+				ThumbLoaderFunc func_progress,
+				gpointer data)
+{
+	if (!tl) return;
+
+	if (tl->standard_loader)
+		{
+		thumb_loader_std_set_callbacks((ThumbLoaderStd *)tl,
+					       (ThumbLoaderStdFunc) func_done,
+					       (ThumbLoaderStdFunc) func_error,
+					       (ThumbLoaderStdFunc) func_progress,
+					       data);
+		return;
+		}
+
+	tl->func_done = func_done;
+	tl->func_error = func_error;
+	tl->func_progress = func_progress;
+
+	tl->data = data;
+}
+
+void thumb_loader_set_cache(ThumbLoader *tl, gint enable_cache, gint local, gint retry_failed)
+{
+        if (!tl) return;
+
+	if (tl->standard_loader)
+		{
+		thumb_loader_std_set_cache((ThumbLoaderStd *)tl, enable_cache, local, retry_failed);
+		return;
+		}
+
+	tl->cache_enable = enable_cache;
+#if 0
+	tl->cache_local = local;
+	tl->cache_retry = retry_failed;
+#endif
 }
 
-gint maintain_thumbnail_dir(gchar *dir, gint recursive)
-{
-	gchar *thumb_dir;
-	gint base_length;
-	gint still_have_a_file = FALSE;
 
-	if (debug) printf("maintainance check: %s\n", dir);
+gint thumb_loader_start(ThumbLoader *tl, const gchar *path)
+{
+	gchar *cache_path = NULL;
 
-	base_length = strlen(homedir()) + strlen(THUMBNAIL_CACHE_DIR);
-	thumb_dir = g_strconcat(homedir(), THUMBNAIL_CACHE_DIR, dir, NULL);
+	if (!tl) return FALSE;
 
-	if (isdir(thumb_dir))
+	if (tl->standard_loader)
 		{
-		DIR             *dp;
-		struct dirent   *dirent;
-		struct stat ent_sbuf;
+		return thumb_loader_std_start((ThumbLoaderStd *)tl, path);
+		}
+
+	if (!tl->path && !path) return FALSE;
 
-		if((dp = opendir(thumb_dir))==NULL)
-			{
-				/* dir not found */
-				g_free(thumb_dir);
-				return FALSE;
-			}
+	if (!tl->path) tl->path = g_strdup(path);
 
-		while ((dirent = readdir(dp)) != NULL)
+	if (tl->cache_enable)
+		{
+		cache_path = cache_find_location(CACHE_TYPE_THUMB, tl->path);
+
+		if (cache_path)
 			{
-			/* skips removed files */
-	                if (dirent->d_ino > 0)
-	                	{
-				int l = 0;
-				gchar *path_buf;
-				if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0)
-					continue;
-				path_buf = g_strconcat(thumb_dir, "/", dirent->d_name, NULL);
-				if (strlen(path_buf) > 4) l = strlen(path_buf) - 4;
+			if (cache_time_valid(cache_path, tl->path))
+				{
+				if (debug) printf("Found in cache:%s\n", tl->path);
 
-				if (stat(path_buf,&ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
+				if (filesize(cache_path) == 0)
 					{
-					/* recurse dir then delete it */
-					gchar *rdir = g_strconcat(dir, "/", dirent->d_name, NULL);
-					if (recursive && !maintain_thumbnail_dir(rdir, TRUE))
-						{
-						if (debug) printf("Deleting thumb dir: %s\n",path_buf);
-						if ( (rmdir (path_buf) < 0) )
-							printf(_("Unable to delete dir: %s\n"), path_buf);
-						}
-					else
-						still_have_a_file = TRUE;
-					g_free(rdir);
+					if (debug) printf("Broken image mark found:%s\n", cache_path);
+					g_free(cache_path);
+					return FALSE;
 					}
-				else
-					{
-					gchar *fp = path_buf + l;
-					fp[0] = '\0';
-					if (strlen(path_buf) > base_length &&
-							!isfile(path_buf + base_length))
-						{
-						fp[0] = '.';
-						if (debug) printf("Deleting thumb: %s\n",path_buf);
-						if ( (unlink (path_buf) < 0) )
-							printf(_("failed to delete:%s\n"),path_buf);
-						}
-					else
-						 still_have_a_file = TRUE;
-					}
-				g_free(path_buf);
+
+				if (debug) printf("Cache location:%s\n", cache_path);
+				}
+			else
+				{
+				g_free(cache_path);
+				cache_path = NULL;
 				}
 			}
-		closedir(dp);
+		}
+
+	if (!cache_path && use_xvpics_thumbnails)
+		{
+		tl->pixbuf = get_xv_thumbnail(tl->path, tl->max_w, tl->max_h);
+		if (tl->pixbuf)
+			{
+			thumb_loader_delay_done(tl);
+			return TRUE;
+			}
+		}
+
+	if (cache_path)
+		{
+		thumb_loader_setup(tl, cache_path);
+		g_free(cache_path);
+		tl->cache_hit = TRUE;
+		}
+	else
+		{
+		thumb_loader_setup(tl, tl->path);
+		}
+
+	if (!image_loader_start(tl->il, thumb_loader_done_cb, tl))
+		{
+		/* try from original if cache attempt */
+		if (tl->cache_hit)
+			{
+			tl->cache_hit = FALSE;
+			print_term(_("Thumbnail image in cache failed to load, trying to recreate.\n"));
+
+			thumb_loader_setup(tl, tl->path);
+			if (image_loader_start(tl->il, thumb_loader_done_cb, tl)) return TRUE;
+			}
+		/* mark failed thumbnail in cache with 0 byte file */
+		if (tl->cache_enable)
+			{
+			thumb_loader_mark_failure(tl);
+			}
+		
+		image_loader_free(tl->il);
+		tl->il = NULL;
+		return FALSE;
+		}
+
+	return TRUE;
+}
+
+#if 0
+gint thumb_loader_to_pixmap(ThumbLoader *tl, GdkPixmap **pixmap, GdkBitmap **mask)
+{
+	if (!tl || !tl->pixbuf) return -1;
+
+	gdk_pixbuf_render_pixmap_and_mask(tl->pixbuf, pixmap, mask, 128);
+
+	return thumb_loader_get_space(tl);
+}
+#endif
+
+GdkPixbuf *thumb_loader_get_pixbuf(ThumbLoader *tl, gint with_fallback)
+{
+	GdkPixbuf *pixbuf;
+
+	if (tl && tl->standard_loader)
+		{
+		return thumb_loader_std_get_pixbuf((ThumbLoaderStd *)tl, with_fallback);
+		}
+
+	if (tl && tl->pixbuf)
+		{
+		pixbuf = tl->pixbuf;
+		g_object_ref(pixbuf);
+		}
+	else if (with_fallback)
+		{
+		gint w, h;
+
+		pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN);
+		w = gdk_pixbuf_get_width(pixbuf);
+		h = gdk_pixbuf_get_height(pixbuf);
+		if ((w > tl->max_w || h > tl->max_h) &&
+		    normalize_thumb(&w, &h, tl->max_w, tl->max_h))
+			{
+			GdkPixbuf *tmp;
+
+			tmp = pixbuf;
+			pixbuf = gdk_pixbuf_scale_simple(tmp, w, h, GDK_INTERP_NEAREST);
+			gdk_pixbuf_unref(tmp);
+			}
 		}
-	g_free(thumb_dir);
-	return still_have_a_file;
+	else
+		{
+		pixbuf = NULL;
+		}
+
+	return pixbuf;
+}
+
+#if 0
+gint thumb_loader_get_space(ThumbLoader *tl)
+{
+	if (!tl) return 0;
+
+	if (tl->pixbuf) return (tl->max_w - gdk_pixbuf_get_width(tl->pixbuf));
+
+	return tl->max_w;
+}
+#endif
+
+ThumbLoader *thumb_loader_new(gint width, gint height)
+{
+	ThumbLoader *tl;
+
+	if (thumbnail_spec_standard)
+		{
+		return (ThumbLoader *)thumb_loader_std_new(width, height);
+		}
+
+	tl = g_new0(ThumbLoader, 1);
+	tl->standard_loader = FALSE;
+	tl->path = NULL;
+	tl->cache_enable = enable_thumb_caching;
+	tl->cache_hit = FALSE;
+	tl->percent_done = 0.0;
+	tl->max_w = width;
+	tl->max_h = height;
+
+	tl->il = NULL;
+
+	tl->idle_done_id = -1;
+
+	return tl;
 }
 
+void thumb_loader_free(ThumbLoader *tl)
+{
+	if (!tl) return;
+
+	if (tl->standard_loader)
+		{
+		thumb_loader_std_free((ThumbLoaderStd *)tl);
+		return;
+		}
+
+	if (tl->pixbuf) gdk_pixbuf_unref(tl->pixbuf);
+	image_loader_free(tl->il);
+	g_free(tl->path);
+
+	if (tl->idle_done_id != -1) g_source_remove(tl->idle_done_id);
+
+	g_free(tl);
+}
+
+#if 0
+gint thumb_from_xpm_d(const char **data, gint max_w, gint max_h, GdkPixmap **pixmap, GdkBitmap **mask)
+{
+	GdkPixbuf *pixbuf;
+	gint w, h;
+
+	pixbuf = gdk_pixbuf_new_from_xpm_data(data);
+	w = gdk_pixbuf_get_width(pixbuf);
+	h = gdk_pixbuf_get_height(pixbuf);
+
+	if ((w > max_w || h > max_h) &&
+	    normalize_thumb(&w, &h, max_w, max_h))
+		{
+		/* scale */
+		GdkPixbuf *tmp;
+
+		tmp = pixbuf;
+		pixbuf = gdk_pixbuf_scale_simple(tmp, w, h, GDK_INTERP_NEAREST);
+		gdk_pixbuf_unref(tmp);
+		}
+
+	gdk_pixbuf_render_pixmap_and_mask(pixbuf, pixmap, mask, 128);
+	gdk_pixbuf_unref(pixbuf);
+
+	return w;
+}
+#endif
+
 /*
  *-----------------------------------------------------------------------------
  * xvpics thumbnail support, read-only (private)
@@ -272,6 +539,7 @@
  *
  * Note: Code has been modified to fit the style of the other code, and to use
  *       a few more glib-isms.
+ * 08-28-2000: Updated to return a gdk_pixbuf, Imlib is dieing a death here.
  */
 
 #define XV_BUFFER 2048
@@ -310,17 +578,31 @@
 }
 #undef XV_BUFFER
 
-static void normalize_thumb(gint *width, gint *height)
+static gint normalize_thumb(gint *width, gint *height, gint max_w, gint max_h)
 {
-	if(*width > thumb_max_width || *height > thumb_max_height)
+	gdouble scale;
+	gint new_w, new_h;
+
+	scale = MIN((gdouble) max_w / *width, (gdouble) max_h / *height);
+	new_w = *width * scale;
+	new_h = *height * scale;
+
+	if (new_w != *width || new_h != *height)
 		{
-		gfloat factor = MAX((gfloat) *width / thumb_max_width, (gfloat) *height / thumb_max_height);
-		*width = (gfloat) *width / factor;
-		*height = (gfloat) *height / factor;
+		*width = new_w;
+		*height = new_h;
+		return TRUE;
 		}
+
+	return FALSE;
 }
 
-static gint get_xv_thumbnail(gchar *thumb_filename, GdkPixmap **thumb_pixmap, GdkBitmap **thumb_mask)
+static void free_rgb_buffer(guchar *pixels, gpointer data)
+{
+	g_free(pixels);
+}
+
+static GdkPixbuf *get_xv_thumbnail(gchar *thumb_filename, gint max_w, gint max_h)
 {
 	gint width, height;
 	gchar *thumb_name;
@@ -328,9 +610,9 @@
 	gchar *last_slash;
 	guchar *packed_data;
 
-	tmp_string = g_strdup(thumb_filename);  
+	tmp_string = path_from_utf8(thumb_filename);
 	last_slash = strrchr(tmp_string, '/');
-	if(!last_slash)	return -1;
+	if(!last_slash)	return NULL;
 	*last_slash++ = '\0';
 
 	thumb_name = g_strconcat(tmp_string, "/.xvpics/", last_slash, NULL);
@@ -341,7 +623,7 @@
 	if(packed_data)
 		{
 		guchar *rgb_data;
-		GdkImlibImage *image;
+		GdkPixbuf *pixbuf;
 		gint i;
 
 		rgb_data = g_new(guchar, width * height * 3);
@@ -351,21 +633,26 @@
 			rgb_data[i * 3 + 1] = ((packed_data[i] & 28) >> 2) * 36;
 			rgb_data[i * 3 + 2] = (packed_data[i] & 3) * 85;
 			}
+		g_free(packed_data);
 
-		g_free(packed_data);
-		image = gdk_imlib_create_image_from_data(rgb_data, NULL, width, height);
-		g_free(rgb_data);
-		normalize_thumb(&width, &height);
-		gdk_imlib_render(image, width, height);
+		pixbuf = gdk_pixbuf_new_from_data(rgb_data, GDK_COLORSPACE_RGB, FALSE, 8,
+						  width, height, 3 * width, free_rgb_buffer, NULL);
+
+		if (normalize_thumb(&width, &height, max_w, max_h))
+			{
+			/* scale */
+			GdkPixbuf *tmp;
+
+			tmp = pixbuf;
+			pixbuf = gdk_pixbuf_scale_simple(tmp, width, height, GDK_INTERP_NEAREST);
+			gdk_pixbuf_unref(tmp);
+			}
 	
-		if(*thumb_pixmap) gdk_imlib_free_pixmap(*thumb_pixmap);
-
-		*thumb_pixmap = gdk_imlib_move_image(image);
-		*thumb_mask = gdk_imlib_move_mask(image);
-		gdk_imlib_destroy_image(image);
-		return thumb_max_width - width;
+		return pixbuf;
 		}
 
-	return -1;
+	return NULL;
 }
 
+
+