2 * Copyright (C) 1999 Mark Crichton
3 * Copyright (C) 1999 The Free Software Foundation
4 * Copyright (C) 2004 John Ellis
5 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * Authors: Mark Crichton <crichton@gimp.org>
8 * Federico Mena-Quintero <federico@gimp.org>
9 * Jonathan Blandford <jrb@redhat.com>
10 * S�ren Sandmann <sandmann@daimi.au.dk>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "image-load.h"
31 #include "image_load_tiff.h"
37 typedef struct _ImageLoaderTiff ImageLoaderTiff;
38 struct _ImageLoaderTiff {
39 ImageLoaderBackendCbAreaUpdated area_updated_cb;
40 ImageLoaderBackendCbSize size_cb;
41 ImageLoaderBackendCbAreaPrepared area_prepared_cb;
46 guint requested_width;
47 guint requested_height;
56 static void free_buffer (guchar *pixels, gpointer data)
62 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
64 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
66 if (context->pos + size > context->used)
69 memcpy (buf, context->buffer + context->pos, size);
75 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
81 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
83 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
88 if (offset > context->used)
90 context->pos = offset;
93 if (offset + context->pos >= context->used)
95 context->pos += offset;
98 if (offset + context->used > context->used)
100 context->pos = context->used + offset;
110 tiff_load_close (thandle_t context)
116 tiff_load_size (thandle_t handle)
118 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
119 return context->used;
123 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
125 ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
127 *buf = (tdata_t *) context->buffer;
128 *size = context->used;
134 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
138 static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
140 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
143 guchar *pixels = NULL;
144 gint width, height, rowstride;
152 TIFFSetWarningHandler(NULL);
154 tiff = TIFFClientOpen ( "libtiff-geeqie", "r", lt,
155 tiff_load_read, tiff_load_write,
156 tiff_load_seek, tiff_load_close,
158 tiff_load_map_file, tiff_load_unmap_file);
161 DEBUG_1("Failed to open TIFF image");
166 if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width))
168 DEBUG_1("Could not get image width (bad TIFF file)");
173 if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height))
175 DEBUG_1("Could not get image height (bad TIFF file)");
180 if (width <= 0 || height <= 0)
182 DEBUG_1("Width or height of TIFF image is zero");
187 rowstride = width * 4;
188 if (rowstride / 4 != width)
190 DEBUG_1("Dimensions of TIFF image too large: width %d", width);
195 bytes = (size_t) height * rowstride;
196 if (bytes / rowstride != (size_t) height)
198 DEBUG_1("Dimensions of TIFF image too large: height %d", height);
203 lt->requested_width = width;
204 lt->requested_height = height;
205 lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
207 pixels = g_try_malloc (bytes);
211 DEBUG_1("Insufficient memory to open TIFF file: need %zu", bytes);
216 lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
217 width, height, rowstride,
222 DEBUG_1("Insufficient memory to open TIFF file");
227 lt->area_prepared_cb(loader, lt->data);
229 if (TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip))
233 const size_t line_bytes = width * sizeof(uint32);
234 guchar *wrk_line = (guchar *)g_malloc(line_bytes);
236 for (row = 0; row < height; row += rowsperstrip)
238 int rows_to_write, i_row;
244 /* Read the strip into an RGBA array */
245 if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
250 * Figure out the number of scanlines actually in this strip.
252 if (row + (int)rowsperstrip > height)
253 rows_to_write = height - row;
255 rows_to_write = rowsperstrip;
258 * For some reason the TIFFReadRGBAStrip() function chooses the
259 * lower left corner as the origin. Vertically mirror scanlines.
261 for (i_row = 0; i_row < rows_to_write / 2; i_row++)
263 guchar *top_line, *bottom_line;
265 top_line = pixels + (row + i_row) * rowstride;
266 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride;
268 memcpy(wrk_line, top_line, line_bytes);
269 memcpy(top_line, bottom_line, line_bytes);
270 memcpy(bottom_line, wrk_line, line_bytes);
272 lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
278 /* fallback, tiled tiff */
279 if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
285 #if G_BYTE_ORDER == G_BIG_ENDIAN
286 /* Turns out that the packing used by TIFFRGBAImage depends on
287 * the host byte order...
290 guchar *ptr = pixels;
291 while (ptr < pixels + bytes)
293 uint32 pixel = *(uint32 *)ptr;
294 int r = TIFFGetR(pixel);
295 int g = TIFFGetG(pixel);
296 int b = TIFFGetB(pixel);
297 int a = TIFFGetA(pixel);
306 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
314 static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
316 ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
318 loader->area_updated_cb = area_updated_cb;
319 loader->size_cb = size_cb;
320 loader->area_prepared_cb = area_prepared_cb;
322 return (gpointer) loader;
326 static void image_loader_tiff_set_size(gpointer loader, int width, int height)
328 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
329 lt->requested_width = width;
330 lt->requested_height = height;
333 static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
335 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
339 static gchar* image_loader_tiff_get_format_name(gpointer loader)
341 return g_strdup("tiff");
343 static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
345 static gchar *mime[] = {"image/tiff", NULL};
346 return g_strdupv(mime);
349 static gboolean image_loader_tiff_close(gpointer loader, GError **error)
354 static void image_loader_tiff_abort(gpointer loader)
356 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
360 static void image_loader_tiff_free(gpointer loader)
362 ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
363 if (lt->pixbuf) g_object_unref(lt->pixbuf);
368 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
370 funcs->loader_new = image_loader_tiff_new;
371 funcs->set_size = image_loader_tiff_set_size;
372 funcs->load = image_loader_tiff_load;
374 funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
375 funcs->close = image_loader_tiff_close;
376 funcs->abort = image_loader_tiff_abort;
377 funcs->free = image_loader_tiff_free;
379 funcs->get_format_name = image_loader_tiff_get_format_name;
380 funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
386 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */