[Nautilus-list] Faster jpeg thumbnailing patch



Attached to this mail is a patch that increases the performance when
thumbnailing large jpeg images. It does so by using the built in scaling
in libjpeg. This means that libjpeg doesn't have to decompress the whole
file when scaling down (high frequency components of the DCT are
ignored). Loading and scaling down jpegs are much faster than loading the
whole picture and then scaling it down.

A quick test on my laptop shows that a directory with 40 1600x1200 jpegs
(just got a digital camera) is thumbnailed in 33 seconds instead of 1
minute and 6 seconds like before. On machines with faster disks this will
probably be even faster.

Btw. To compile with eel and librsvg in a different prefix than the rest
of gnome I had to s/LIBRSVG_INCLUDEDIR/LIBRSVG_CFLAGS/ and
s/EEL_INCLUDEDIR/EEL_CFLAGS/ in all Makefiles and configure.in.

Please cc me for replies, i'm not on nautilus-list.

/ Alex

Index: acconfig.h
===================================================================
RCS file: /cvs/gnome/nautilus/acconfig.h,v
retrieving revision 1.24
diff -u -p -r1.24 acconfig.h
--- acconfig.h	2001/04/11 12:37:37	1.24
+++ acconfig.h	2001/04/26 23:15:09
@@ -8,6 +8,7 @@
 #undef HAVE_GETTEXT
 #undef HAVE_LC_MESSAGES
 #undef HAVE_MEDUSA
+#undef HAVE_LIBJPEG
 #undef HAVE_STPCPY
 #undef HAVE_LIBBZ2
 #undef HAVE_AMMONITE
Index: configure.in
===================================================================
RCS file: /cvs/gnome/nautilus/configure.in,v
retrieving revision 1.326
diff -u -p -r1.326 configure.in
--- configure.in	2001/04/21 00:23:46	1.326
+++ configure.in	2001/04/26 23:15:12
@@ -793,6 +793,27 @@ dnl =======================
 dnl = End tests for libpng
 dnl =======================
 
+dnl ====================================
+dnl = Begin tests for libjpeg
+dnl ====================================
+  if test -z "$LIBJPEG"; then
+    AC_CHECK_LIB(jpeg, jpeg_start_decompress,
+      AC_CHECK_HEADER(jpeglib.h,
+        jpeg_ok=yes,
+        jpeg_ok=no),
+      AC_MSG_WARN(*** (jpeg library not found) ***), -lm)
+    if test "$jpeg_ok" = yes; then
+      JPEG='jpeg'; LIBJPEG='-ljpeg'
+      AC_DEFINE(HAVE_LIBJPEG)
+    else
+     AC_MSG_WARN(*** JPEG loader will not be built (jpeg header file not found) ***)
+    fi
+  fi
+
+AC_SUBST(LIBJPEG)
+dnl =======================
+dnl = End tests for libjpeg
+dnl =======================
 
 dnl =======================
 dnl = Set up for library directories
Index: libnautilus-extensions/Makefile.am
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-extensions/Makefile.am,v
retrieving revision 1.184
diff -u -p -r1.184 Makefile.am
--- libnautilus-extensions/Makefile.am	2001/04/20 02:01:40	1.184
+++ libnautilus-extensions/Makefile.am	2001/04/26 23:15:18
@@ -48,6 +48,7 @@ libnautilus_extensions_la_LDFLAGS =	\
 	$(GNOMECANVASPIXBUF_LIBS)	\
 	$(GNOME_LIBS) 			\
 	$(LIBPNG) 			\
+	$(LIBJPEG) 			\
 	$(MEDUSA_LIBS) 			\
 	$(OAF_LIBS) 			\
 	$(VFS_LIBS) 			\
@@ -117,6 +118,7 @@ libnautilus_extensions_la_SOURCES = \
 	nautilus-sound.c \
 	nautilus-theme.c \
 	nautilus-thumbnails.c \
+	nautilus-thumbnails-jpeg.c \
 	nautilus-trash-directory.c \
 	nautilus-trash-file.c \
 	nautilus-trash-monitor.c \
@@ -192,6 +194,7 @@ noinst_HEADERS = \
 	nautilus-sound.h \
 	nautilus-theme.h \
 	nautilus-thumbnails.h \
+	nautilus-thumbnails-jpeg.h \
 	nautilus-trash-directory.h \
 	nautilus-trash-file.h \
 	nautilus-trash-monitor.h \
