added custom tiff loader
authorVladimir Nadvornik <nadvornik@suse.cz>
Thu, 29 Sep 2011 15:14:36 +0000 (17:14 +0200)
committerVladimir Nadvornik <nadvornik@suse.cz>
Thu, 29 Sep 2011 15:14:36 +0000 (17:14 +0200)
configure.in
src/Makefile.am
src/image-load.c
src/image_load_tiff.c [new file with mode: 0644]
src/image_load_tiff.h [new file with mode: 0644]

index 5cffbf5..d24f598 100644 (file)
@@ -313,6 +313,28 @@ AC_SUBST(JPEG_CFLAGS)
 AC_SUBST(JPEG_LIBS)
 
 
+#  libtiff support
+# ----------------------------------------------------------------------
+
+AC_ARG_ENABLE([tiff],
+  AC_HELP_STRING([--disable-tiff], [disable direct tiff support]),
+    [libtiff=$enableval], [libtiff=auto])
+
+if test "x${libtiff}" != "xno"; then
+  AC_CHECK_LIB(tiff, TIFFClientOpen,
+      HAVE_TIFF=yes
+      TIFF_LIBS=-ltiff
+      AC_DEFINE(HAVE_TIFF, 1, [define to enable use of custom tiff loader]),
+      HAVE_TIFF=no)
+else
+  HAVE_TIFF=disabled
+fi
+
+AM_CONDITIONAL(HAVE_TIFF, [test "x$HAVE_TIFF" = xyes])
+AC_SUBST(TIFF_CFLAGS)
+AC_SUBST(TIFF_LIBS)
+
+
 #  Exiv2 support
 # ----------------------------------------------------------------------
 
@@ -470,7 +492,7 @@ Flags:
   Gtk:           $GTK_CFLAGS
   Glib:          $GLIB_CFLAGS
   Thread:        $GTHREAD_LIBS
-  Others:       $JPEG_LIBS $LCMS_LIBS $EXIV2_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS
+  Others:       $JPEG_LIBS $TIFF_LIBS $LCMS_LIBS $EXIV2_LIBS $LIBCHAMPLAIN_LIBS $LIBCHAMPLAIN_GTK_LIBS
 
 Localization:
   NLS support:   $USE_NLS
index 237e6f2..02d31a6 100644 (file)
@@ -164,6 +164,8 @@ geeqie_SOURCES = \
        image_load_gdk.h\
        image_load_jpeg.c\
        image_load_jpeg.h\
+       image_load_tiff.c\
+       image_load_tiff.h\
        image-overlay.c \
        image-overlay.h \
        img-view.c      \
@@ -251,7 +253,7 @@ geeqie_SOURCES = \
        window.c        \
        window.h
 
-geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS)
+geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS)
 
 EXTRA_DIST = \
        $(extra_SLIK)
index 157756b..167ca89 100644 (file)
@@ -15,6 +15,7 @@
 #include "image-load.h"
 #include "image_load_gdk.h"
 #include "image_load_jpeg.h"
+#include "image_load_tiff.h"
 
 #include "exif.h"
 #include "filedata.h"
@@ -520,6 +521,16 @@ static void image_loader_setup_loader(ImageLoader *il)
                image_loader_backend_set_jpeg(&il->backend);
                }
        else
+#endif
+#ifdef HAVE_TIFF
+       if (il->bytes_total >= 10 &&
+           (memcmp(il->mapped_file, "MM\0*", 4) == 0 ||
+            memcmp(il->mapped_file, "II*\0", 4) == 0))
+               {
+               DEBUG_1("Using custom tiff loader");
+               image_loader_backend_set_tiff(&il->backend);
+               }
+       else
 #endif
                image_loader_backend_set_default(&il->backend);
 
