Simplify vflist_get_formatted()
[geeqie.git] / src / image_load_tiff.c
1 /*
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
6  *
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>
11  *          Vladimir Nadvornik
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #include "main.h"
29
30 #include "image-load.h"
31 #include "image_load_tiff.h"
32
33 #ifdef HAVE_TIFF
34
35 #include <tiffio.h>
36
37 typedef struct _ImageLoaderTiff ImageLoaderTiff;
38 struct _ImageLoaderTiff {
39         ImageLoaderBackendCbAreaUpdated area_updated_cb;
40         ImageLoaderBackendCbSize size_cb;
41         ImageLoaderBackendCbAreaPrepared area_prepared_cb;
42
43         gpointer data;
44
45         GdkPixbuf *pixbuf;
46         guint requested_width;
47         guint requested_height;
48
49         gboolean abort;
50
51         const guchar *buffer;
52         toff_t used;
53         toff_t pos;
54         gint page_num;
55         gint page_total;
56 };
57
58 static void free_buffer (guchar *pixels, gpointer data)
59 {
60         g_free (pixels);
61 }
62
63 static tsize_t
64 tiff_load_read (thandle_t handle, tdata_t buf, tsize_t size)
65 {
66         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
67
68         if (context->pos + size > context->used)
69                 return 0;
70
71         memcpy (buf, context->buffer + context->pos, size);
72         context->pos += size;
73         return size;
74 }
75
76 static tsize_t
77 tiff_load_write (thandle_t handle, tdata_t buf, tsize_t size)
78 {
79         return -1;
80 }
81
82 static toff_t
83 tiff_load_seek (thandle_t handle, toff_t offset, int whence)
84 {
85         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
86
87         switch (whence)
88                 {
89                 case SEEK_SET:
90                         if (offset > context->used)
91                                 return -1;
92                         context->pos = offset;
93                         break;
94                 case SEEK_CUR:
95                         if (offset + context->pos >= context->used)
96                                 return -1;
97                         context->pos += offset;
98                         break;
99                 case SEEK_END:
100                         if (offset + context->used > context->used)
101                                 return -1;
102                         context->pos = context->used + offset;
103                         break;
104                 default:
105                         return -1;
106                 }
107
108         return context->pos;
109 }
110
111 static int
112 tiff_load_close (thandle_t context)
113 {
114         return 0;
115 }
116
117 static toff_t
118 tiff_load_size (thandle_t handle)
119 {
120         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
121         return context->used;
122 }
123
124 static int
125 tiff_load_map_file (thandle_t handle, tdata_t *buf, toff_t *size)
126 {
127         ImageLoaderTiff *context = (ImageLoaderTiff *)handle;
128
129         *buf = (tdata_t *) context->buffer;
130         *size = context->used;
131
132         return 0;
133 }
134
135 static void
136 tiff_load_unmap_file (thandle_t handle, tdata_t data, toff_t offset)
137 {
138 }
139
140 static gboolean image_loader_tiff_load (gpointer loader, const guchar *buf, gsize count, GError **error)
141 {
142         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
143
144         TIFF *tiff;
145         guchar *pixels = NULL;
146         gint width, height, rowstride;
147         size_t bytes;
148         uint32 rowsperstrip;
149         gint dircount = 0;
150
151         lt->buffer = buf;
152         lt->used = count;
153         lt->pos = 0;
154
155         TIFFSetWarningHandler(NULL);
156
157         tiff = TIFFClientOpen ( "libtiff-geeqie", "r", lt,
158                                                         tiff_load_read, tiff_load_write,
159                                                         tiff_load_seek, tiff_load_close,
160                                                         tiff_load_size,
161                                                         tiff_load_map_file, tiff_load_unmap_file);
162         if (!tiff)
163                 {
164                 DEBUG_1("Failed to open TIFF image");
165                 return FALSE;
166                 }
167         else
168                 {
169                 do
170                         {
171                         dircount++;
172                         } while (TIFFReadDirectory(tiff));
173                 lt->page_total = dircount;
174                 }
175
176     if (!TIFFSetDirectory(tiff, lt->page_num))
177                 {
178                 DEBUG_1("Failed to open TIFF image");
179                 TIFFClose(tiff);
180                 return FALSE;
181                 }
182
183         if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width))
184                 {
185                 DEBUG_1("Could not get image width (bad TIFF file)");
186                 TIFFClose(tiff);
187                 return FALSE;
188                 }
189
190         if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height))
191                 {
192                 DEBUG_1("Could not get image height (bad TIFF file)");
193                 TIFFClose(tiff);
194                 return FALSE;
195                 }
196
197         if (width <= 0 || height <= 0)
198                 {
199                 DEBUG_1("Width or height of TIFF image is zero");
200                 TIFFClose(tiff);
201                 return FALSE;
202                 }
203
204         rowstride = width * 4;
205         if (rowstride / 4 != width)
206                 { /* overflow */
207                 DEBUG_1("Dimensions of TIFF image too large: width %d", width);
208                 TIFFClose(tiff);
209                 return FALSE;
210                 }
211
212         bytes = (size_t) height * rowstride;
213         if (bytes / rowstride != (size_t) height)
214                 { /* overflow */
215                 DEBUG_1("Dimensions of TIFF image too large: height %d", height);
216                 TIFFClose(tiff);
217                 return FALSE;
218                 }
219
220         lt->requested_width = width;
221         lt->requested_height = height;
222         lt->size_cb(loader, lt->requested_width, lt->requested_height, lt->data);
223
224         pixels = g_try_malloc (bytes);
225
226         if (!pixels)
227                 {
228                 DEBUG_1("Insufficient memory to open TIFF file: need %zu", bytes);
229                 TIFFClose(tiff);
230                 return FALSE;
231                 }
232
233         lt->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, TRUE, 8,
234                                                                                    width, height, rowstride,
235                                                                                    free_buffer, NULL);
236         if (!lt->pixbuf)
237                 {
238                 g_free (pixels);
239                 DEBUG_1("Insufficient memory to open TIFF file");
240                 TIFFClose(tiff);
241                 return FALSE;
242                 }
243
244         lt->area_prepared_cb(loader, lt->data);
245
246         if (TIFFGetField(tiff, TIFFTAG_ROWSPERSTRIP, &rowsperstrip))
247                 {
248                 /* read by strip */
249                 ptrdiff_t row;
250                 const size_t line_bytes = width * sizeof(uint32);
251                 guchar *wrk_line = (guchar *)g_malloc(line_bytes);
252
253                 for (row = 0; row < height; row += rowsperstrip)
254                         {
255                         int rows_to_write, i_row;
256
257                         if (lt->abort) {
258                                 break;
259                         }
260
261                         /* Read the strip into an RGBA array */
262                         if (!TIFFReadRGBAStrip(tiff, row, (uint32 *)(pixels + row * rowstride))) {
263                                 break;
264                         }
265
266                         /*
267                          * Figure out the number of scanlines actually in this strip.
268                         */
269                         if (row + (int)rowsperstrip > height)
270                                 rows_to_write = height - row;
271                         else
272                                 rows_to_write = rowsperstrip;
273
274                         /*
275                          * For some reason the TIFFReadRGBAStrip() function chooses the
276                          * lower left corner as the origin.  Vertically mirror scanlines.
277                          */
278                         for (i_row = 0; i_row < rows_to_write / 2; i_row++)
279                                 {
280                                 guchar *top_line, *bottom_line;
281
282                                 top_line = pixels + (row + i_row) * rowstride;
283                                 bottom_line = pixels + (row + rows_to_write - i_row - 1) * rowstride;
284
285                                 memcpy(wrk_line, top_line, line_bytes);
286                                 memcpy(top_line, bottom_line, line_bytes);
287                                 memcpy(bottom_line, wrk_line, line_bytes);
288                                 }
289                         lt->area_updated_cb(loader, 0, row, width, rows_to_write, lt->data);
290                         }
291                 g_free(wrk_line);
292                 }
293         else
294                 {
295                 /* fallback, tiled tiff */
296                 if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)pixels, ORIENTATION_TOPLEFT, 1))
297                         {
298                         TIFFClose(tiff);
299                         return FALSE;
300                         }
301
302 #if G_BYTE_ORDER == G_BIG_ENDIAN
303                 /* Turns out that the packing used by TIFFRGBAImage depends on
304                  * the host byte order...
305                  */
306                 {
307                 guchar *ptr = pixels;
308                 while (ptr < pixels + bytes)
309                         {
310                         uint32 pixel = *(uint32 *)ptr;
311                         int r = TIFFGetR(pixel);
312                         int g = TIFFGetG(pixel);
313                         int b = TIFFGetB(pixel);
314                         int a = TIFFGetA(pixel);
315                         *ptr++ = r;
316                         *ptr++ = g;
317                         *ptr++ = b;
318                         *ptr++ = a;
319                         }
320                 }
321 #endif
322
323                 lt->area_updated_cb(loader, 0, 0, width, height, lt->data);
324                 }
325         TIFFClose(tiff);
326
327         return TRUE;
328 }
329
330
331 static gpointer image_loader_tiff_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data)
332 {
333         ImageLoaderTiff *loader = g_new0(ImageLoaderTiff, 1);
334
335         loader->area_updated_cb = area_updated_cb;
336         loader->size_cb = size_cb;
337         loader->area_prepared_cb = area_prepared_cb;
338         loader->data = data;
339         return (gpointer) loader;
340 }
341
342
343 static void image_loader_tiff_set_size(gpointer loader, int width, int height)
344 {
345         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
346         lt->requested_width = width;
347         lt->requested_height = height;
348 }
349
350 static GdkPixbuf* image_loader_tiff_get_pixbuf(gpointer loader)
351 {
352         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
353         return lt->pixbuf;
354 }
355
356 static gchar* image_loader_tiff_get_format_name(gpointer loader)
357 {
358         return g_strdup("tiff");
359 }
360 static gchar** image_loader_tiff_get_format_mime_types(gpointer loader)
361 {
362         static gchar *mime[] = {"image/tiff", NULL};
363         return g_strdupv(mime);
364 }
365
366 static gboolean image_loader_tiff_close(gpointer loader, GError **error)
367 {
368         return TRUE;
369 }
370
371 static void image_loader_tiff_abort(gpointer loader)
372 {
373         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
374         lt->abort = TRUE;
375 }
376
377 static void image_loader_tiff_free(gpointer loader)
378 {
379         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
380         if (lt->pixbuf) g_object_unref(lt->pixbuf);
381         g_free(lt);
382 }
383
384 static void image_loader_tiff_set_page_num(gpointer loader, gint page_num)
385 {
386         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
387
388         lt->page_num = page_num;
389 }
390
391 static gint image_loader_tiff_get_page_total(gpointer loader)
392 {
393         ImageLoaderTiff *lt = (ImageLoaderTiff *) loader;
394
395         return lt->page_total;
396 }
397
398 void image_loader_backend_set_tiff(ImageLoaderBackend *funcs)
399 {
400         funcs->loader_new = image_loader_tiff_new;
401         funcs->set_size = image_loader_tiff_set_size;
402         funcs->load = image_loader_tiff_load;
403         funcs->write = NULL;
404         funcs->get_pixbuf = image_loader_tiff_get_pixbuf;
405         funcs->close = image_loader_tiff_close;
406         funcs->abort = image_loader_tiff_abort;
407         funcs->free = image_loader_tiff_free;
408
409         funcs->get_format_name = image_loader_tiff_get_format_name;
410         funcs->get_format_mime_types = image_loader_tiff_get_format_mime_types;
411
412         funcs->set_page_num = image_loader_tiff_set_page_num;
413         funcs->get_page_total = image_loader_tiff_get_page_total;
414 }
415
416
417
418 #endif
419 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */