view src/image_load_tiff.c @ 2103:48f2a488bee6 merge-requests/3

Do not access private GdkPixbuf data The GdkPixbuf is private to Gdk, so we can not access it, rather use numbers and data we anyway have.
author Michal Čihař <mcihar@suse.cz>
date Wed, 22 Aug 2012 14:20:31 +0200
parents b141d52944ab
children
line wrap: on
line source

/*
 * 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!
 */

/* based on code from GdkPixbuf library - TIFF image loader
 *
 * Copyright (C) 1999 Mark Crichton
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Mark Crichton <crichton@gimp.org>
 *          Federico Mena-Quintero <federico@gimp.org>
 *          Jonathan Blandford <jrb@redhat.com>
 *          S�ren Sandmann <sandmann@daimi.au.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#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;
#if G_BYTE_ORDER == G_BIG_ENDIAN
    int i;
#endif

        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... 
	         */ 
        for (i = 0; i < bytes;)
			{
			uint32 pixel = *(uint32 *)(pixels + i);
			int r = TIFFGetR(pixel);
			int g = TIFFGetG(pixel);
			int b = TIFFGetB(pixel);
			int a = TIFFGetA(pixel);
			pixels[i++] = r;
			pixels[i++] = g;
			pixels[i++] = b;
			pixels[i++] = 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