diff --git a/src/image_load_tiff.c b/src/image_load_tiff.c
new file mode 100644 (file)
index 0000000..34efc76
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Geeqie
+ * (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2011 The Geeqie Team
+ *
+ * Author: Vladimir Nadvornik
+ *
+ * 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 "main.h"
+
+#include "image-load.h"
+#include "image_load_tiff.h"
+
+#ifdef HAVE_TIFF
+
+#include <tiffio.h>
+
+typedef struct _ImageLoaderTiff ImageLoaderTiff;
+struct _ImageLoaderTiff {
+       ImageLoaderBackendCbAreaUpdated area_updated_cb;
+       ImageLoaderBackendCbSize size_cb;
+       ImageLoaderBackendCbAreaPrepared area_prepared_cb;
+       
+       gpointer data;
+
+       GdkPixbuf *pixbuf;
+       guint requested_width;
+       guint requested_height;
+       
+       gboolean abort;
+
+       const guchar *buffer;
+       toff_t used;
+       toff_t pos;
+};
+
+static void free_buffer (guchar *pixels, gpointer data)
+{
+       g_free (pixels);
+}
+
+static tsize_t
+tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
+{
+        ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
+        
+        if (context->pos + size > context->used)
+                return 0;
+        
+        memcpy (buf, context->buffer + context->pos, size);
+        context->pos += size;
+        return size;
+}
+
+static tsize_t
+tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
+{
+        return -1;
+}
+
+static toff_t
+tiff_load_seek (thandle_t handle, toff_t offset, int whence)
+{
+        ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
+        
+        switch (whence) {
+        case SEEK_SET:
+                if (offset > context->used)
+                        return -1;
+                context->pos = offset;
+                break;
+        case SEEK_CUR:
+                if (offset + context->pos >= context->used)
+                        return -1;
+                context->pos += offset;
+                break;
+        case SEEK_END:
+                if (offset + context->used > context->used)
+                        return -1;
+                context->pos = context->used + offset;
+                break;
+        default:
+                return -1;
+        }
+        return context->pos;
+}
+
+static int
+tiff_load_close (thandle_t context)
+{
+        return 0;
+}
+
+static toff_t
+tiff_load_size (thandle_t handle)
+{
+        ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
+        
+        return context->used;
+}
+
+static int
+tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
+{
+        ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
+        
+        *buf = (tdata_t *) context->buffer;
+        *size = context->used;
+        
+        return 0;
+}
+
+static void
+tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
+{
+}
+
+static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
+{
+       ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+
+        TIFF *tiff;
+       guchar *pixels = NULL;
+       gint width, height, rowstride, bytes;
+       uint16 orientation = 0;
+       uint16 transform = 0;
+       uint32 rowsperstrip;
+
+       lt->buffer = buf;
+       lt->used = count;
+       lt->pos = 0;
+
+       TIFFSetWarningHandler(NULL);
+
+        tiff = TIFFClientOpen ("libtiff-geeqie", "r", lt, 
+                               tiff_load_read, tiff_load_write, 
+                               tiff_load_seek, tiff_load_close, 
+                               tiff_load_size, 
+                               tiff_load_map_file, tiff_load_unmap_file);
+        if (!tiff) 
+               {
+               DEBUG_1("Failed to open TIFF image");
+               return FALSE;
+               }
+
+
+
+       if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width)) {
+               DEBUG_1("Could not get image width (bad TIFF file)");
+               TIFFClose(tiff);
+                return FALSE;
+        }
+        
+        if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height)) {
+               DEBUG_1("Could not get image height (bad TIFF file)");
+               TIFFClose(tiff);
+                return FALSE;
+        }
+
+        if (width <= 0 || height <= 0) {
+               DEBUG_1("Width or height of TIFF image is zero");
+               TIFFClose(tiff);
+                return FALSE;                
+        }
+        
+        rowstride = width * 4;
+        if (rowstride / 4 != width) { /* overflow */
+               DEBUG_1("Dimensions of TIFF image too large");
+               TIFFClose(tiff);
+                return FALSE;                
+        }
+        
+        bytes = height * rowstride;
+        if (bytes / rowstride != height) { /* overflow */
+               DEBUG_1("Dimensions of TIFF image too large");
+               TIFFClose(tiff);
+                return FALSE;                
+        }
+
+       lt->requested_width = width;
+       lt->requested_height = height;
+       lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
+
+        pixels = g_try_malloc (bytes);
+
+        if (!pixels) {
+               DEBUG_1("Insufficient memory to open TIFF file");
+               TIFFClose(tiff);
+                return FALSE;
+        }
+
+       lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
+                                           width, height, rowstride,
+                                           free_buffer, NULL);
+        if (!lt->pixbuf) {
+                g_free (pixels);
+                DEBUG_1("Insufficient memory to open TIFF file");
+               TIFFClose(tiff);
+                return FALSE;
+        }
+
+       /* Set the "orientation" key associated with this image. libtiff 
+          orientation handling is odd, so further processing is required
+          by higher-level functions based on this tag. If the embedded
+          orientation tag is 1-4, libtiff flips/mirrors the image as
+          required, and no client processing is required - so we report 
+          no orientation. Orientations 5-8 require rotations which would 
+          swap the width and height of the image. libtiff does not do this. 
+          Instead it interprets orientations 5-8 the same as 1-4. 
+          See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
+          To correct for this, the client must apply the transform normally
+          used for orientation 5 to both orientations 5 and 7, and apply
+          the transform normally used for orientation 7 for both
+          orientations 6 and 8. Then everythings works out OK! */
+       
+       TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
+
+       switch (orientation) {
+               case 5:
+               case 7:
+                       transform = 5;
+                       break;
+               case 6:
+               case 8:
+                       transform = 7;
+                       break;
+               default:
+                       transform = 0;
+                       break;
+       }
+
+
+       lt->area_prepared_cb(loader, lt->data);
+
+
+
+       if( TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) ) 
+               {
+               /* read by strip */
+               int row;
+               guchar *wrk_line = (guchar *)g_malloc(width * sizeof (uint32));
+
+
+               for( row = 0; row < height; row += rowsperstrip )
+                       {
+                       int rows_to_write, i_row;
+                       
+                       if (lt->abort) {
+                               break;
+                       }
+
+                       /* Read the strip into an RGBA array */
+                       if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
+                               break;
+                       }
+
+                       /*
+                        * Figure out the number of scanlines actually in this strip.
+                       */
+                       if( row + (int)rowsperstrip > height )
+                               rows_to_write = height - row;
+                       else
+                               rows_to_write = rowsperstrip;
+
+
+
+                       /*
+                        * For some reason the TIFFReadRGBAStrip() function chooses the
+                        * lower left corner as the origin.  Vertically mirror scanlines.
+                        */
+                       for( i_row = 0; i_row < rows_to_write / 2; i_row++ )
+                               {
+                               guchar *top_line, *bottom_line;
+
+                               top_line = pixels + (row + i_row) * rowstride;
+                               bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride; 
+
+                               memcpy(wrk_line, top_line, 4*width);
+                               memcpy(top_line, bottom_line, 4*width);
+                               memcpy(bottom_line, wrk_line, 4*width);
+                       }
+                       lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
+                       }
+               g_free(wrk_line);
+               }
+       else
+               {
+               /* fallback, tiled tiff */
+               if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1)) 
+                       {
+                       TIFFClose(tiff);
+                       return FALSE;
+                       }
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+               /* Turns out that the packing used by TIFFRGBAImage depends on 
+                * the host byte order... 
+                */ 
+               while (pixels < pixbuf->pixels + bytes) 
+                       {
+                       uint32 pixel = *(uint32 *)pixels;
+                       int r = TIFFGetR(pixel);
+                       int g = TIFFGetG(pixel);
+                       int b = TIFFGetB(pixel);
+                       int a = TIFFGetA(pixel);
+                       *pixels++ = r;
+                       *pixels++ = g;
+                       *pixels++ = b;
+                       *pixels++ = a;
+                       }
+#endif
+
+               lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
+               }
+       TIFFClose(tiff);
+
+       return TRUE;
+}
+
+
+static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
+{
+        ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
+        
+       loader->area_updated_cb = area_updated_cb;
+       loader->size_cb = size_cb;
+       loader->area_prepared_cb = area_prepared_cb;
+       loader->data = data;
+       return (gpointer) loader;
+}
+
+
+static void image_loader_tiff_set_size(gpointer loader, int width, int height)
+{
+       ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+       lt->requested_width = width;
+       lt->requested_height = height;
+}
+
+static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
+{
+       ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+       return lt->pixbuf;
+}
+
+static gchar* image_loader_tiff_get_format_name(gpointer loader)
+{
+       return g_strdup("tiff");
+}
+static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
+{
+       static gchar *mime[] = {"image/tiff", NULL};
+       return g_strdupv(mime);
+}
+
+static gboolean image_loader_tiff_close(gpointer loader, GError **error)
+{
+       return TRUE;
+}
+
+static void image_loader_tiff_abort(gpointer loader)
+{
+       ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+       lt->abort = TRUE;
+}
+
+static void image_loader_tiff_free(gpointer loader)
+{
+       ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
+       if (lt->pixbuf) g_object_unref(lt->pixbuf);
+       g_free(lt);
+}
+
+
+void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
+{
+       funcs->loader_new = image_loader_tiff_new;
+       funcs->set_size = image_loader_tiff_set_size;
+       funcs->load = image_loader_tiff_load;
+       funcs->write = NULL;
+       funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
+       funcs->close = image_loader_tiff_close;
+       funcs->abort = image_loader_tiff_abort;
+       funcs->free = image_loader_tiff_free;
+       
+       funcs->get_format_name = image_loader_tiff_get_format_name;
+       funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
+}
+
+
+
+#endif
\ No newline at end of file
diff --git a/src/image_load_tiff.h b/src/image_load_tiff.h
new file mode 100644 (file)
index 0000000..59898fc
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Geeqie
+ * (C) 2004 John Ellis
+ * Copyright (C) 2008 - 2011 The Geeqie Team
+ *
+ * Author: Vladimir Nadvornik
+ *
+ * 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!
+ */
+
+#ifndef IMAGE_LOAD_TIFF_H
+#define IMAGE_LOAD_TIFF_H
+
+#ifdef HAVE_TIFF
+void image_loader_backend_set_tiff(ImageLoaderBackend *funcs);
+#endif
+
+#endif
+