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