updated copyright notice
[geeqie.git] / src / image_load_tiff.c
1 /*
2  * Geeqie
3  * (C) 2004 John Ellis
4  * Copyright (C) 2008 - 2011 The Geeqie Team
5  *
6  * Author: Vladimir Nadvornik
7  *
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!
11  */
12
13 /* based on code from GdkPixbuf library - TIFF image loader
14  *
15  * Copyright (C) 1999 Mark Crichton
16  * Copyright (C) 1999 The Free Software Foundation
17  *
18  * Authors: Mark Crichton <crichton@gimp.org>
19  *          Federico Mena-Quintero <federico@gimp.org>
20  *          Jonathan Blandford <jrb@redhat.com>
21  *          S�ren Sandmann <sandmann@daimi.au.dk>
22  *
23  * This library is free software; you can redistribute it and/or
24  * modify it under the terms of the GNU Lesser General Public
25  * License as published by the Free Software Foundation; either
26  * version 2 of the License, or (at your option) any later version.
27  *
28  * This library is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
31  * Lesser General Public License for more details.
32  *
33  * You should have received a copy of the GNU Lesser General Public
34  * License along with this library; if not, write to the
35  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
36  * Boston, MA 02111-1307, USA.
37  */
38
39 #include "main.h"
40
41 #include "image-load.h"
42 #include "image_load_tiff.h"
43
44 #ifdef HAVE_TIFF
45
46 #include <tiffio.h>
47
48 typedef struct _ImageLoaderTiff ImageLoaderTiff;
49 struct _ImageLoaderTiff {
50         ImageLoaderBackendCbAreaUpdated area_updated_cb;
51         ImageLoaderBackendCbSize size_cb;
52         ImageLoaderBackendCbAreaPrepared area_prepared_cb;
53         
54         gpointer data;
55
56         GdkPixbuf *pixbuf;
57         guint requested_width;
58         guint requested_height;
59         
60         gboolean abort;
61
62         const guchar *buffer;
63         toff_t used;
64         toff_t pos;
65 };
66
67 static void free_buffer (guchar *pixels, gpointer data)
68 {
69         g_free (pixels);
70 }
71
72 static tsize_t
73 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
74 {
75         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
76         
77         if (context->pos + size > context->used)
78                 return 0;
79         
80         memcpy (buf, context->buffer + context->pos, size);
81         context->pos += size;
82         return size;
83 }
84
85 static tsize_t
86 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
87 {
88         return -1;
89 }
90
91 static toff_t
92 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
93 {
94         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
95         
96         switch (whence) {
97         case SEEK_SET:
98                 if (offset > context->used)
99                         return -1;
100                 context->pos = offset;
101                 break;
102         case SEEK_CUR:
103                 if (offset + context->pos >= context->used)
104                         return -1;
105                 context->pos += offset;
106                 break;
107         case SEEK_END:
108                 if (offset + context->used > context->used)
109                         return -1;
110                 context->pos = context->used + offset;
111                 break;
112         default:
113                 return -1;
114         }
115         return context->pos;
116 }
117
118 static int
119 tiff_load_close (thandle_t context)
120 {
121         return 0;
122 }
123
124 static toff_t
125 tiff_load_size (thandle_t handle)
126 {
127         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
128         
129         return context->used;
130 }
131
132 static int
133 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
134 {
135         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
136         
137         *buf = (tdata_t *) context->buffer;
138         *size = context->used;
139         
140         return 0;
141 }
142
143 static void
144 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
145 {
146 }
147
148 static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
149 {
150         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
151
152         TIFF *tiff;
153         guchar *pixels = NULL;
154         gint width, height, rowstride, bytes;
155         uint16 orientation = 0;
156         uint16 transform = 0;
157         uint32 rowsperstrip;
158
159         lt->buffer = buf;
160         lt->used = count;
161         lt->pos = 0;
162
163         TIFFSetWarningHandler(NULL);
164
165         tiff = TIFFClientOpen ("libtiff-geeqie", "r", lt, 
166                                tiff_load_read, tiff_load_write, 
167                                tiff_load_seek, tiff_load_close, 
168                                tiff_load_size, 
169                                tiff_load_map_file, tiff_load_unmap_file);
170         if (!tiff) 
171                 {
172                 DEBUG_1("Failed to open TIFF image");
173                 return FALSE;
174                 }
175
176
177
178         if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width)) {
179                 DEBUG_1("Could not get image width (bad TIFF file)");
180                 TIFFClose(tiff);
181                 return FALSE;
182         }
183         
184         if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height)) {
185                 DEBUG_1("Could not get image height (bad TIFF file)");
186                 TIFFClose(tiff);
187                 return FALSE;
188         }
189
190         if (width <= 0 || height <= 0) {
191                 DEBUG_1("Width or height of TIFF image is zero");
192                 TIFFClose(tiff);
193                 return FALSE;                
194         }
195         
196         rowstride = width * 4;
197         if (rowstride / 4 != width) { /* overflow */
198                 DEBUG_1("Dimensions of TIFF image too large");
199                 TIFFClose(tiff);
200                 return FALSE;                
201         }
202         
203         bytes = height * rowstride;
204         if (bytes / rowstride != height) { /* overflow */
205                 DEBUG_1("Dimensions of TIFF image too large");
206                 TIFFClose(tiff);
207                 return FALSE;                
208         }
209
210         lt->requested_width = width;
211         lt->requested_height = height;
212         lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
213
214         pixels = g_try_malloc (bytes);
215
216         if (!pixels) {
217                 DEBUG_1("Insufficient memory to open TIFF file");
218                 TIFFClose(tiff);
219                 return FALSE;
220         }
221
222         lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8, 
223                                            width, height, rowstride,
224                                            free_buffer, NULL);
225         if (!lt->pixbuf) {
226                 g_free (pixels);
227                 DEBUG_1("Insufficient memory to open TIFF file");
228                 TIFFClose(tiff);
229                 return FALSE;
230         }
231
232         /* Set the "orientation" key associated with this image. libtiff 
233            orientation handling is odd, so further processing is required
234            by higher-level functions based on this tag. If the embedded
235            orientation tag is 1-4, libtiff flips/mirrors the image as
236            required, and no client processing is required - so we report 
237            no orientation. Orientations 5-8 require rotations which would 
238            swap the width and height of the image. libtiff does not do this. 
239            Instead it interprets orientations 5-8 the same as 1-4. 
240            See http://bugzilla.remotesensing.org/show_bug.cgi?id=1548.
241            To correct for this, the client must apply the transform normally
242            used for orientation 5 to both orientations 5 and 7, and apply
243            the transform normally used for orientation 7 for both
244            orientations 6 and 8. Then everythings works out OK! */
245         
246         TIFFGetField (tiff, TIFFTAG_ORIENTATION, &orientation);
247
248         switch (orientation) {
249                 case 5:
250                 case 7:
251                         transform = 5;
252                         break;
253                 case 6:
254                 case 8:
255                         transform = 7;
256                         break;
257                 default:
258                         transform = 0;
259                         break;
260         }
261
262
263         lt->area_prepared_cb(loader, lt->data);
264
265
266
267         if( TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip) ) 
268                 {
269                 /* read by strip */
270                 int row;
271                 guchar *wrk_line = (guchar *)g_malloc(width * sizeof (uint32));
272
273
274                 for( row = 0; row < height; row += rowsperstrip )
275                         {
276                         int rows_to_write, i_row;
277                         
278                         if (lt->abort) {
279                                 break;
280                         }
281
282                         /* Read the strip into an RGBA array */
283                         if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
284                                 break;
285                         }
286
287                         /*
288                          * Figure out the number of scanlines actually in this strip.
289                         */
290                         if( row + (int)rowsperstrip > height )
291                                 rows_to_write = height - row;
292                         else
293                                 rows_to_write = rowsperstrip;
294
295
296
297                         /*
298                          * For some reason the TIFFReadRGBAStrip() function chooses the
299                          * lower left corner as the origin.  Vertically mirror scanlines.
300                          */
301                         for( i_row = 0; i_row < rows_to_write / 2; i_row++ )
302                                 {
303                                 guchar *top_line, *bottom_line;
304
305                                 top_line = pixels + (row + i_row) * rowstride;
306                                 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride; 
307
308                                 memcpy(wrk_line, top_line, 4*width);
309                                 memcpy(top_line, bottom_line, 4*width);
310                                 memcpy(bottom_line, wrk_line, 4*width);
311                         }
312                         lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
313                         }
314                 g_free(wrk_line);
315                 }
316         else
317                 {
318                 /* fallback, tiled tiff */
319                 if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1)) 
320                         {
321                         TIFFClose(tiff);
322                         return FALSE;
323                         }
324
325 #if G_BYTE_ORDER == G_BIG_ENDIAN
326                 /* Turns out that the packing used by TIFFRGBAImage depends on 
327                  * the host byte order... 
328                  */ 
329                 while (pixels < pixbuf->pixels + bytes) 
330                         {
331                         uint32 pixel = *(uint32 *)pixels;
332                         int r = TIFFGetR(pixel);
333                         int g = TIFFGetG(pixel);
334                         int b = TIFFGetB(pixel);
335                         int a = TIFFGetA(pixel);
336                         *pixels++ = r;
337                         *pixels++ = g;
338                         *pixels++ = b;
339                         *pixels++ = a;
340                         }
341 #endif
342
343                 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
344                 }
345         TIFFClose(tiff);
346
347         return TRUE;
348 }
349
350
351 static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
352 {
353         ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
354         
355         loader->area_updated_cb = area_updated_cb;
356         loader->size_cb = size_cb;
357         loader->area_prepared_cb = area_prepared_cb;
358         loader->data = data;
359         return (gpointer) loader;
360 }
361
362
363 static void image_loader_tiff_set_size(gpointer loader, int width, int height)
364 {
365         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
366         lt->requested_width = width;
367         lt->requested_height = height;
368 }
369
370 static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
371 {
372         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
373         return lt->pixbuf;
374 }
375
376 static gchar* image_loader_tiff_get_format_name(gpointer loader)
377 {
378         return g_strdup("tiff");
379 }
380 static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
381 {
382         static gchar *mime[] = {"image/tiff", NULL};
383         return g_strdupv(mime);
384 }
385
386 static gboolean image_loader_tiff_close(gpointer loader, GError **error)
387 {
388         return TRUE;
389 }
390
391 static void image_loader_tiff_abort(gpointer loader)
392 {
393         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
394         lt->abort = TRUE;
395 }
396
397 static void image_loader_tiff_free(gpointer loader)
398 {
399         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
400         if (lt->pixbuf) g_object_unref(lt->pixbuf);
401         g_free(lt);
402 }
403
404
405 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
406 {
407         funcs->loader_new = image_loader_tiff_new;
408         funcs->set_size = image_loader_tiff_set_size;
409         funcs->load = image_loader_tiff_load;
410         funcs->write = NULL;
411         funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
412         funcs->close = image_loader_tiff_close;
413         funcs->abort = image_loader_tiff_abort;
414         funcs->free = image_loader_tiff_free;
415         
416         funcs->get_format_name = image_loader_tiff_get_format_name;
417         funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
418 }
419
420
421
422 #endif