summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-tiff/file-tiff-io.c
diff options
context:
space:
mode:
Diffstat (limited to 'plug-ins/file-tiff/file-tiff-io.c')
-rw-r--r--plug-ins/file-tiff/file-tiff-io.c585
1 files changed, 585 insertions, 0 deletions
diff --git a/plug-ins/file-tiff/file-tiff-io.c b/plug-ins/file-tiff/file-tiff-io.c
new file mode 100644
index 0000000..09ef81f
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-io.c
@@ -0,0 +1,585 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+
+static gboolean tiff_file_size_error = FALSE;
+
+typedef struct
+{
+ GFile *file;
+ GObject *stream;
+ GInputStream *input;
+ GOutputStream *output;
+ gboolean can_seek;
+
+ gchar *buffer;
+ gsize allocated;
+ gsize used;
+ gsize position;
+} TiffIO;
+
+
+static TIFFExtendProc parent_extender;
+
+static void tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static void tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static tsize_t tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static tsize_t tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static toff_t tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence);
+static gint tiff_io_close (thandle_t handle);
+static toff_t tiff_io_get_file_size (thandle_t handle);
+static void register_geotags (TIFF *tif);
+
+static void
+register_geotags (TIFF *tif)
+{
+ static gboolean geotifftags_registered = FALSE;
+
+ if (geotifftags_registered)
+ return;
+
+ geotifftags_registered = TRUE;
+
+ TIFFMergeFieldInfo (tif, geotifftags_fieldinfo, (sizeof (geotifftags_fieldinfo) / sizeof (geotifftags_fieldinfo[0])));
+
+ if (parent_extender)
+ (*parent_extender) (tif);
+}
+
+static TiffIO tiff_io = { 0, };
+
+
+TIFF *
+tiff_open (GFile *file,
+ const gchar *mode,
+ GError **error)
+{
+ TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning);
+ TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error);
+
+ parent_extender = TIFFSetTagExtender (register_geotags);
+
+ tiff_io.file = file;
+
+ if (! strcmp (mode, "r"))
+ {
+ tiff_io.input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! tiff_io.input)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.input);
+ }
+#ifdef TIFF_VERSION_BIG
+ else if(! strcmp (mode, "w") || ! strcmp (mode, "w8"))
+#else
+ else if(! strcmp (mode, "w"))
+#endif
+ {
+ tiff_io.output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE,
+ G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! tiff_io.output)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.output);
+ }
+ else if(! strcmp (mode, "a"))
+ {
+ GIOStream *iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL,
+ error));
+ if (! iostream)
+ return NULL;
+
+ tiff_io.input = g_io_stream_get_input_stream (iostream);
+ tiff_io.output = g_io_stream_get_output_stream (iostream);
+ tiff_io.stream = G_OBJECT (iostream);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+#if 0
+#warning FIXME !can_seek code is broken
+ tiff_io.can_seek = g_seekable_can_seek (G_SEEKABLE (tiff_io.stream));
+#endif
+ tiff_io.can_seek = TRUE;
+
+ return TIFFClientOpen ("file-tiff", mode,
+ (thandle_t) &tiff_io,
+ tiff_io_read,
+ tiff_io_write,
+ tiff_io_seek,
+ tiff_io_close,
+ tiff_io_get_file_size,
+ NULL, NULL);
+}
+
+gboolean
+tiff_got_file_size_error (void)
+{
+ return tiff_file_size_error;
+}
+
+void
+tiff_reset_file_size_error (void)
+{
+ tiff_file_size_error = FALSE;
+}
+
+static void
+tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gint tag = 0;
+
+ /* Between libtiff 3.7.0beta2 and 4.0.0alpha. */
+ if (! strcmp (fmt, "%s: unknown field with tag %d (0x%x) encountered") ||
+ /* Before libtiff 3.7.0beta2. */
+ ! strcmp (fmt, "%.1000s: unknown field with tag %d (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ va_arg (ap_test, const char *); /* ignore first arg */
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ /* for older versions of libtiff? */
+ else if (! strcmp (fmt, "unknown field with tag %d (0x%x) ignored") ||
+ /* Since libtiff 4.0.0alpha. */
+ ! strcmp (fmt, "Unknown field with tag %d (0x%x) encountered") ||
+ /* Since libtiff 4.3.0rc1. */
+ ! strcmp (fmt, "Unknown field with tag %u (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ else if (! strcmp (fmt, "Incorrect value for \"%s\"; tag ignored"))
+ {
+ va_list ap_test;
+ const char *stag;
+
+ G_VA_COPY (ap_test, ap);
+
+ stag = va_arg (ap_test, const char *);
+
+ if (! strcmp (stag, "RichTIFFIPTC"))
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ /* This is an error in Adobe products. Just report to stderr. */
+ g_printerr ("[%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ va_end (ap_test);
+ }
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=131975
+ * Ignore the warnings about unregistered private tags (>= 32768).
+ */
+ if (tag >= 32768)
+ return;
+
+ /* Other unknown fields are only reported to stderr. */
+ if (tag > 0)
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("%s\n", msg);
+ g_free (msg);
+
+ return;
+ }
+ else if (! strcmp (module, "TIFFReadDirectory") &&
+ ! strcmp (fmt,
+ "Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel."
+ " Defining non-color channels as ExtraSamples."))
+ {
+ /* We will process this issue in our code. Just report to stderr. */
+ g_printerr ("%s: [%s] %s\n", G_STRFUNC, module, fmt);
+
+ return;
+ }
+ else if (! strcmp (module, "Fax4Decode") ||
+ g_str_has_prefix (module, "Fax3Decode"))
+ {
+ /* Certain corrupt TIFF Fax images can produce a large amount of
+ * warnings which can cause GIMP to run out of GDI resources on
+ * Windows and eventually crash.
+ * The real problem seems to be that the amount of error console
+ * messages does not have a limit.
+ * See e.g. the first page of m1-8110934bb3b18d0e87ccc1ddfc5f0107.tif
+ * from imagetestsuite. LibTiff does not return -1 from
+ * ReadScanline, presumably because for fax images it's not
+ * unreasonable to expect certain lines to fail.
+ * Let's just only report to stderr in this case. */
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("LibTiff warning: [%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
+}
+
+static void
+tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gchar *msg;
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=132297
+ * Ignore the errors related to random access and JPEG compression
+ */
+ if (! strcmp (fmt, "Compression algorithm does not support random access"))
+ return;
+
+ msg = g_strdup_vprintf (fmt, ap);
+
+#ifdef TIFF_VERSION_BIG
+ if (g_strcmp0 (fmt, "Maximum TIFF file size exceeded") == 0)
+ /* @module in my tests were "TIFFAppendToStrip" but I wonder if
+ * this same error could not happen with other "modules".
+ */
+ tiff_file_size_error = TRUE;
+ else
+ /* Easier for debugging to at least print messages on stderr. */
+ g_printerr ("LibTiff error: [%s] %s\n", module, msg);
+#endif
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "%s", msg);
+ g_free (msg);
+}
+
+static tsize_t
+tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize read = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_read = 0;
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) buffer, (gsize) size,
+ &bytes_read,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ read = bytes_read;
+ }
+ else
+ {
+ if (io->position + size > io->used)
+ {
+ gsize missing;
+ gsize bytes_read;
+
+ missing = io->position + size - io->used;
+
+ if (io->used + missing > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size = 1;
+ gsize needed;
+
+ needed = io->used + missing - io->allocated;
+ while (new_size < io->allocated + needed)
+ new_size *= 2;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) (io->buffer + io->used),
+ missing,
+ &bytes_read, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ io->used += bytes_read;
+ }
+
+ g_assert (io->position + size <= io->used);
+
+ memcpy (buffer, io->buffer + io->position, size);
+ io->position += size;
+
+ read = size;
+ }
+
+ return (tsize_t) read;
+}
+
+static tsize_t
+tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize written = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_written = 0;
+
+ if (! g_output_stream_write_all (io->output,
+ (void *) buffer, (gsize) size,
+ &bytes_written,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ written = bytes_written;
+ }
+ else
+ {
+ if (io->position + size > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size;
+
+ new_size = io->position + size;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ g_assert (io->position + size <= io->allocated);
+
+ memcpy (io->buffer + io->position, buffer, size);
+ io->position += size;
+
+ io->used = MAX (io->used, io->position);
+
+ written = size;
+ }
+
+ return (tsize_t) written;
+}
+
+static GSeekType
+lseek_to_seek_type (gint whence)
+{
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ return G_SEEK_SET;
+
+ case SEEK_CUR:
+ return G_SEEK_CUR;
+
+ case SEEK_END:
+ return G_SEEK_END;
+ }
+}
+
+static toff_t
+tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean sought = FALSE;
+ goffset position = -1;
+
+ if (io->can_seek)
+ {
+ sought = g_seekable_seek (G_SEEKABLE (io->stream),
+ (goffset) offset, lseek_to_seek_type (whence),
+ NULL, &error);
+ if (sought)
+ {
+ position = g_seekable_tell (G_SEEKABLE (io->stream));
+ }
+ else
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ if (offset <= io->used)
+ position = io->position = offset;
+ break;
+
+ case SEEK_CUR:
+ if (io->position + offset <= io->used)
+ position = io->position += offset;
+ break;
+
+ case G_SEEK_END:
+ if (io->used + offset <= io->used)
+ position = io->position = io->used + offset;
+ break;
+ }
+ }
+
+ return (toff_t) position;
+}
+
+static gint
+tiff_io_close (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean closed = FALSE;
+
+ if (io->input && ! io->output)
+ {
+ closed = g_input_stream_close (io->input, NULL, &error);
+ }
+ else
+ {
+ if (! io->can_seek && io->buffer && io->allocated)
+ {
+ if (! g_output_stream_write_all (io->output,
+ (void *) io->buffer,
+ io->allocated,
+ NULL, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (io->input)
+ {
+ closed = g_io_stream_close (G_IO_STREAM (io->stream), NULL, &error);
+ }
+ else
+ {
+ closed = g_output_stream_close (io->output, NULL, &error);
+ }
+ }
+
+ if (! closed)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (io->stream);
+ io->stream = NULL;
+ io->input = NULL;
+ io->output = NULL;
+
+ g_free (io->buffer);
+ io->buffer = NULL;
+
+ io->allocated = 0;
+ io->used = 0;
+ io->position = 0;
+
+ return closed ? 0 : -1;
+}
+
+static toff_t
+tiff_io_get_file_size (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ GFileInfo *info;
+ goffset size = 0;
+
+ info = g_file_query_info (io->file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+ if (! info)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ size = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+
+ return (toff_t) size;
+}