[Nautilus-list] Re: Faster jpeg thumbnailing patch



On Fri, 27 Apr 2001, Alexander Larsson wrote:

> 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.
> 
> Please cc me for replies, i'm not on nautilus-list.

Apparently it needed some better error handling to handle broken
jpeg files. Here is an updated patch.

/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 10:28:44 2001
@@ -0,0 +1,255 @@
+/* -*- 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 <setjmp.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;
+
+struct error_handler_data {
+	struct jpeg_error_mgr pub;
+	sigjmp_buf setjmp_buffer;
+};
+
+#define INPUT_BUF_SIZE  4096
+
+static void
+fatal_error_handler (j_common_ptr cinfo)
+{
+	struct error_handler_data *errmgr = (struct error_handler_data *) cinfo->err;
+        
+	siglongjmp (errmgr->setjmp_buffer, 1);
+
+        g_assert_not_reached ();
+}
+
+static void
+output_message_handler (j_common_ptr cinfo)
+{
+  /* This method keeps libjpeg from dumping crap to stderr */
+}
+
+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 error_handler_data jerr;
+	GnomeVFSHandle *handle;
+	unsigned char *lines[1];
+	guchar *buffer = NULL;
+	guchar *pixels = NULL;
+	guchar *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.pub);
+	jerr.pub.error_exit = fatal_error_handler;
+        jerr.pub.output_message = output_message_handler;
+
+	if (sigsetjmp (jerr.setjmp_buffer, 1)) {
+		/* Whoops there was a jpeg error */
+		g_free (pixels);
+		g_free (buffer);
+
+		jpeg_destroy_decompress (&cinfo);
+		return NULL;
+	}
+	
+	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]