Index: libnautilus-extensions/nautilus-thumbnails.c
===================================================================
RCS file: /cvs/gnome/nautilus/libnautilus-extensions/nautilus-thumbnails.c,v
retrieving revision 1.18
diff -u -p -r1.18 nautilus-thumbnails.c
--- libnautilus-extensions/nautilus-thumbnails.c	2001/04/04 11:28:54	1.18
+++ libnautilus-extensions/nautilus-thumbnails.c	2001/04/26 23:15:20
@@ -24,6 +24,7 @@
 
 #include <config.h>
 #include "nautilus-thumbnails.h"
+#include "nautilus-thumbnails-jpeg.h"
 
 #include "nautilus-directory-notify.h"
 #include "nautilus-icon-factory-private.h"
@@ -593,6 +594,14 @@ make_thumbnails (gpointer data)
 						fclose (f);
 					}
 				}
+#ifdef HAVE_LIBJPEG
+			} else if (nautilus_file_is_mime_type (file, "image/jpeg")) {
+				if (info->thumbnail_uri != NULL)
+					full_size_image =
+						nautilus_thumbnail_load_scaled_jpeg (info->thumbnail_uri,
+										     96,
+										     96);
+#endif
 			} else {
 				if (info->thumbnail_uri != NULL)
 					full_size_image = eel_gdk_pixbuf_load (info->thumbnail_uri);						
--- /dev/null	Thu Aug 24 11:00:32 2000
+++ libnautilus-extensions/nautilus-thumbnails-jpeg.h	Fri Apr 27 00:10:40 2001
@@ -0,0 +1,36 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   nautilus-thumbnails-jpeg.h: Fast loading of scaled jpeg images
+ 
+   Copyright (C) 2001 Red Hat Inc
+  
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+  
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Alexander Larsson <alexl redhat com>
+*/
+
+#ifndef NAUTILUS_THUMBNAILS_JPEG_H
+#define NAUTILUS_THUMBNAILS_JPEG_H
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+GdkPixbuf *
+nautilus_thumbnail_load_scaled_jpeg (const char *uri,
+				     int         target_width,
+				     int         target_heigh);
+
+#endif /* NAUTILUS_THUMBNAILS_JPEG_H */
+
--- /dev/null	Thu Aug 24 11:00:32 2000
+++ libnautilus-extensions/nautilus-thumbnails-jpeg.c	Fri Apr 27 00:53:07 2001
@@ -0,0 +1,220 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+
+   nautilus-thumbnails-jpeg.c: Fast loading of scaled jpeg images
+ 
+   Copyright (C) 2001 Red Hat Inc
+  
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+  
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+  
+   Author: Alexander Larsson <alexl redhat com>
+*/
+
+#include <config.h>
+
+#ifdef HAVE_LIBJPEG
+#include <stdlib.h>
+#include <stdio.h>
+#include <jpeglib.h>
+#include <libgnomevfs/gnome-vfs-types.h>
+#include <libgnomevfs/gnome-vfs-ops.h>
+#include <libgnomevfs/gnome-vfs-utils.h>
+
+#include "nautilus-thumbnails-jpeg.h"
+
+
+typedef struct {
+	struct jpeg_source_mgr pub;	/* public fields */
+	
+	GnomeVFSHandle *handle;
+	JOCTET * buffer;
+} GnomeVFSSource;
+
+#define INPUT_BUF_SIZE  4096
+
+static void
+init_source (j_decompress_ptr cinfo)
+{
+}
+
+static gboolean
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+	GnomeVFSSource *src = (GnomeVFSSource *) cinfo->src;
+	GnomeVFSFileSize nbytes;
+	GnomeVFSResult result;
+	
+	result = gnome_vfs_read (src->handle,
+				 src->buffer,
+				 INPUT_BUF_SIZE,
+				 &nbytes);
+	
+	if (result != GNOME_VFS_OK || nbytes == 0) {
+		/* Insert a fake EOI marker */
+		src->buffer[0] = (JOCTET) 0xFF;
+		src->buffer[1] = (JOCTET) JPEG_EOI;
+		nbytes = 2;
+	}
+	
+	src->pub.next_input_byte = src->buffer;
+	src->pub.bytes_in_buffer = nbytes;
+	
+	return TRUE;
+}
+
+static void
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+	GnomeVFSSource *src = (GnomeVFSSource *) cinfo->src;
+	
+	if (num_bytes > 0) {
+		while (num_bytes > (long) src->pub.bytes_in_buffer) {
+			num_bytes -= (long) src->pub.bytes_in_buffer;
+			fill_input_buffer(cinfo);
+		}
+		src->pub.next_input_byte += (size_t) num_bytes;
+		src->pub.bytes_in_buffer -= (size_t) num_bytes;
+	}
+}
+
+static void
+term_source (j_decompress_ptr cinfo)
+{
+	GnomeVFSSource *src = (GnomeVFSSource *) cinfo->src;
+	g_free (src->buffer);
+	src->buffer = NULL;
+}
+
+static void
+vfs_src (j_decompress_ptr cinfo, GnomeVFSHandle *handle)
+{
+	GnomeVFSSource *src;
+	
+	if (cinfo->src == NULL) {	/* first time for this JPEG object? */
+		cinfo->src = (struct jpeg_source_mgr *) g_new (GnomeVFSSource, 1);
+		src = (GnomeVFSSource *) cinfo->src;
+		src->buffer = g_malloc (INPUT_BUF_SIZE * sizeof (JOCTET));
+	}
+	
+	src = (GnomeVFSSource *) cinfo->src;
+	src->pub.init_source = init_source;
+	src->pub.fill_input_buffer = fill_input_buffer;
+	src->pub.skip_input_data = skip_input_data;
+	src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+	src->pub.term_source = term_source;
+	src->handle = handle;
+	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+	src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
+
+static int
+calculate_divisor (int width,
+		   int height,
+		   int target_width,
+		   int target_height)
+{
+	if (width/8 > target_width && height/8 > target_height)
+		return 8;
+	if (width/4 > target_width && height/4 > target_height)
+		return 4;
+	if (width/2 > target_width && height/2 > target_height)
+		return 2;
+	return 1;
+}
+
+static void
+free_buffer (guchar *pixels, gpointer data)
+{
+	g_free (pixels);
+}
+
+GdkPixbuf *
+nautilus_thumbnail_load_scaled_jpeg (const char *uri,
+				     int         target_width,
+				     int         target_height)
+{
+	struct jpeg_decompress_struct cinfo;
+	struct jpeg_error_mgr jerr;
+	GnomeVFSHandle *handle;
+	unsigned char *lines[1];
+	guchar *buffer = NULL;
+	guchar *pixels, *ptr = NULL;
+	GnomeVFSResult result;
+	unsigned int i;
+	
+	result = gnome_vfs_open (&handle,
+				 uri,
+				 GNOME_VFS_OPEN_READ);
+	if (result != GNOME_VFS_OK) {
+		return NULL;
+	}
+
+  
+	cinfo.err = jpeg_std_error (&jerr);
+	jpeg_create_decompress (&cinfo);
+
+	vfs_src (&cinfo, handle);
+
+	jpeg_read_header(&cinfo, TRUE);
+
+	cinfo.scale_num = 1;
+	cinfo.scale_denom = calculate_divisor (cinfo.image_width,
+					       cinfo.image_height,
+					       target_width,
+					       target_height);
+	cinfo.dct_method = JDCT_FASTEST;
+	cinfo.do_fancy_upsampling = FALSE;
+	
+	jpeg_start_decompress(&cinfo);
+
+	pixels = g_malloc (cinfo.output_width *	cinfo.output_height * 3);
+
+	if (cinfo.num_components == 1) {
+		/* Allocate extra buffer for grayscale data */
+		buffer = g_malloc (cinfo.output_width);
+		lines[0] = buffer;
+		ptr = pixels;
+	} else
+		lines[0] = pixels;
+	
+	while (cinfo.output_scanline < cinfo.output_height) {
+		jpeg_read_scanlines (&cinfo, lines, 1);
+		
+		if (cinfo.num_components == 1) {
+			/* Convert grayscale to rgb */
+			for (i = 0; i < cinfo.output_width; i++) {
+				ptr[i*3] = buffer[i];
+				ptr[i*3+1] = buffer[i];
+				ptr[i*3+2] = buffer[i];
+			}
+			ptr += cinfo.output_width * 3;
+		} else
+			lines[0] += cinfo.output_width * 3;
+	}
+
+	g_free (buffer);
+	
+	jpeg_finish_decompress(&cinfo);
+	jpeg_destroy_decompress(&cinfo);
+	
+	return gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, FALSE, 8,
+					 cinfo.output_width,
+					 cinfo.output_height,
+					 cinfo.output_width * 3,
+					 free_buffer, NULL);
+}
+
+
+#endif /* HAVE_LIBJPEG */


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