1 /* This file is an image processing operation for GEGL
2  *
3  * GEGL is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 3 of the License, or (at your option) any later version.
7  *
8  * GEGL is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with GEGL; if not, see <https://www.gnu.org/licenses/>.
15  *
16  * Copyright 2015 Martin Blanchard <tchaik@gmx.com>
17  */
18 
19 #include "config.h"
20 #ifdef HAVE_STRPTIME
21 #define _XOPEN_SOURCE
22 #include <time.h>
23 #endif
24 #include <glib/gi18n-lib.h>
25 #include <gegl-metadata.h>
26 
27 #ifdef GEGL_PROPERTIES
28 
29 property_file_path (path, _("File"), "")
30   description (_("Path of file to load"))
31 property_uri (uri, _("URI"), "")
32   description (_("URI for file to load"))
33 
34 property_int(directory, _("Directory"), 1)
35   description (_("Image file directory (subfile)"))
36   value_range (1, G_MAXINT)
37   ui_range (1, 16)
38 
39 property_object(metadata, _("Metadata"), GEGL_TYPE_METADATA)
40   description (_("Object to receive image metadata"))
41 
42 #else
43 
44 #define GEGL_OP_SOURCE
45 #define GEGL_OP_NAME     tiff_load
46 #define GEGL_OP_C_SOURCE tiff-load.c
47 
48 
49 #include <gegl-op.h>
50 #include <gegl-gio-private.h>
51 #include <glib/gprintf.h>
52 #include <tiffio.h>
53 
54 typedef enum {
55   TIFF_LOADING_RGBA,
56   TIFF_LOADING_CONTIGUOUS,
57   TIFF_LOADING_SEPARATED
58 } LoadingMode;
59 
60 typedef struct
61 {
62   GFile *file;
63   GInputStream *stream;
64   gboolean can_seek;
65 
66   gchar *buffer;
67   gsize allocated;
68   gsize position;
69   gsize loaded;
70 
71   TIFF *tiff;
72 
73   gint directory;
74 
75   const Babl *format;
76   LoadingMode mode;
77 
78   gint width;
79   gint height;
80 } Priv;
81 
82 #ifdef HAVE_STRPTIME
83 /* Parse the TIFF timestamp format - requires strptime() */
84 static void
85 tiff_parse_timestamp (const GValue *src_value, GValue *dest_value)
86 {
87   GDateTime *datetime;
88   struct tm tm;
89   GTimeZone *tz;
90   const gchar *datestr;
91   gchar *ret;
92 
93   g_return_if_fail (G_VALUE_HOLDS_STRING (src_value));
94   g_return_if_fail (G_TYPE_CHECK_VALUE_TYPE (dest_value, G_TYPE_DATE_TIME));
95 
96   datestr = g_value_get_string (src_value);
97   g_return_if_fail (datestr != NULL);
98 
99   ret = strptime (datestr, "%Y:%m:%d %T", &tm);
100   g_return_if_fail (ret != NULL);
101 
102   tz = g_time_zone_new_local ();
103   datetime = g_date_time_new (tz, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
104                               tm.tm_hour, tm.tm_min, tm.tm_sec);
105   g_time_zone_unref (tz);
106 
107   g_return_if_fail (datetime != NULL);
108   g_value_take_boxed (dest_value, datetime);
109 }
110 #endif
111 
112 static const GeglMetadataMap tiff_load_metadata[] =
113 {
114   { "Artist",               "artist",       NULL },
115   { "Copyright",            "copyright",    NULL },
116 #ifdef HAVE_STRPTIME
117   { "DateTime",             "timestamp",    tiff_parse_timestamp },
118 #endif
119   { "ImageDescription",     "description",  NULL },
120   { "PageName",             "title",        NULL },
121   { "Software",             "software",     NULL },
122 };
123 
124 static void
125 cleanup(GeglOperation *operation)
126 {
127   GeglProperties *o = GEGL_PROPERTIES(operation);
128   Priv *p = (Priv*) o->user_data;
129 
130   if (p != NULL)
131     {
132       if (p->tiff != NULL)
133         TIFFClose(p->tiff);
134       else if (p->stream != NULL)
135         g_input_stream_close(G_INPUT_STREAM(p->stream), NULL, NULL);
136 
137       g_clear_object (&p->stream);
138       p->tiff = NULL;
139 
140       g_clear_object (&p->file);
141 
142       p->width = p->height = 0;
143       p->directory = 0;
144     }
145 }
146 
147 static GSeekType
148 lseek_to_seek_type(int whence)
149 {
150   switch (whence)
151     {
152     default:
153     case SEEK_SET:
154       return G_SEEK_SET;
155 
156     case SEEK_CUR:
157       return G_SEEK_CUR;
158 
159     case SEEK_END:
160       return G_SEEK_END;
161     }
162 }
163 
164 static void
165 error_handler(const char *module,
166               const char *format,
167               va_list arguments)
168 {
169   gchar *message;
170 
171   g_vasprintf(&message, format, arguments);
172   g_warning("%s", message);
173 
174   g_free(message);
175 }
176 
177 static void
178 warning_handler(const char *module,
179                 const char *format,
180                 va_list arguments)
181 {
182   gchar *message;
183 
184   g_vasprintf(&message, format, arguments);
185   g_message("%s", message);
186 
187   g_free(message);
188 }
189 
190 static tsize_t
191 read_from_stream(thandle_t handle,
192                  tdata_t buffer,
193                  tsize_t size)
194 {
195   Priv *p = (Priv*) handle;
196   GError *error = NULL;
197   gchar *new_buffer;
198   gsize new_size = 1;
199   gsize missing, needed;
200   gssize read = -1;
201 
202   g_assert(p->stream);
203 
204   if (p->can_seek)
205     {
206       read = g_input_stream_read(G_INPUT_STREAM(p->stream),
207                                  (void *) buffer, (gsize) size,
208                                  NULL, &error);
209       if (read < 0 && error)
210         {
211           g_warning("%s", error->message);
212           g_error_free(error);
213         }
214     }
215   else
216     {
217       if (p->position + size > p->loaded)
218         {
219           missing = p->position + size - p->loaded;
220           if (p->loaded + missing > p->allocated)
221             {
222               needed = p->loaded + missing - p->allocated;
223               while (new_size < p->allocated + needed)
224                 new_size *= 2;
225 
226               new_buffer = g_try_realloc(p->buffer, new_size);
227               if (!new_buffer)
228                 return -1;
229 
230               p->allocated = new_size;
231               p->buffer = new_buffer;
232             }
233 
234           while (missing > 0)
235             {
236               read = g_input_stream_read(G_INPUT_STREAM(p->stream),
237                                          (void *) (p->buffer + p->loaded),
238                                          missing,
239                                          NULL, &error);
240               if (read < 0)
241                 {
242                   if (error)
243                   {
244                     g_warning("%s", error->message);
245                     g_error_free(error);
246                   }
247                   break;
248                 }
249 
250               p->loaded += read;
251               missing -= read;
252             }
253         }
254 
255       g_assert(p->position + size <= p->loaded);
256 
257       memcpy(buffer, p->buffer + p->position, size);
258       p->position += size;
259       read = size;
260     }
261 
262   return (tsize_t) read;
263 }
264 
265 static tsize_t
266 write_to_stream(thandle_t handle,
267                 tdata_t buffer,
268                 tsize_t size)
269 {
270   Priv *p = (Priv*) handle;
271 
272   g_assert(p->stream && FALSE);
273 
274   return -1;
275 }
276 
277 static toff_t
278 seek_in_stream(thandle_t handle,
279                toff_t offset,
280                int whence)
281 {
282   Priv *p = (Priv*) handle;
283   GError *error = NULL;
284   gboolean sought = FALSE;
285   goffset position = -1;
286 
287   g_assert(p->stream);
288 
289   if (p->can_seek)
290     {
291       sought = g_seekable_seek(G_SEEKABLE(p->stream),
292                                (goffset) offset, lseek_to_seek_type(whence),
293                                NULL, &error);
294       if (sought)
295         position = g_seekable_tell(G_SEEKABLE(p->stream));
296       else if (error)
297         {
298           g_warning("%s", error->message);
299           g_error_free(error);
300         }
301     }
302   else
303     {
304       switch (whence)
305         {
306         default:
307         case SEEK_SET:
308           if (offset <= p->loaded)
309             position = p->position = offset;
310           break;
311 
312         case SEEK_CUR:
313           if (p->position + offset <= p->loaded)
314             position = p->position += offset;
315           break;
316 
317         case G_SEEK_END:
318           if (p->loaded + offset <= p->loaded)
319             position = p->position = p->loaded + offset;
320           break;
321         }
322     }
323 
324   return (toff_t) position;
325 }
326 
327 static int
328 close_stream(thandle_t handle)
329 {
330   Priv *p = (Priv*) handle;
331   GError *error = NULL;
332   gboolean closed = FALSE;
333 
334   g_assert(p->stream);
335 
336   closed = g_input_stream_close(G_INPUT_STREAM(p->stream),
337                                 NULL, &error);
338   if (!closed && error)
339     {
340       g_warning("%s", error->message);
341       g_error_free(error);
342     }
343 
344   g_clear_object(&p->stream);
345 
346   p->loaded = 0;
347   p->position = 0;
348 
349   g_clear_pointer(&p->buffer, g_free);
350 
351   p->allocated = 0;
352 
353   return (closed) ? 0 : -1;
354 }
355 
356 static toff_t
357 get_file_size(thandle_t handle)
358 {
359   Priv *p = (Priv*) handle;
360   GError *error = NULL;
361   GFileInfo *info;
362   goffset size;
363 
364   g_assert(p->stream);
365 
366   size = p->loaded;
367 
368   if (p->file != NULL)
369     {
370       info = g_file_query_info(p->file,
371                                G_FILE_ATTRIBUTE_STANDARD_SIZE,
372                                G_FILE_QUERY_INFO_NONE,
373                                NULL, &error);
374       if (info == NULL)
375         {
376           if (error)
377           {
378             g_warning("%s", error->message);
379             g_error_free(error);
380           }
381         }
382       else
383         {
384           if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
385             size = g_file_info_get_size(info);
386           g_object_unref(info);
387         }
388     }
389 
390   return (toff_t) size;
391 }
392 
393 static void
394 set_meta_string (GObject *metadata, const gchar *name, const gchar *value)
395 {
396   GValue gvalue = G_VALUE_INIT;
397   GeglMetadataIter iter;
398 
399   g_value_init (&gvalue, G_TYPE_STRING);
400   g_value_set_string (&gvalue, value);
401   if (gegl_metadata_iter_lookup (GEGL_METADATA (metadata), &iter, name))
402     gegl_metadata_iter_set_value (GEGL_METADATA (metadata), &iter, &gvalue);
403   g_value_unset (&gvalue);
404 }
405 
406 static gint
407 query_tiff(GeglOperation *operation)
408 {
409   GeglProperties *o = GEGL_PROPERTIES(operation);
410   Priv *p = (Priv*) o->user_data;
411   gushort color_space, compression;
412   gushort bits_per_sample, samples_per_pixel;
413   gushort sample_format;
414   gboolean has_alpha = FALSE;
415   gboolean alpha_is_premultiplied = FALSE;
416   gushort *extra_types = NULL;
417   gushort nb_extras, planar_config;
418   gboolean fallback_mode = FALSE;
419   gchar format_string[32];
420   const Babl *space = NULL;
421   guint width, height;
422 
423   g_return_val_if_fail(p->tiff != NULL, -1);
424 
425   if (!TIFFGetField(p->tiff, TIFFTAG_IMAGEWIDTH, &width))
426     {
427       g_warning("could not get TIFF image width");
428       return -1;
429     }
430   else if (!TIFFGetField(p->tiff, TIFFTAG_IMAGELENGTH, &height))
431     {
432       g_warning("could not get TIFF image height");
433       return -1;
434     }
435 
436   TIFFGetFieldDefaulted(p->tiff, TIFFTAG_COMPRESSION, &compression);
437   if (!TIFFGetField(p->tiff, TIFFTAG_PHOTOMETRIC, &color_space))
438     {
439       g_warning("could not get photometric from TIFF image");
440       if (compression == COMPRESSION_CCITTFAX3 ||
441           compression == COMPRESSION_CCITTFAX4 ||
442           compression == COMPRESSION_CCITTRLE  ||
443           compression == COMPRESSION_CCITTRLEW)
444         {
445           g_message("assuming min-is-white (CCITT compressed)");
446           color_space = PHOTOMETRIC_MINISWHITE;
447         }
448       else
449         {
450           g_message("assuming min-is-black");
451           color_space = PHOTOMETRIC_MINISBLACK;
452         }
453     }
454 
455   TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
456   if (!TIFFGetField(p->tiff, TIFFTAG_EXTRASAMPLES, &nb_extras, &extra_types))
457     nb_extras = 0;
458 
459   if (nb_extras > 0)
460     {
461       if (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)
462         {
463           has_alpha = TRUE;
464           alpha_is_premultiplied = TRUE;
465           nb_extras--;
466         }
467       else if (extra_types[0] == EXTRASAMPLE_UNASSALPHA)
468         {
469           has_alpha = TRUE;
470           alpha_is_premultiplied = FALSE;
471           nb_extras--;
472         }
473       else if (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)
474         {
475           has_alpha = TRUE;
476           alpha_is_premultiplied = FALSE;
477           nb_extras--;
478         }
479     }
480 
481   switch(color_space)
482     {
483     case PHOTOMETRIC_MINISBLACK:
484     case PHOTOMETRIC_MINISWHITE:
485       if (samples_per_pixel > 1 + nb_extras)
486         {
487           nb_extras = samples_per_pixel - 2;
488           has_alpha = TRUE;
489         }
490 
491       if (has_alpha)
492         {
493           if(alpha_is_premultiplied)
494             g_strlcpy(format_string, "Y'aA ", 32);
495           else
496             g_strlcpy(format_string, "Y'A ", 32);
497         }
498       else
499         g_strlcpy(format_string, "Y' ", 32);
500       break;
501 
502     case PHOTOMETRIC_RGB:
503       if (samples_per_pixel > 3 + nb_extras)
504         {
505           nb_extras = samples_per_pixel - 4;
506           has_alpha = TRUE;
507         }
508 
509       if (has_alpha)
510         {
511           if (alpha_is_premultiplied)
512             g_strlcpy(format_string, "R'aG'aB'aA ", 32);
513           else
514             g_strlcpy(format_string, "R'G'B'A ", 32);
515         }
516       else
517         g_strlcpy(format_string, "R'G'B' ", 32);
518       break;
519     case PHOTOMETRIC_SEPARATED:
520       if (samples_per_pixel > 4 + nb_extras)
521         {
522           nb_extras = samples_per_pixel - 5;
523           has_alpha = TRUE;
524         }
525 #if 0
526       if (has_alpha)
527         {
528           if (alpha_is_premultiplied)
529             g_strlcpy(format_string, "camayakaA ", 32);
530           else
531             g_strlcpy(format_string, "cmykA ", 32);
532         }
533       else
534         g_strlcpy(format_string, "cmyk ", 32);
535 #else
536       if (has_alpha)
537         {
538           if (alpha_is_premultiplied)
539             g_strlcpy(format_string, "CaMaYaKaA ", 32);
540           else
541             g_strlcpy(format_string, "CMYKA ", 32);
542         }
543       else
544         g_strlcpy(format_string, "CMYK ", 32);
545 #endif
546 
547       break;
548     default:
549       fallback_mode = TRUE;
550       break;
551     }
552 
553   TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLEFORMAT, &sample_format);
554   TIFFGetFieldDefaulted(p->tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
555 
556   switch(bits_per_sample)
557     {
558     case 8:
559       g_strlcat(format_string, "u8", 32);
560       break;
561 
562     case 16:
563       if (sample_format == SAMPLEFORMAT_IEEEFP)
564         g_strlcat(format_string, "half", 32);
565       else
566         g_strlcat(format_string, "u16", 32);
567       break;
568 
569     case 32:
570       if (sample_format == SAMPLEFORMAT_IEEEFP)
571         g_strlcat(format_string, "float", 32);
572       else
573         g_strlcat(format_string, "u32", 32);
574       break;
575 
576     case 64:
577       g_strlcat(format_string, "double", 32);
578       break;
579 
580     default:
581       fallback_mode = TRUE;
582       break;
583     }
584 
585   if (fallback_mode == TRUE)
586     g_strlcpy(format_string, "R'aG'aB'aA u8", 32);
587 
588   TIFFGetFieldDefaulted(p->tiff, TIFFTAG_PLANARCONFIG, &planar_config);
589 
590   {
591     uint32  profile_size;
592     guchar *icc_profile;
593 
594     /* set the ICC profile - if found in the TIFF */
595     if (TIFFGetField (p->tiff, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile))
596       {
597         const char *error = NULL;
598         space = babl_space_from_icc ((char*)icc_profile, (gint)profile_size,
599                                      BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error);
600         if (error)
601           g_warning ("error creating space from icc: %s\n", error);
602       }
603 
604   }
605 
606   p->format = babl_format_with_space (format_string, space);
607   if (fallback_mode)
608     p->mode = TIFF_LOADING_RGBA;
609   else if (planar_config == PLANARCONFIG_CONTIG)
610     p->mode = TIFF_LOADING_CONTIGUOUS;
611   else
612     p->mode = TIFF_LOADING_SEPARATED;
613 
614   p->height = (gint) height;
615   p->width = (gint) width;
616 
617   if (o->metadata != NULL)
618     {
619       gfloat resx = 300.0f, resy = 300.0f;
620       gboolean have_x, have_y;
621       guint16 unit;
622       gchar *str;
623       GeglResolutionUnit resunit;
624 
625       gegl_metadata_register_map (GEGL_METADATA (o->metadata),
626                                   "gegl:tiff-load",
627                                   GEGL_MAP_EXCLUDE_UNMAPPED,
628                                   tiff_load_metadata,
629                                   G_N_ELEMENTS (tiff_load_metadata));
630 
631       TIFFGetFieldDefaulted (p->tiff, TIFFTAG_RESOLUTIONUNIT, &unit);
632       have_x = TIFFGetField (p->tiff, TIFFTAG_XRESOLUTION, &resx);
633       have_y = TIFFGetField (p->tiff, TIFFTAG_YRESOLUTION, &resy);
634       if (!have_x && have_y)
635         resx = resy;
636       else if (have_x && !have_y)
637         resy = resx;
638 
639       switch (unit)
640         {
641         case RESUNIT_INCH:
642           resunit = GEGL_RESOLUTION_UNIT_DPI;
643           break;
644         case RESUNIT_CENTIMETER:
645           resunit = GEGL_RESOLUTION_UNIT_DPM;
646           resx *= 100.0f;
647           resy *= 100.0f;
648           break;
649         default:
650           resunit = GEGL_RESOLUTION_UNIT_NONE;
651           break;
652         }
653       gegl_metadata_set_resolution (GEGL_METADATA (o->metadata), resunit, resx, resy);
654 
655       //XXX make and model for scanner
656 
657       if (TIFFGetField (p->tiff, TIFFTAG_ARTIST, &str))
658         set_meta_string (o->metadata, "Artist", str);
659       if (TIFFGetField (p->tiff, TIFFTAG_COPYRIGHT, &str))
660         set_meta_string (o->metadata, "Copyright", str);
661       if (TIFFGetField (p->tiff, TIFFTAG_PAGENAME, &str))
662         set_meta_string (o->metadata, "PageName", str);
663       if (TIFFGetField (p->tiff, TIFFTAG_SOFTWARE, &str))
664         set_meta_string (o->metadata, "Software", str);
665       if (TIFFGetField (p->tiff, TIFFTAG_IMAGEDESCRIPTION, &str))
666         set_meta_string (o->metadata, "ImageDescription", str);
667       if (TIFFGetField (p->tiff, TIFFTAG_DATETIME, &str))
668         set_meta_string (o->metadata, "DateTime", str);
669 
670       gegl_metadata_unregister_map (GEGL_METADATA (o->metadata));
671     }
672 
673   return 0;
674 }
675 
676 static gint
677 load_RGBA(GeglOperation *operation,
678           GeglBuffer    *output)
679 {
680   GeglProperties *o = GEGL_PROPERTIES(operation);
681   Priv *p = (Priv*) o->user_data;
682   guint32 *buffer;
683   gint row;
684 
685   g_return_val_if_fail(p->tiff != NULL, -1);
686 
687   buffer = g_try_new(guint32, p->width * p->height * sizeof(guint32));
688 
689   g_assert(buffer != NULL);
690 
691   if (!TIFFReadRGBAImage(p->tiff, p->width, p->height, buffer, 0))
692     {
693       g_message("unsupported layout, RGBA loader failed");
694       g_free(buffer);
695       return -1;
696     }
697 
698   for (row = 0; row < p->height; row++)
699     {
700       GeglRectangle line = { 0, p->height - row - 1, p->width, 1 };
701 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
702       guint row_start = row * p->width;
703       guint row_end = row * p->width + p->width;
704       guint i;
705 
706       for (i = row_start; i < row_end; i++)
707         buffer[i] = GUINT32_TO_LE(buffer[i]);
708 #endif
709 
710       gegl_buffer_set(output, &line, 0, p->format,
711                       ((guchar *) buffer) + (row * p->width * 4),
712                       GEGL_AUTO_ROWSTRIDE);
713     }
714 
715   g_free(buffer);
716   return 0;
717 }
718 
719 static gint
720 load_contiguous(GeglOperation *operation,
721                 GeglBuffer    *output)
722 {
723   GeglProperties *o = GEGL_PROPERTIES(operation);
724   Priv *p = (Priv*) o->user_data;
725   guint32 tile_width = (guint32) p->width;
726   guint32 tile_height = 1;
727   guchar *buffer;
728   gint x, y;
729 
730   g_return_val_if_fail(p->tiff != NULL, -1);
731 
732   if (!TIFFIsTiled(p->tiff))
733       buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff));
734   else
735     {
736       TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width);
737       TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height);
738 
739       buffer = g_try_new(guchar, TIFFTileSize(p->tiff));
740     }
741 
742   g_assert(buffer != NULL);
743 
744   for (y = 0; y < p->height; y += tile_height)
745     {
746       for (x = 0; x < p->width; x += tile_width)
747         {
748           GeglRectangle tile = { x, y, tile_width, tile_height };
749 
750           if (TIFFIsTiled(p->tiff))
751             TIFFReadTile(p->tiff, buffer, x, y, 0, 0);
752           else
753             TIFFReadScanline(p->tiff, buffer, y, 0);
754 
755           gegl_buffer_set(output, &tile, 0, p->format,
756                           (guchar *) buffer,
757                           GEGL_AUTO_ROWSTRIDE);
758         }
759     }
760 
761   g_free(buffer);
762   return 0;
763 }
764 
765 static gint
766 load_separated(GeglOperation *operation,
767                GeglBuffer    *output)
768 {
769   GeglProperties *o = GEGL_PROPERTIES(operation);
770   Priv *p = (Priv*) o->user_data;
771   guint32 tile_width = (guint32) p->width;
772   guint32 tile_height = 1;
773   gint output_bytes_per_pixel;
774   gint nb_components, offset = 0;
775   guchar *buffer;
776   gint i;
777 
778   g_return_val_if_fail(p->tiff != NULL, -1);
779 
780   if (!TIFFIsTiled(p->tiff))
781     buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff));
782   else
783     {
784       TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width);
785       TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height);
786 
787       buffer = g_try_new(guchar, TIFFTileSize(p->tiff));
788     }
789 
790   g_assert(buffer != NULL);
791 
792   nb_components = babl_format_get_n_components(p->format);
793   output_bytes_per_pixel = babl_format_get_bytes_per_pixel(p->format);
794 
795   for (i = 0; i < nb_components; i++)
796     {
797       const Babl *plane_format;
798       const Babl *component_type;
799       gint plane_bytes_per_pixel;
800       gint x, y;
801 
802       component_type = babl_format_get_type(p->format, i);
803 
804       plane_format = babl_format_n(component_type, 1);
805 
806       plane_bytes_per_pixel = babl_format_get_bytes_per_pixel(plane_format);
807 
808       for (y = 0; y < p->height; y += tile_height)
809         {
810           for (x = 0; x < p->width; x += tile_width)
811             {
812               GeglRectangle output_tile = { x, y, tile_width, tile_height };
813               GeglRectangle plane_tile = { 0, 0, tile_width, tile_height };
814               GeglBufferIterator *iterator;
815               GeglBuffer *linear;
816 
817               if (TIFFIsTiled(p->tiff))
818                 TIFFReadTile(p->tiff, buffer, x, y, 0, i);
819               else
820                 TIFFReadScanline(p->tiff, buffer, y, i);
821 
822               linear = gegl_buffer_linear_new_from_data(buffer, plane_format,
823                                                         &plane_tile,
824                                                         GEGL_AUTO_ROWSTRIDE,
825                                                         NULL, NULL);
826 
827               iterator = gegl_buffer_iterator_new(linear, &plane_tile,
828                                                   0, NULL,
829                                                   GEGL_ACCESS_READ,
830                                                   GEGL_ABYSS_NONE, 2);
831 
832               gegl_buffer_iterator_add(iterator, output, &output_tile,
833                                        0, p->format,
834                                        GEGL_ACCESS_READWRITE,
835                                        GEGL_ABYSS_NONE);
836 
837               while (gegl_buffer_iterator_next(iterator))
838                 {
839                   guchar *plane_buffer = iterator->items[0].data;
840                   guchar *output_buffer = iterator->items[1].data;
841                   gint nb_pixels = iterator->length;
842 
843                   output_buffer += offset;
844 
845                   while (nb_pixels--)
846                   {
847                     memcpy(output_buffer, plane_buffer, plane_bytes_per_pixel);
848 
849                     output_buffer += output_bytes_per_pixel;
850                     plane_buffer += plane_bytes_per_pixel;
851                   }
852                 }
853 
854               g_object_unref(linear);
855             }
856         }
857 
858       offset += plane_bytes_per_pixel;
859     }
860 
861   g_free(buffer);
862   return 0;
863 }
864 
865 static void
866 prepare(GeglOperation *operation)
867 {
868   GeglProperties *o = GEGL_PROPERTIES(operation);
869   Priv *p = (o->user_data) ? o->user_data : g_new0(Priv, 1);
870   GError *error = NULL;
871   GFile *file = NULL;
872   gint directories;
873 
874   g_assert(p != NULL);
875 
876   if (p->file != NULL && (o->uri || o->path))
877     {
878       if (o->uri && strlen(o->uri) > 0)
879         file = g_file_new_for_uri(o->uri);
880       else if (o->path && strlen(o->path) > 0)
881         file = g_file_new_for_path(o->path);
882       if (file != NULL)
883         {
884           if (!g_file_equal(p->file, file))
885             cleanup(operation);
886           g_object_unref(file);
887         }
888     }
889 
890   o->user_data = (void*) p;
891 
892   if (p->stream == NULL)
893     {
894       p->stream = gegl_gio_open_input_stream(o->uri, o->path, &p->file, &error);
895       if (p->stream != NULL && p->file != NULL)
896         p->can_seek = g_seekable_can_seek(G_SEEKABLE(p->stream));
897       if (p->stream == NULL)
898         {
899           if (error)
900           {
901             g_warning("%s", error->message);
902             g_error_free(error);
903           }
904           cleanup(operation);
905           return;
906         }
907 
908       TIFFSetErrorHandler(error_handler);
909       TIFFSetWarningHandler(warning_handler);
910 
911       p->tiff = TIFFClientOpen("GEGL-tiff-load", "r", (thandle_t) p,
912                                read_from_stream, write_to_stream,
913                                seek_in_stream, close_stream,
914                                get_file_size, NULL, NULL);
915       if (p->tiff == NULL)
916         {
917           if (o->uri != NULL && strlen(o->uri) > 0)
918             g_warning("failed to open TIFF from %s", o->uri);
919           else
920             g_warning("failed to open TIFF from %s", o->path);
921           cleanup(operation);
922           return;
923         }
924     }
925 
926   if (o->directory != p->directory)
927     {
928       directories = TIFFNumberOfDirectories(p->tiff);
929       if (o->directory > 1 && o->directory <= directories)
930         TIFFSetDirectory(p->tiff, o->directory - 1);
931 
932       if (query_tiff(operation))
933         {
934           g_warning("could not query TIFF file");
935           cleanup(operation);
936           return;
937         }
938 
939         p->directory = o->directory;
940     }
941 
942   gegl_operation_set_format(operation, "output", p->format);
943 }
944 
945 static GeglRectangle
946 get_bounding_box(GeglOperation *operation)
947 {
948   GeglProperties *o = GEGL_PROPERTIES(operation);
949   GeglRectangle result = { 0, 0, 0, 0 };
950   Priv *p = (Priv*) o->user_data;
951 
952   if (p->tiff != NULL)
953     {
954       result.width = p->width;
955       result.height = p->height;
956     }
957 
958   return result;
959 }
960 
961 static gboolean
962 process(GeglOperation *operation,
963         GeglBuffer *output,
964         const GeglRectangle *result,
965         gint level)
966 {
967   GeglProperties *o = GEGL_PROPERTIES(operation);
968   Priv *p = (Priv*) o->user_data;
969 
970   if (p->tiff != NULL)
971     {
972       switch (p->mode)
973       {
974       case TIFF_LOADING_RGBA:
975         if (!load_RGBA(operation, output))
976           return TRUE;
977         break;
978 
979       case TIFF_LOADING_CONTIGUOUS:
980         if (!load_contiguous(operation, output))
981           return TRUE;
982         break;
983 
984       case TIFF_LOADING_SEPARATED:
985         if (!load_separated(operation, output))
986           return TRUE;
987         break;
988 
989       default:
990         break;
991       }
992     }
993 
994   return FALSE;
995 }
996 
997 static GeglRectangle
998 get_cached_region(GeglOperation       *operation,
999                   const GeglRectangle *roi)
1000 {
1001   return get_bounding_box(operation);
1002 }
1003 
1004 static void
1005 finalize(GObject *object)
1006 {
1007   GeglProperties *o = GEGL_PROPERTIES(object);
1008 
1009   if (o->user_data != NULL)
1010     {
1011       cleanup(GEGL_OPERATION(object));
1012       g_clear_pointer(&o->user_data, g_free);
1013     }
1014 
1015   G_OBJECT_CLASS(gegl_op_parent_class)->finalize(object);
1016 }
1017 
1018 static void
1019 gegl_op_class_init(GeglOpClass *klass)
1020 {
1021   GeglOperationClass *operation_class;
1022   GeglOperationSourceClass *source_class;
1023 
1024   G_OBJECT_CLASS(klass)->finalize = finalize;
1025 
1026   operation_class = GEGL_OPERATION_CLASS(klass);
1027   source_class = GEGL_OPERATION_SOURCE_CLASS(klass);
1028 
1029   source_class->process = process;
1030   operation_class->prepare = prepare;
1031   operation_class->get_bounding_box = get_bounding_box;
1032   operation_class->get_cached_region = get_cached_region;
1033 
1034   gegl_operation_class_set_keys(operation_class,
1035     "name",          "gegl:tiff-load",
1036     "title",       _("TIFF File Loader"),
1037     "categories",    "hidden",
1038     "description", _("TIFF image loader using libtiff"),
1039     NULL);
1040 
1041   gegl_operation_handlers_register_loader(
1042     "image/tiff", "gegl:tiff-load");
1043   gegl_operation_handlers_register_loader(
1044     "image/x-tiff-multipage", "gegl:tiff-load");
1045   gegl_operation_handlers_register_loader(
1046     ".tiff", "gegl:tiff-load");
1047   gegl_operation_handlers_register_loader(
1048     ".tif", "gegl:tiff-load");
1049 }
1050 
1051 #endif
1052