From: Vladimir Nadvornik Date: Thu, 29 Sep 2011 15:14:36 +0000 (+0200) Subject: added custom tiff loader X-Git-Tag: 1.1~38 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=4f028fb255a5edaeb2aec25d2e87ef4b8a2aa44d added custom tiff loader --- diff --git a/configure.in b/configure.in index 5cffbf58..d24f598a 100644 --- a/configure.in +++ b/configure.in @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index 237e6f29..02d31a67 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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) diff --git a/src/image-load.c b/src/image-load.c index 157756b6..167ca89e 100644 --- a/src/image-load.c +++ b/src/image-load.c @@ -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 index 00000000..34efc76d --- /dev/null +++ b/src/image_load_tiff.c @@ -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 + +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 index 00000000..59898fc5 --- /dev/null +++ b/src/image_load_tiff.h @@ -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 +