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