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