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.
32 #include "image-load-tiff.h"
38 #include <gdk-pixbuf/gdk-pixbuf.h>
39 #include <glib-object.h>
45 #include "image-load.h"
50 struct ImageLoaderTiff {
51 ImageLoaderBackendCbAreaUpdated area_updated_cb;
52 ImageLoaderBackendCbSize size_cb;
53 ImageLoaderBackendCbAreaPrepared area_prepared_cb;
58 guint requested_width;
59 guint requested_height;
70 void free_buffer (guchar *pixels, gpointer)
75 tsize_t tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
77 auto context = static_cast<ImageLoaderTiff *>(handle);
79 if (context->pos + size > context->used)
82 memcpy (buf, context->buffer + context->pos, size);
87 tsize_t tiff_load_write (thandle_t, tdata_t, tsize_t)
92 toff_t tiff_load_seek (thandle_t handle, toff_t offset, int whence)
94 auto context = static_cast<ImageLoaderTiff *>(handle);
99 if (offset > context->used)
101 context->pos = offset;
104 if (offset + context->pos >= context->used)
106 context->pos += offset;
109 if (offset + context->used > context->used)
111 context->pos = context->used + offset;
120 int tiff_load_close (thandle_t)
125 toff_t tiff_load_size (thandle_t handle)
127 auto context = static_cast<ImageLoaderTiff *>(handle);
128 return context->used;
131 int tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
133 auto context = static_cast<ImageLoaderTiff *>(handle);
135 *buf = const_cast<guchar *>(context->buffer);
136 *size = context->used;
141 void tiff_load_unmap_file (thandle_t, tdata_t, toff_t)
145 gboolean image_loader_tiff_write (gpointer loader, const guchar *buf, gsize &chunk_size, gsize count, GError **)
147 auto lt = static_cast<ImageLoaderTiff *>(loader);
150 guchar *pixels = nullptr;
155 guint32 rowsperstrip;
162 TIFFSetWarningHandler(nullptr);
164 tiff = TIFFClientOpen ( "libtiff-geeqie", "r", lt,
165 tiff_load_read, tiff_load_write,
166 tiff_load_seek, tiff_load_close,
168 tiff_load_map_file, tiff_load_unmap_file);
171 DEBUG_1("Failed to open TIFF image");
177 } while (TIFFReadDirectory(tiff));
179 lt->page_total = dircount;
181 if (!TIFFSetDirectory(tiff, lt->page_num))
183 DEBUG_1("Failed to open TIFF image");
188 if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width))
190 DEBUG_1("Could not get image width (bad TIFF file)");
195 if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height))
197 DEBUG_1("Could not get image height (bad TIFF file)");
202 if (width <= 0 || height <= 0)
204 DEBUG_1("Width or height of TIFF image is zero");
209 rowstride = width * 4;
210 if (rowstride / 4 != width)
212 DEBUG_1("Dimensions of TIFF image too large: width %d", width);
217 bytes = static_cast<size_t>(height) * rowstride;
218 if (bytes / rowstride != static_cast<size_t>(height))
220 DEBUG_1("Dimensions of TIFF image too large: height %d", height);
225 lt->requested_width = width;
226 lt->requested_height = height;
227 lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
229 pixels = static_cast<guchar *>(g_try_malloc (bytes));
233 DEBUG_1("Insufficient memory to open TIFF file: need %zu", bytes);
238 lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
239 width, height, rowstride,
240 free_buffer, nullptr);
244 DEBUG_1("Insufficient memory to open TIFF file");
249 lt->area_prepared_cb(loader, lt->data);
251 if (TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip))
255 const size_t line_bytes = width * sizeof(guint32);
256 auto wrk_line = static_cast<guchar *>(g_malloc(line_bytes));
258 for (row = 0; row < height; row += rowsperstrip)
267 /* Read the strip into an RGBA array */
268 if (!TIFFReadRGBAStrip(tiff, row, reinterpret_cast<guint32 *>(pixels + row * rowstride))) {
273 * Figure out the number of scanlines actually in this strip.
275 if (row + static_cast<int>(rowsperstrip) > height)
276 rows_to_write = height - row;
278 rows_to_write = rowsperstrip;
281 * For some reason the TIFFReadRGBAStrip() function chooses the
282 * lower left corner as the origin. Vertically mirror scanlines.
284 for (i_row = 0; i_row < rows_to_write / 2; i_row++)
289 top_line = pixels + (row + i_row) * rowstride;
290 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride;
292 memcpy(wrk_line, top_line, line_bytes);
293 memcpy(top_line, bottom_line, line_bytes);
294 memcpy(bottom_line, wrk_line, line_bytes);
296 lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
302 /* fallback, tiled tiff */
303 if (!TIFFReadRGBAImageOriented (tiff, width, height, reinterpret_cast<guint32 *>(pixels), ORIENTATION_TOPLEFT, 1))
309 #if G_BYTE_ORDER == G_BIG_ENDIAN
310 /* Turns out that the packing used by TIFFRGBAImage depends on
311 * the host byte order...
314 guchar *ptr = pixels;
315 while (ptr < pixels + bytes)
317 guint32 pixel = *(guint32 *)ptr;
318 int r = TIFFGetR(pixel);
319 int g = TIFFGetG(pixel);
320 int b = TIFFGetB(pixel);
321 int a = TIFFGetA(pixel);
330 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
339 gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
341 auto loader = g_new0(ImageLoaderTiff, 1);
343 loader->area_updated_cb = area_updated_cb;
344 loader->size_cb = size_cb;
345 loader->area_prepared_cb = area_prepared_cb;
351 void image_loader_tiff_set_size(gpointer loader, int width, int height)
353 auto lt = static_cast<ImageLoaderTiff *>(loader);
354 lt->requested_width = width;
355 lt->requested_height = height;
358 GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
360 auto lt = static_cast<ImageLoaderTiff *>(loader);
364 gchar* image_loader_tiff_get_format_name(gpointer)
366 return g_strdup("tiff");
369 gchar** image_loader_tiff_get_format_mime_types(gpointer)
371 static const gchar *mime[] = {"image/tiff", nullptr};
372 return g_strdupv(const_cast<gchar **>(mime));
375 gboolean image_loader_tiff_close(gpointer, GError **)
380 void image_loader_tiff_abort(gpointer loader)
382 auto lt = static_cast<ImageLoaderTiff *>(loader);
386 void image_loader_tiff_free(gpointer loader)
388 auto lt = static_cast<ImageLoaderTiff *>(loader);
389 if (lt->pixbuf) g_object_unref(lt->pixbuf);
393 void image_loader_tiff_set_page_num(gpointer loader, gint page_num)
395 auto lt = static_cast<ImageLoaderTiff *>(loader);
397 lt->page_num = page_num;
400 gint image_loader_tiff_get_page_total(gpointer loader)
402 auto lt = static_cast<ImageLoaderTiff *>(loader);
404 return lt->page_total;
409 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
411 funcs->loader_new = image_loader_tiff_new;
412 funcs->set_size = image_loader_tiff_set_size;
413 funcs->write = image_loader_tiff_write;
414 funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
415 funcs->close = image_loader_tiff_close;
416 funcs->abort = image_loader_tiff_abort;
417 funcs->free = image_loader_tiff_free;
419 funcs->get_format_name = image_loader_tiff_get_format_name;
420 funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
422 funcs->set_page_num = image_loader_tiff_set_page_num;
423 funcs->get_page_total = image_loader_tiff_get_page_total;
427 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */