Trim trailing white spaces.
[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                 {
98                 case SEEK_SET:
99                         if (offset > context->used)
100                                 return -1;
101                         context->pos = offset;
102                         break;
103                 case SEEK_CUR:
104                         if (offset + context->pos >= context->used)
105                                 return -1;
106                         context->pos += offset;
107                         break;
108                 case SEEK_END:
109                         if (offset + context->used > context->used)
110                                 return -1;
111                         context->pos = context->used + offset;
112                         break;
113                 default:
114                         return -1;
115                 }
116
117         return context->pos;
118 }
119
120 static int
121 tiff_load_close (thandle_t context)
122 {
123         return 0;
124 }
125
126 static toff_t
127 tiff_load_size (thandle_t handle)
128 {
129         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
130         return context->used;
131 }
132
133 static int
134 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
135 {
136         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
137
138         *buf = (tdata_t *) context->buffer;
139         *size = context->used;
140
141         return 0;
142 }
143
144 static void
145 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
146 {
147 }
148
149 static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
150 {
151         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
152
153         TIFF *tiff;
154         guchar *pixels = NULL;
155         gint width, height, rowstride, bytes;
156         uint32 rowsperstrip;
157
158         lt->buffer = buf;
159         lt->used = count;
160         lt->pos = 0;
161
162         TIFFSetWarningHandler(NULL);
163
164         tiff = TIFFClientOpen ( "libtiff-geeqie", "r", lt,
165                                                         tiff_load_read, tiff_load_write,
166                                                         tiff_load_seek, tiff_load_close,
167                                                         tiff_load_size,
168                                                         tiff_load_map_file, tiff_load_unmap_file);
169         if (!tiff)
170                 {
171                 DEBUG_1("Failed to open TIFF image");
172                 return FALSE;
173                 }
174
175
176         if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width))
177                 {
178                 DEBUG_1("Could not get image width (bad TIFF file)");
179                 TIFFClose(tiff);
180                 return FALSE;
181                 }
182
183         if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height))
184                 {
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                 {
192                 DEBUG_1("Width or height of TIFF image is zero");
193                 TIFFClose(tiff);
194                 return FALSE;
195                 }
196
197         rowstride = width * 4;
198         if (rowstride / 4 != width)
199                 { /* overflow */
200                 DEBUG_1("Dimensions of TIFF image too large");
201                 TIFFClose(tiff);
202                 return FALSE;
203                 }
204
205         bytes = height * rowstride;
206         if (bytes / rowstride != height)
207                 { /* overflow */
208                 DEBUG_1("Dimensions of TIFF image too large");
209                 TIFFClose(tiff);
210                 return FALSE;
211                 }
212
213         lt->requested_width = width;
214         lt->requested_height = height;
215         lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
216
217         pixels = g_try_malloc (bytes);
218
219         if (!pixels)
220                 {
221                 DEBUG_1("Insufficient memory to open TIFF file");
222                 TIFFClose(tiff);
223                 return FALSE;
224                 }
225
226         lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
227                                                                                    width, height, rowstride,
228                                                                                    free_buffer, NULL);
229         if (!lt->pixbuf)
230                 {
231                 g_free (pixels);
232                 DEBUG_1("Insufficient memory to open TIFF file");
233                 TIFFClose(tiff);
234                 return FALSE;
235                 }
236
237         lt->area_prepared_cb(loader, lt->data);
238
239         if (TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip))
240                 {
241                 /* read by strip */
242                 int row;
243                 guchar *wrk_line = (guchar *)g_malloc(width * sizeof (uint32));
244
245                 for (row = 0; row < height; row += rowsperstrip)
246                         {
247                         int rows_to_write, i_row;
248                         
249                         if (lt->abort) {
250                                 break;
251                         }
252
253                         /* Read the strip into an RGBA array */
254                         if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
255                                 break;
256                         }
257
258                         /*
259                          * Figure out the number of scanlines actually in this strip.
260                         */
261                         if (row + (int)rowsperstrip > height)
262                                 rows_to_write = height - row;
263                         else
264                                 rows_to_write = rowsperstrip;
265
266                         /*
267                          * For some reason the TIFFReadRGBAStrip() function chooses the
268                          * lower left corner as the origin.  Vertically mirror scanlines.
269                          */
270                         for (i_row = 0; i_row < rows_to_write / 2; i_row++)
271                                 {
272                                 guchar *top_line, *bottom_line;
273
274                                 top_line = pixels + (row + i_row) * rowstride;
275                                 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride;
276
277                                 memcpy(wrk_line, top_line, 4*width);
278                                 memcpy(top_line, bottom_line, 4*width);
279                                 memcpy(bottom_line, wrk_line, 4*width);
280                                 }
281                         lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
282                         }
283                 g_free(wrk_line);
284                 }
285         else
286                 {
287                 /* fallback, tiled tiff */
288                 if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
289                         {
290                         TIFFClose(tiff);
291                         return FALSE;
292                         }
293
294 #if G_BYTE_ORDER == G_BIG_ENDIAN
295                 /* Turns out that the packing used by TIFFRGBAImage depends on
296                  * the host byte order...
297                  */
298                 while (pixels < lt->pixbuf->pixels + bytes)
299                         {
300                         uint32 pixel = *(uint32 *)pixels;
301                         int r = TIFFGetR(pixel);
302                         int g = TIFFGetG(pixel);
303                         int b = TIFFGetB(pixel);
304                         int a = TIFFGetA(pixel);
305                         *pixels++ = r;
306                         *pixels++ = g;
307                         *pixels++ = b;
308                         *pixels++ = a;
309                         }
310 #endif
311
312                 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
313                 }
314         TIFFClose(tiff);
315
316         return TRUE;
317 }
318
319
320 static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
321 {
322         ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
323
324         loader->area_updated_cb = area_updated_cb;
325         loader->size_cb = size_cb;
326         loader->area_prepared_cb = area_prepared_cb;
327         loader->data = data;
328         return (gpointer) loader;
329 }
330
331
332 static void image_loader_tiff_set_size(gpointer loader, int width, int height)
333 {
334         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
335         lt->requested_width = width;
336         lt->requested_height = height;
337 }
338
339 static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
340 {
341         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
342         return lt->pixbuf;
343 }
344
345 static gchar* image_loader_tiff_get_format_name(gpointer loader)
346 {
347         return g_strdup("tiff");
348 }
349 static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
350 {
351         static gchar *mime[] = {"image/tiff", NULL};
352         return g_strdupv(mime);
353 }
354
355 static gboolean image_loader_tiff_close(gpointer loader, GError **error)
356 {
357         return TRUE;
358 }
359
360 static void image_loader_tiff_abort(gpointer loader)
361 {
362         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
363         lt->abort = TRUE;
364 }
365
366 static void image_loader_tiff_free(gpointer loader)
367 {
368         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
369         if (lt->pixbuf) g_object_unref(lt->pixbuf);
370         g_free(lt);
371 }
372
373
374 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
375 {
376         funcs->loader_new = image_loader_tiff_new;
377         funcs->set_size = image_loader_tiff_set_size;
378         funcs->load = image_loader_tiff_load;
379         funcs->write = NULL;
380         funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
381         funcs->close = image_loader_tiff_close;
382         funcs->abort = image_loader_tiff_abort;
383         funcs->free = image_loader_tiff_free;
384         
385         funcs->get_format_name = image_loader_tiff_get_format_name;
386         funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
387 }
388
389
390
391 #endif
392 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */