[gegl] tiff: Add a gegl:tiff-load operation



commit ae739a08a9902a7d3b4c660c83ef5b2b1c170920
Author: Martin Blanchard <tchaik gmx com>
Date:   Fri Oct 2 21:38:44 2015 +0200

    tiff: Add a gegl:tiff-load operation
    
    New GIO based TIFF file loader using libtiff. Handles contiguous and
    separated planar configuration, integer and floating-point samples and
    various sample bit depth. Test file + resources are included. Based on
    GIMP's file-tiff-load plugin.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=739124

 configure.ac                                       |   21 +
 operations/external/Makefile.am                    |    7 +
 operations/external/tiff-load.c                    |  846 ++++++++++++++++++++
 tests/compositions/Makefile.am                     |    1 +
 .../data/gegl-16bit-float-4ch-assoc-contiguous.tif |  Bin 0 -> 558148 bytes
 .../data/gegl-8bit-4ch-assoc-contiguous.tif        |  Bin 0 -> 289108 bytes
 .../data/gegl-8bit-4ch-assoc-separated.tif         |  Bin 0 -> 289140 bytes
 .../data/gegl-8bit-4ch-unassoc-contiguous.tif      |  Bin 0 -> 269143 bytes
 tests/compositions/reference/tiff-load.png         |  Bin 0 -> 190180 bytes
 tests/compositions/tiff-load.xml                   |   65 ++
 10 files changed, 940 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 0a22bee..023b18c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -55,6 +55,7 @@ m4_define([pango_required_version], [0.0.0])
 m4_define([pangocairo_required_version], [0.0.0])
 m4_define([png_required_version], [0.0.0])
 m4_define([sdl_required_version], [0.0.0])
+m4_define([libtiff_required_version], [4.0.0])
 m4_define([webp_required_version], [0.0.0])
 m4_define([poly2tri-c_required_version], [0.0.0])
 
@@ -1142,6 +1143,25 @@ AM_CONDITIONAL(HAVE_UMFPACK, test "x$have_umfpack" = "xyes")
 AC_SUBST(UMFPACK_CFLAGS)
 AC_SUBST(UMFPACK_LIBS)
 
+###################
+# Check for libtiff
+###################
+
+AC_ARG_WITH(libtiff, [  --without-libtiff       build without libtiff support])
+
+have_libtiff="no"
+if test "x$with_libtiff" != "xno"; then
+  PKG_CHECK_MODULES(TIFF, libtiff-4 >= libtiff_required_version,
+    have_libtiff="yes",
+    have_libtiff="no  (libtiff library not found)")
+fi
+
+AM_CONDITIONAL(HAVE_TIFF, test "$have_libtiff" = "yes")
+
+AC_SUBST(TIFF_CFLAGS)
+AC_SUBST(TIFF_LIBS)
+
+
 ################
 # Check for webp
 ################
@@ -1346,6 +1366,7 @@ Optional dependencies:
   EXIV:            $have_exiv2
   gexiv2:          $have_gexiv2
   umfpack:         $have_umfpack
+  TIFF             $have_libtiff
   webp:            $have_webp
   poly2tri-c:      $have_p2tc
 ]);
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index 3e863b3..89633bb 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -128,6 +128,13 @@ lcms_from_profile_la_SOURCES = lcms-from-profile.c
 lcms_from_profile_la_LIBADD  = $(op_libs) $(LCMS_LIBS)
 lcms_from_profile_la_CFLAGS  = $(AM_CFLAGS)
 endif
+ 
+if HAVE_TIFF
+ops += tiff-load.la
+tiff_load_la_SOURCES = tiff-load.c
+tiff_load_la_LIBADD = $(op_libs) $(TIFF_LIBS)
+tiff_load_la_CFLAGS = $(AM_CFLAGS) $(TIFF_CFLAGS)
+endif
 
 if HAVE_WEBP
 ops += webp-load.la
diff --git a/operations/external/tiff-load.c b/operations/external/tiff-load.c
new file mode 100644
index 0000000..0b3c40b
--- /dev/null
+++ b/operations/external/tiff-load.c
@@ -0,0 +1,846 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2015 Martin Blanchard <tchaik gmx com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+
+#ifdef GEGL_PROPERTIES
+
+property_file_path (path, _("File"), "")
+  description (_("Path of file to load"))
+property_uri (uri, _("URI"), "")
+  description (_("URI for file to load"))
+
+property_int(directory, _("Directory"), 1)
+  description (_("Image file directory (subfile)"))
+  value_range (1, G_MAXINT)
+  ui_range (1, 16)
+
+#else
+
+#define GEGL_OP_SOURCE
+#define GEGL_OP_C_SOURCE tiff-load.c
+
+#include <gegl-op.h>
+#include <gegl-gio-private.h>
+#include <tiffio.h>
+
+typedef enum {
+  TIFF_LOADING_RGBA,
+  TIFF_LOADING_CONTIGUOUS,
+  TIFF_LOADING_SEPARATED
+} LoadingMode;
+
+typedef struct
+{
+  GFile *file;
+  GInputStream *stream;
+  gboolean can_seek;
+
+  gchar *buffer;
+  gsize allocated;
+  gsize position;
+  gsize loaded;
+
+  TIFF *tiff;
+
+  gint directory;
+
+  const Babl *format;
+  LoadingMode mode;
+
+  gint width;
+  gint height;
+} Priv;
+
+static void
+cleanup(GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+
+  if (p != NULL)
+    {
+      if (p->tiff != NULL)
+        TIFFClose(p->tiff);
+      else if (p->stream != NULL)
+        g_input_stream_close(G_INPUT_STREAM(p->stream), NULL, NULL);
+      if (p->stream != NULL)
+        g_clear_object(&p->stream);
+      p->tiff = NULL;
+
+      if (p->file != NULL)
+        g_clear_object(&p->file);
+
+      p->width = p->height = 0;
+      p->directory = 0;
+    }
+}
+
+static GSeekType
+lseek_to_seek_type(int whence)
+{
+  switch (whence)
+    {
+    default:
+    case SEEK_SET:
+      return G_SEEK_SET;
+
+    case SEEK_CUR:
+      return G_SEEK_CUR;
+
+    case G_SEEK_END:
+      return SEEK_END;
+    }
+}
+
+static tsize_t
+read_from_stream(thandle_t handle,
+                 tdata_t buffer,
+                 tsize_t size)
+{
+  Priv *p = (Priv*) handle;
+  GError *error = NULL;
+  gchar *new_buffer;
+  gsize new_size = 1;
+  gsize missing, needed;
+  gssize read = -1;
+
+  g_assert(p->stream);
+
+  if (p->can_seek)
+    {
+      read = g_input_stream_read(G_INPUT_STREAM(p->stream),
+                                 (void *) buffer, (gsize) size,
+                                 NULL, &error);
+      if (read < 0)
+        {
+          g_warning(error->message);
+          g_error_free(error);
+        }
+    }
+  else
+    {
+      if (p->position + size > p->loaded)
+        {
+          missing = p->position + size - p->loaded;
+          if (p->loaded + missing > p->allocated)
+            {
+              needed = p->loaded + missing - p->allocated;
+              while (new_size < p->allocated + needed)
+                new_size *= 2;
+
+              new_buffer = g_try_realloc(p->buffer, new_size);
+              if (!new_buffer)
+                return -1;
+
+              p->allocated = new_size;
+              p->buffer = new_buffer;
+            }
+
+          while (missing > 0)
+            {
+              read = g_input_stream_read(G_INPUT_STREAM(p->stream),
+                                         (void *) (p->buffer + p->loaded),
+                                         missing,
+                                         NULL, &error);
+              if (read < 0)
+                {
+                  g_warning(error->message);
+                  g_error_free(error);
+                  break;
+                }
+
+              p->loaded += read;
+              missing -= read;
+            }
+        }
+
+      g_assert(p->position + size <= p->loaded);
+
+      memcpy(buffer, p->buffer + p->position, size);
+      p->position += size;
+      read = size;
+    }
+
+  return (tsize_t) read;
+}
+
+static tsize_t
+write_to_stream(thandle_t handle,
+                tdata_t buffer,
+                tsize_t size)
+{
+  Priv *p = (Priv*) handle;
+
+  g_assert(p->stream && FALSE);
+
+  return -1;
+}
+
+static toff_t
+seek_in_stream(thandle_t handle,
+               toff_t offset,
+               int whence)
+{
+  Priv *p = (Priv*) handle;
+  GError *error = NULL;
+  gboolean sought = FALSE;
+  goffset position = -1;
+
+  g_assert(p->stream);
+
+  if (p->can_seek)
+    {
+      sought = g_seekable_seek(G_SEEKABLE(p->stream),
+                               (goffset) offset, lseek_to_seek_type(whence),
+                               NULL, &error);
+      if (sought)
+        position = g_seekable_tell(G_SEEKABLE(p->stream));
+      else
+        {
+          g_warning(error->message);
+          g_error_free(error);
+        }
+    }
+  else
+    {
+      switch (whence)
+        {
+        default:
+        case SEEK_SET:
+          if (offset <= p->loaded)
+            position = p->position = offset;
+          break;
+
+        case SEEK_CUR:
+          if (p->position + offset <= p->loaded)
+            position = p->position += offset;
+          break;
+
+        case G_SEEK_END:
+          if (p->loaded + offset <= p->loaded)
+            position = p->position = p->loaded + offset;
+          break;
+        }
+    }
+
+  return (toff_t) position;
+}
+
+static int
+close_stream(thandle_t handle)
+{
+  Priv *p = (Priv*) handle;
+  GError *error = NULL;
+  gboolean closed = FALSE;
+
+  g_assert(p->stream);
+
+  closed = g_input_stream_close(G_INPUT_STREAM(p->stream),
+                                NULL, &error);
+  if (!closed)
+    {
+      g_warning(error->message);
+      g_error_free(error);
+    }
+
+  g_clear_object(&p->stream);
+
+  p->loaded = 0;
+  p->position = 0;
+
+  if (p->buffer != NULL)
+    g_free(p->buffer);
+  p->buffer = NULL;
+
+  p->allocated = 0;
+
+  return (closed) ? 0 : -1;
+}
+
+static toff_t
+get_file_size(thandle_t handle)
+{
+  Priv *p = (Priv*) handle;
+  GError *error = NULL;
+  GFileInfo *info;
+  goffset size;
+
+  g_assert(p->stream);
+
+  size = p->loaded;
+
+  if (p->file != NULL)
+    {
+      info = g_file_query_info(p->file,
+                               G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                               G_FILE_QUERY_INFO_NONE,
+                               NULL, &error);
+      if (info == NULL)
+        {
+          g_warning(error->message);
+          g_error_free(error);
+        }
+      else
+        {
+          if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_STANDARD_SIZE))
+            size = g_file_info_get_size(info);
+          g_object_unref(info);
+        }
+    }
+
+  return (toff_t) size;
+}
+
+static gint
+query_tiff(GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+  gshort color_space, compression;
+  gushort bits_per_sample, samples_per_pixel;
+  gushort sample_format;
+  gboolean has_alpha = FALSE;
+  gboolean alpha_is_premultiplied = FALSE;
+  gushort *extra_types = NULL;
+  gushort nb_extras, planar_config;
+  gboolean fallback_mode = FALSE;
+  gchar format_string[32];
+  guint width, height;
+
+  g_return_val_if_fail(p->tiff != NULL, -1);
+
+  if (!TIFFGetField(p->tiff, TIFFTAG_IMAGEWIDTH, &width))
+    {
+      g_warning("could not get TIFF image width");
+      return -1;
+    }
+  else if (!TIFFGetField(p->tiff, TIFFTAG_IMAGELENGTH, &height))
+    {
+      g_warning("could not get TIFF image height");
+      return -1;
+    }
+
+  TIFFGetFieldDefaulted(p->tiff, TIFFTAG_COMPRESSION, &compression);
+  if (!TIFFGetField(p->tiff, TIFFTAG_PHOTOMETRIC, &color_space))
+    {
+      g_warning("could not get photometric from TIFF image");
+      if (compression == COMPRESSION_CCITTFAX3 ||
+          compression == COMPRESSION_CCITTFAX4 ||
+          compression == COMPRESSION_CCITTRLE  ||
+          compression == COMPRESSION_CCITTRLEW)
+        {
+          g_message("assuming min-is-white (CCITT compressed)");
+          color_space = PHOTOMETRIC_MINISWHITE;
+        }
+      else
+        {
+          g_message("assuming min-is-black");
+          color_space = PHOTOMETRIC_MINISBLACK;
+        }
+    }
+
+  TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+  if (!TIFFGetField(p->tiff, TIFFTAG_EXTRASAMPLES, &nb_extras, &extra_types))
+    nb_extras = 0;
+
+  if (nb_extras > 0)
+    {
+      if (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)
+        {
+          has_alpha = TRUE;
+          alpha_is_premultiplied = TRUE;
+          nb_extras--;
+        }
+      else if (extra_types[0] == EXTRASAMPLE_UNASSALPHA)
+        {
+          has_alpha = TRUE;
+          alpha_is_premultiplied = FALSE;
+          nb_extras--;
+        }
+      else if (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)
+        {
+          has_alpha = TRUE;
+          alpha_is_premultiplied = FALSE;
+          nb_extras--;
+        }
+    }
+
+  switch(color_space)
+    {
+    case PHOTOMETRIC_MINISBLACK:
+    case PHOTOMETRIC_MINISWHITE:
+      if (samples_per_pixel > 1 + nb_extras)
+        {
+          nb_extras = samples_per_pixel - 2;
+          has_alpha = TRUE;
+        }
+
+      if (has_alpha)
+        {
+          if(alpha_is_premultiplied)
+            g_strlcpy(format_string, "Y'aA ", 32);
+          else
+            g_strlcpy(format_string, "Y'A ", 32);
+        }
+      else
+        g_strlcpy(format_string, "Y' ", 32);
+      break;
+
+    case PHOTOMETRIC_RGB:
+      if (samples_per_pixel > 3 + nb_extras)
+        {
+          nb_extras = samples_per_pixel - 4;
+          has_alpha = TRUE;
+        }
+
+      if (has_alpha)
+        {
+          if (alpha_is_premultiplied)
+            g_strlcpy(format_string, "R'aG'aB'aA ", 32);
+          else
+            g_strlcpy(format_string, "R'G'B'A ", 32);
+        }
+      else
+        g_strlcpy(format_string, "R'G'B' ", 32);
+      break;
+
+    default:
+      fallback_mode = TRUE;
+      break;
+    }
+
+  TIFFGetFieldDefaulted(p->tiff, TIFFTAG_SAMPLEFORMAT, &sample_format);
+  TIFFGetFieldDefaulted(p->tiff, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+
+  switch(bits_per_sample)
+    {
+    case 8:
+      g_strlcat(format_string, "u8", 32);
+      break;
+
+    case 16:
+      if (sample_format == SAMPLEFORMAT_IEEEFP)
+        g_strlcat(format_string, "half", 32);
+      else
+        g_strlcat(format_string, "u16", 32);
+      break;
+
+    case 32:
+      if (sample_format == SAMPLEFORMAT_IEEEFP)
+        g_strlcat(format_string, "float", 32);
+      else
+        g_strlcat(format_string, "u32", 32);
+      break;
+
+    case 64:
+      g_strlcat(format_string, "double", 32);
+      break;
+
+    default:
+      fallback_mode = TRUE;
+      break;
+    }
+
+  if (fallback_mode == TRUE)
+    g_strlcpy(format_string, "R'aG'aB'aA u8", 32);
+
+  TIFFGetFieldDefaulted(p->tiff, TIFFTAG_PLANARCONFIG, &planar_config);
+
+  p->format = babl_format(format_string);
+  if (fallback_mode)
+    p->mode = TIFF_LOADING_RGBA;
+  else if (planar_config == PLANARCONFIG_CONTIG)
+    p->mode = TIFF_LOADING_CONTIGUOUS;
+  else
+    p->mode = TIFF_LOADING_SEPARATED;
+
+  p->height = (gint) height;
+  p->width = (gint) width;
+
+  return 0;
+}
+
+static gint
+load_RGBA(GeglOperation *operation,
+          GeglBuffer    *output)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+  guint32 *buffer;
+  gint row;
+
+  g_return_val_if_fail(p->tiff != NULL, -1);
+
+  buffer = g_try_new(guint32, p->width * p->height * sizeof(guint32));
+
+  g_assert(buffer != NULL);
+
+  if (!TIFFReadRGBAImage(p->tiff, p->width, p->height, buffer, 0))
+    {
+      g_message("unsupported layout, RGBA loader failed");
+      g_free(buffer);
+      return -1;
+    }
+
+  for (row = 0; row < p->height; row++)
+    {
+      GeglRectangle line = { 0, p->height - row - 1, p->width, 1 };
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+      guint row_start = row * p->width;
+      guint row end = row * p->width + p->width;
+      guint i;
+
+      for (i = row_start; i < row_end; i++)
+        buffer[i] = GUINT32_TO_LE(buffer[i]);
+#endif
+
+      gegl_buffer_set(output, &line, 0, p->format,
+                      ((guchar *) buffer) + (row * p->width * 4),
+                      GEGL_AUTO_ROWSTRIDE);
+    }
+
+  g_free(buffer);
+  return 0;
+}
+
+static gint
+load_contiguous(GeglOperation *operation,
+                GeglBuffer    *output)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+  guint32 tile_width = (guint32) p->width;
+  guint32 tile_height = 1;
+  guchar *buffer;
+  gint x, y;
+
+  g_return_val_if_fail(p->tiff != NULL, -1);
+
+  if (!TIFFIsTiled(p->tiff))
+      buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff));
+  else
+    {
+      TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width);
+      TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height);
+
+      buffer = g_try_new(guchar, TIFFTileSize(p->tiff));
+    }
+
+  g_assert(buffer != NULL);
+
+  for (y = 0; y < p->height; y += tile_height)
+    {
+      for (x = 0; x < p->width; x += tile_width)
+        {
+          GeglRectangle tile = { x, y, tile_width, tile_height };
+
+          if (TIFFIsTiled(p->tiff))
+            TIFFReadTile(p->tiff, buffer, x, y, 0, 0);
+          else
+            TIFFReadScanline(p->tiff, buffer, y, 0);
+
+          gegl_buffer_set(output, &tile, 0, p->format,
+                          (guchar *) buffer,
+                          GEGL_AUTO_ROWSTRIDE);
+        }
+    }
+
+  g_free(buffer);
+  return 0;
+}
+
+static gint
+load_separated(GeglOperation *operation,
+               GeglBuffer    *output)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+  guint32 tile_width = (guint32) p->width;
+  guint32 tile_height = 1;
+  gint output_bytes_per_pixel;
+  gint nb_components, offset = 0;
+  guchar *buffer;
+  gint i;
+
+  g_return_val_if_fail(p->tiff != NULL, -1);
+
+  if (!TIFFIsTiled(p->tiff))
+    buffer = g_try_new(guchar, TIFFScanlineSize(p->tiff));
+  else
+    {
+      TIFFGetField(p->tiff, TIFFTAG_TILEWIDTH, &tile_width);
+      TIFFGetField(p->tiff, TIFFTAG_TILELENGTH, &tile_height);
+
+      buffer = g_try_new(guchar, TIFFTileSize(p->tiff));
+    }
+
+  g_assert(buffer != NULL);
+
+  nb_components = babl_format_get_n_components(p->format);
+  output_bytes_per_pixel = babl_format_get_bytes_per_pixel(p->format);
+
+  for (i = 0; i < nb_components; i++)
+    {
+      const Babl *plane_format;
+      const Babl *component_type;
+      gint plane_bytes_per_pixel;
+      gint x, y;
+
+      component_type = babl_format_get_type(p->format, i);
+
+      plane_format = babl_format_n(component_type, 1);
+
+      plane_bytes_per_pixel = babl_format_get_bytes_per_pixel(plane_format);
+
+      for (y = 0; y < p->height; y += tile_height)
+        {
+          for (x = 0; x < p->width; x += tile_width)
+            {
+              GeglRectangle output_tile = { x, y, tile_width, tile_height };
+              GeglRectangle plane_tile = { 0, 0, tile_width, tile_height };
+              GeglBufferIterator *iterator;
+              GeglBuffer *linear;
+
+              if (TIFFIsTiled(p->tiff))
+                TIFFReadTile(p->tiff, buffer, x, y, 0, i);
+              else
+                TIFFReadScanline(p->tiff, buffer, y, i);
+
+              linear = gegl_buffer_linear_new_from_data(buffer, plane_format,
+                                                        &plane_tile,
+                                                        GEGL_AUTO_ROWSTRIDE,
+                                                        NULL, NULL);
+
+              iterator = gegl_buffer_iterator_new(linear, &plane_tile,
+                                                  0, NULL,
+                                                  GEGL_ACCESS_READ,
+                                                  GEGL_ABYSS_NONE);
+
+              gegl_buffer_iterator_add(iterator, output, &output_tile,
+                                       0, p->format,
+                                       GEGL_ACCESS_READWRITE,
+                                       GEGL_ABYSS_NONE);
+
+              while (gegl_buffer_iterator_next(iterator))
+                {
+                  guchar *plane_buffer = iterator->data[0];
+                  guchar *output_buffer = iterator->data[1];
+                  gint nb_pixels = iterator->length;
+
+                  output_buffer += offset;
+
+                  while (nb_pixels--)
+                  {
+                    memcpy(output_buffer, plane_buffer, plane_bytes_per_pixel);
+
+                    output_buffer += output_bytes_per_pixel;
+                    plane_buffer += plane_bytes_per_pixel;
+                  }
+                }
+
+              g_object_unref(linear);
+            }
+        }
+
+      offset += plane_bytes_per_pixel;
+    }
+
+  g_free(buffer);
+  return 0;
+}
+
+static void
+prepare(GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (o->user_data) ? o->user_data : g_new0(Priv, 1);
+  GError *error = NULL;
+  GFile *file = NULL;
+  gint directories;
+
+  g_assert(p != NULL);
+
+  if (p->file != NULL && (o->uri || o->path))
+    {
+      if (o->uri && strlen(o->uri) > 0)
+        file = g_file_new_for_uri(o->uri);
+      else if (o->path && strlen(o->path) > 0)
+        file = g_file_new_for_path(o->path);
+      if (file != NULL)
+        {
+          if (!g_file_equal(p->file, file))
+            cleanup(operation);
+          g_object_unref(file);
+        }
+    }
+
+  o->user_data = (void*) p;
+
+  if (p->stream == NULL)
+    {
+      p->stream = gegl_gio_open_input_stream(o->uri, o->path, &p->file, &error);
+      if (p->stream != NULL && p->file != NULL)
+        p->can_seek = g_seekable_can_seek(G_SEEKABLE(p->stream));
+      if (p->stream == NULL)
+        {
+          g_warning(error->message);
+          g_error_free(error);
+          cleanup(operation);
+          return;
+        }
+
+      p->tiff = TIFFClientOpen("GEGL-tiff-load", "r", (thandle_t) p,
+                               read_from_stream, write_to_stream,
+                               seek_in_stream, close_stream,
+                               get_file_size, NULL, NULL);
+      if (p->tiff == NULL)
+        {
+          g_warning("failed to open TIFF from %s", o->path);
+          g_input_stream_close(p->stream, NULL, NULL);
+          if (p->file != NULL)
+            g_object_unref(p->file);
+          cleanup(operation);
+          return;
+        }
+    }
+
+  if (o->directory != p->directory)
+    {
+      directories = TIFFNumberOfDirectories(p->tiff);
+      if (o->directory > 1 && o->directory <= directories)
+        TIFFSetDirectory(p->tiff, o->directory - 1);
+
+      if (query_tiff(operation))
+        {
+          g_warning("could not query TIFF file");
+          cleanup(operation);
+          return;
+        }
+
+        p->directory = o->directory;
+    }
+
+  gegl_operation_set_format(operation, "output", p->format);
+}
+
+static GeglRectangle
+get_bounding_box(GeglOperation *operation)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  GeglRectangle result = { 0, 0, 0, 0 };
+  Priv *p = (Priv*) o->user_data;
+
+  if (p->tiff != NULL)
+    {
+      result.width = p->width;
+      result.height = p->height;
+    }
+
+  return result;
+}
+
+static gboolean
+process(GeglOperation *operation,
+        GeglBuffer *output,
+        const GeglRectangle *result,
+        gint level)
+{
+  GeglProperties *o = GEGL_PROPERTIES(operation);
+  Priv *p = (Priv*) o->user_data;
+
+  if (p->tiff != NULL)
+    {
+      switch (p->mode)
+      {
+      case TIFF_LOADING_RGBA:
+        if (!load_RGBA(operation, output))
+          return TRUE;
+        break;
+
+      case TIFF_LOADING_CONTIGUOUS:
+        if (!load_contiguous(operation, output))
+          return TRUE;
+        break;
+
+      case TIFF_LOADING_SEPARATED:
+        if (!load_separated(operation, output))
+          return TRUE;
+        break;
+
+      default:
+        break;
+      }
+    }
+
+  return FALSE;
+}
+
+static GeglRectangle
+get_cached_region(GeglOperation       *operation,
+                  const GeglRectangle *roi)
+{
+  return get_bounding_box(operation);
+}
+
+static void
+finalize(GObject *object)
+{
+  GeglProperties *o = GEGL_PROPERTIES(object);
+
+  if (o->user_data != NULL)
+    {
+      cleanup(GEGL_OPERATION(object));
+      if (o->user_data != NULL)
+        g_free(o->user_data);
+      o->user_data = NULL;
+    }
+
+  G_OBJECT_CLASS(gegl_op_parent_class)->finalize(object);
+}
+
+static void
+gegl_op_class_init(GeglOpClass *klass)
+{
+  GeglOperationClass *operation_class;
+  GeglOperationSourceClass *source_class;
+
+  G_OBJECT_CLASS(klass)->finalize = finalize;
+
+  operation_class = GEGL_OPERATION_CLASS(klass);
+  source_class = GEGL_OPERATION_SOURCE_CLASS(klass);
+
+  source_class->process = process;
+  operation_class->prepare = prepare;
+  operation_class->get_bounding_box = get_bounding_box;
+  operation_class->get_cached_region = get_cached_region;
+
+  gegl_operation_class_set_keys(operation_class,
+    "name",          "gegl:tiff-load",
+    "title",       _("TIFF File Loader"),
+    "categories",    "hidden",
+    "description", _("TIFF image loader using libtiff"),
+    NULL);
+
+  gegl_extension_handler_register(".tiff", "gegl:tiff-load");
+  gegl_extension_handler_register(".tif", "gegl:tiff-load");
+}
+
+#endif
diff --git a/tests/compositions/Makefile.am b/tests/compositions/Makefile.am
index c2d9de4..48ada99 100644
--- a/tests/compositions/Makefile.am
+++ b/tests/compositions/Makefile.am
@@ -78,6 +78,7 @@ NO_OPENCL_TESTS =               \
   pnm-raw-load.xml              \
   pnm-ascii-load.xml            \
   jpg-load-datauri.xml          \
+  tiff-load.xml                 \
   tile.xml
 
 # Conditional tests
diff --git a/tests/compositions/data/gegl-16bit-float-4ch-assoc-contiguous.tif 
b/tests/compositions/data/gegl-16bit-float-4ch-assoc-contiguous.tif
new file mode 100644
index 0000000..79acee9
Binary files /dev/null and b/tests/compositions/data/gegl-16bit-float-4ch-assoc-contiguous.tif differ
diff --git a/tests/compositions/data/gegl-8bit-4ch-assoc-contiguous.tif 
b/tests/compositions/data/gegl-8bit-4ch-assoc-contiguous.tif
new file mode 100644
index 0000000..7cc7629
Binary files /dev/null and b/tests/compositions/data/gegl-8bit-4ch-assoc-contiguous.tif differ
diff --git a/tests/compositions/data/gegl-8bit-4ch-assoc-separated.tif 
b/tests/compositions/data/gegl-8bit-4ch-assoc-separated.tif
new file mode 100644
index 0000000..3c09847
Binary files /dev/null and b/tests/compositions/data/gegl-8bit-4ch-assoc-separated.tif differ
diff --git a/tests/compositions/data/gegl-8bit-4ch-unassoc-contiguous.tif 
b/tests/compositions/data/gegl-8bit-4ch-unassoc-contiguous.tif
new file mode 100644
index 0000000..a04a21e
Binary files /dev/null and b/tests/compositions/data/gegl-8bit-4ch-unassoc-contiguous.tif differ
diff --git a/tests/compositions/reference/tiff-load.png b/tests/compositions/reference/tiff-load.png
new file mode 100644
index 0000000..14c91f4
Binary files /dev/null and b/tests/compositions/reference/tiff-load.png differ
diff --git a/tests/compositions/tiff-load.xml b/tests/compositions/tiff-load.xml
new file mode 100644
index 0000000..17c567e
--- /dev/null
+++ b/tests/compositions/tiff-load.xml
@@ -0,0 +1,65 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<gegl>
+  <node operation='gegl:over'>
+    <node operation='gegl:translate'>
+      <params>
+        <param name='x'>20.000000</param>
+        <param name='y'>20.000000</param>
+      </params>
+    </node>
+    <node operation='gegl:tiff-load'>
+      <params>
+        <param name='path'>data/gegl-8bit-4ch-assoc-contiguous.tif</param>
+
+      </params>
+    </node>
+  </node>
+  <node operation='gegl:over'>
+    <node operation='gegl:translate'>
+      <params>
+        <param name='x'>488.000000</param>
+        <param name='y'>20.000000</param>
+      </params>
+    </node>
+    <node operation='gegl:tiff-load'>
+      <params>
+        <param name='path'>data/gegl-8bit-4ch-assoc-separated.tif</param>
+      </params>
+    </node>
+  </node>
+  <node operation='gegl:over'>
+    <node operation='gegl:translate'>
+      <params>
+        <param name='x'>20.000000</param>
+        <param name='y'>190.000000</param>
+      </params>
+    </node>
+    <node operation='gegl:tiff-load'>
+      <params>
+        <param name='path'>data/gegl-8bit-4ch-unassoc-contiguous.tif</param>
+      </params>
+    </node>
+  </node>
+  <node operation='gegl:over'>
+    <node operation='gegl:translate'>
+      <params>
+        <param name='x'>488.000000</param>
+        <param name='y'>190.000000</param>
+      </params>
+    </node>
+    <node operation='gegl:tiff-load'>
+      <params>
+        <param name='path'>data/gegl-16bit-float-4ch-assoc-contiguous.tif</param>
+      </params>
+    </node>
+  </node>
+  <node operation='gegl:crop'>
+     <params>
+         <param name='width'>956</param>
+         <param name='height'>360</param>
+    </params>
+  </node>
+  <node operation='gegl:checkerboard'>
+  </node>
+</gegl>
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]