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