view src/thumb.c @ 8:e0d0593d519e

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:07:07 +0000
parents c0e337a01cb7
children d907d608745f
line wrap: on
line source

/*
 * GQview image viewer
 * (C)2000 John Ellis
 *
 * Author: John Ellis
 *
 */

#include "gqview.h"
#include "icons/img_unknown.xpm" /* fixme! duplicate, included in image.c too */

#define THUMBNAIL_CACHE_DIR "/.gqview_thmb"

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);

/*
 *-----------------------------------------------------------------------------
 * thumbnail routines: creation, caching, and maintenance (public)
 *-----------------------------------------------------------------------------
 */

gint create_thumbnail(gchar *path, GdkPixmap **thumb_pixmap, GdkBitmap **thumb_mask)
{
	gint width, height;
	gint space;
	GdkImlibImage *thumb = NULL;
	GdkImlibImage *image = NULL;
	gint cached = FALSE;

	if (debug) printf("Gen thumbnail:%s\n",path);

	/* 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;
			}
		}

	/* start load from cache */

	if (enable_thumb_caching)
		{
		gchar *cache_path;
		cache_path = g_strconcat(homedir(), THUMBNAIL_CACHE_DIR, path, ".png", NULL);

		if (isfile(cache_path) && filetime(cache_path) >= filetime(path))
			{
			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
				{
				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;
				}
			}
		else
			{
			width = image->rgb_width;
			height = image->rgb_height;
			cached = TRUE; /* don't cache images smaller than thumbnail size */
			}
		if (*thumb_pixmap) gdk_imlib_free_pixmap(*thumb_pixmap);
		*thumb_pixmap = NULL;
		*thumb_mask = NULL;

	/* start save cache */

		if (enable_thumb_caching && !cached)
			{
			gchar *thumb_path;
			gchar *base_dir;
			gchar *thumb_dir;
			gchar *image_dir;

			/* 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))
				{
				if (debug) printf("creating thumbnail dir:%s\n", base_dir);
				if (mkdir(base_dir, 0755) < 0)
					printf(_("create dir failed: %s\n"), base_dir);
				}

			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);

			thumb_path = g_strconcat(base_dir, path, ".png", NULL);
			if (debug) printf("Saving thumb: %s\n",thumb_path);

			gdk_imlib_save_image(thumb, thumb_path, NULL);

			g_free(base_dir);
			g_free(thumb_path);
			}
		else
			{
			thumb = image;
			}

	/* 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;
		}
	else
		{
		space = -1;
		}
	return space;
}

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);

	base_length = strlen(homedir()) + strlen(THUMBNAIL_CACHE_DIR);
	thumb_dir = g_strconcat(homedir(), THUMBNAIL_CACHE_DIR, dir, NULL);

	if (isdir(thumb_dir))
		{
		DIR             *dp;
		struct dirent   *dirent;
		struct stat ent_sbuf;

		if((dp = opendir(thumb_dir))==NULL)
			{
				/* dir not found */
				g_free(thumb_dir);
				return FALSE;
			}

		while ((dirent = readdir(dp)) != NULL)
			{
			/* 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 (stat(path_buf,&ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
					{
					/* 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);
					}
				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);
				}
			}
		closedir(dp);
		}
	g_free(thumb_dir);
	return still_have_a_file;
}

/*
 *-----------------------------------------------------------------------------
 * xvpics thumbnail support, read-only (private)
 *-----------------------------------------------------------------------------
 */

/*
 * xvpics code originally supplied by:
 * "Diederen Damien" <D.Diederen@student.ulg.ac.be>
 *
 * Note: Code has been modified to fit the style of the other code, and to use
 *       a few more glib-isms.
 */

#define XV_BUFFER 2048
static guchar *load_xv_thumbnail(gchar *filename, gint *widthp, gint *heightp)
{
	FILE *file;
	gchar buffer[XV_BUFFER];
	guchar *data;
	gint width, height, depth;

	file = fopen(filename, "rt");
	if(!file) return NULL;

	fgets(buffer, XV_BUFFER, file);
	if(strncmp(buffer, "P7 332", 6) != 0)
		{
		fclose(file);
		return NULL;
		}

	while(fgets(buffer, XV_BUFFER, file) && buffer[0] == '#') /* do_nothing() */;

	if(sscanf(buffer, "%d %d %d", &width, &height, &depth) != 3)
		{
		fclose(file);
		return NULL;
		}

	data = g_new(guchar, width * height);
	fread(data, 1, width * height, file);

	fclose(file);
	*widthp = width;
	*heightp = height;
	return data;
}
#undef XV_BUFFER

static void normalize_thumb(gint *width, gint *height)
{
	if(*width > thumb_max_width || *height > thumb_max_height)
		{
		gfloat factor = MAX((gfloat) *width / thumb_max_width, (gfloat) *height / thumb_max_height);
		*width = (gfloat) *width / factor;
		*height = (gfloat) *height / factor;
		}
}

static gint get_xv_thumbnail(gchar *thumb_filename, GdkPixmap **thumb_pixmap, GdkBitmap **thumb_mask)
{
	gint width, height;
	gchar *thumb_name;
	gchar *tmp_string;
	gchar *last_slash;
	guchar *packed_data;

	tmp_string = g_strdup(thumb_filename);  
	last_slash = strrchr(tmp_string, '/');
	if(!last_slash)	return -1;
	*last_slash++ = '\0';

	thumb_name = g_strconcat(tmp_string, "/.xvpics/", last_slash, NULL);
	packed_data = load_xv_thumbnail(thumb_name, &width, &height);
	g_free(tmp_string);
	g_free(thumb_name);

	if(packed_data)
		{
		guchar *rgb_data;
		GdkImlibImage *image;
		gint i;

		rgb_data = g_new(guchar, width * height * 3);
		for(i = 0; i < width * height; i++)
			{
			rgb_data[i * 3 + 0] = (packed_data[i] >> 5) * 36;
			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);
		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);
	
		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 -1;
}