4 * Copyright (C) 2008 - 2011 The Geeqie Team
6 * Author: Vladimir Nadvornik
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
16 #include "image-load.h"
17 #include "image_load_tiff.h"
23 typedef struct _ImageLoaderTiff ImageLoaderTiff;
24 struct _ImageLoaderTiff {
25 ImageLoaderBackendCbAreaUpdated area_updated_cb;
26 ImageLoaderBackendCbSize size_cb;
27 ImageLoaderBackendCbAreaPrepared area_prepared_cb;
32 guint requested_width;
33 guint requested_height;
42 static void free_buffer (guchar *pixels, gpointer data)
48 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
50 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
52 if (context->pos + size > context->used)
55 memcpy (buf, context->buffer + context->pos, size);
61 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
67 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
69 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
73 if (offset > context->used)
75 context->pos = offset;
78 if (offset + context->pos >= context->used)
80 context->pos += offset;
83 if (offset + context->used > context->used)
85 context->pos = context->used + offset;
94 tiff_load_close (thandle_t context)
100 tiff_load_size (thandle_t handle)
102 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
104 return context->used;
108 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
110 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
112 *buf = (tdata_t *) context->buffer;
113 *size = context->used;
119 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
123 static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
125 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
128 guchar *pixels = NULL;
129 gint width, height, rowstride, bytes;
130 uint16 orientation = 0;
131 uint16 transform = 0;
138 TIFFSetWarningHandler(NULL);
140 tiff = TIFFClientOpen ("libtiff-geeqie", "r", lt,
141 tiff_load_read, tiff_load_write,
142 tiff_load_seek, tiff_load_close,
144 tiff_load_map_file, tiff_load_unmap_file);
147 DEBUG_1("Failed to open TIFF image");
153 if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width)) {
154 DEBUG_1("Could not get image width (bad TIFF file)");
159 if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height)) {
160 DEBUG_1("Could not get image height (bad TIFF file)");
165 if (width <= 0 || height <= 0) {
166 DEBUG_1("Width or height of TIFF image is zero");
171 rowstride = width * 4;
172 if (rowstride / 4 != width) { /* overflow */
173 DEBUG_1("Dimensions of TIFF image too large");
178 bytes = height * rowstride;
179 if (bytes / rowstride != height) { /* overflow */
180 DEBUG_1("Dimensions of TIFF image too large");
185 lt->requested_width = width;
186 lt->requested_height = height;
187 lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
189 pixels = g_try_malloc (bytes);
192 DEBUG_1("Insufficient memory to open TIFF file");
197 lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
198 width, height, rowstride,
202 DEBUG_1("Insufficient memory to open TIFF file");
207 /* Set the "orientation" key associated with this image. libtiff
208 orientation handling is odd, so further processing is required
209 by higher-level functions based on this tag. If the embedded
210 orientation tag is 1-4, libtiff flips/mirrors the image as
211 required, and no client processing is required - so we report
212 no orientation. Orientations 5-8 require rotations which would
213 swap the width and height of the image. libtiff does not do this.
214 Instead it interprets orientations 5-8 the same as 1-4.
215 See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
216 To correct for this, the client must apply the transform normally
217 used for orientation 5 to both orientations 5 and 7, and apply
218 the transform normally used for orientation 7 for both
219 orientations 6 and 8. Then everythings works out OK! */
221 TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
223 switch (orientation) {
238 lt->area_prepared_cb(loader, lt->data);
242 if( TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) )
246 guchar *wrk_line = (guchar *)g_malloc(width * sizeof (uint32));
249 for( row = 0; row < height; row += rowsperstrip )
251 int rows_to_write, i_row;
257 /* Read the strip into an RGBA array */
258 if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
263 * Figure out the number of scanlines actually in this strip.
265 if( row + (int)rowsperstrip > height )
266 rows_to_write = height - row;
268 rows_to_write = rowsperstrip;
273 * For some reason the TIFFReadRGBAStrip() function chooses the
274 * lower left corner as the origin. Vertically mirror scanlines.
276 for( i_row = 0; i_row < rows_to_write / 2; i_row++ )
278 guchar *top_line, *bottom_line;
280 top_line = pixels + (row + i_row) * rowstride;
281 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride;
283 memcpy(wrk_line, top_line, 4*width);
284 memcpy(top_line, bottom_line, 4*width);
285 memcpy(bottom_line, wrk_line, 4*width);
287 lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
293 /* fallback, tiled tiff */
294 if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
300 #if G_BYTE_ORDER == G_BIG_ENDIAN
301 /* Turns out that the packing used by TIFFRGBAImage depends on
302 * the host byte order...
304 while (pixels < pixbuf->pixels + bytes)
306 uint32 pixel = *(uint32 *)pixels;
307 int r = TIFFGetR(pixel);
308 int g = TIFFGetG(pixel);
309 int b = TIFFGetB(pixel);
310 int a = TIFFGetA(pixel);
318 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
326 static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
328 ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
330 loader->area_updated_cb = area_updated_cb;
331 loader->size_cb = size_cb;
332 loader->area_prepared_cb = area_prepared_cb;
334 return (gpointer) loader;
338 static void image_loader_tiff_set_size(gpointer loader, int width, int height)
340 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
341 lt->requested_width = width;
342 lt->requested_height = height;
345 static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
347 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
351 static gchar* image_loader_tiff_get_format_name(gpointer loader)
353 return g_strdup("tiff");
355 static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
357 static gchar *mime[] = {"image/tiff", NULL};
358 return g_strdupv(mime);
361 static gboolean image_loader_tiff_close(gpointer loader, GError **error)
366 static void image_loader_tiff_abort(gpointer loader)
368 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
372 static void image_loader_tiff_free(gpointer loader)
374 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
375 if (lt->pixbuf) g_object_unref(lt->pixbuf);
380 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
382 funcs->loader_new = image_loader_tiff_new;
383 funcs->set_size = image_loader_tiff_set_size;
384 funcs->load = image_loader_tiff_load;
386 funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
387 funcs->close = image_loader_tiff_close;
388 funcs->abort = image_loader_tiff_abort;
389 funcs->free = image_loader_tiff_free;
391 funcs->get_format_name = image_loader_tiff_get_format_name;
392 funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;