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