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