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