diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/file-tiff/file-tiff-io.c | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/file-tiff/file-tiff-io.c')
-rw-r--r-- | plug-ins/file-tiff/file-tiff-io.c | 585 |
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; +} |