diff options
Diffstat (limited to 'plug-ins/file-tiff/file-tiff-load.c')
-rw-r--r-- | plug-ins/file-tiff/file-tiff-load.c | 2815 |
1 files changed, 2815 insertions, 0 deletions
diff --git a/plug-ins/file-tiff/file-tiff-load.c b/plug-ins/file-tiff/file-tiff-load.c new file mode 100644 index 0000000..72e9538 --- /dev/null +++ b/plug-ins/file-tiff/file-tiff-load.c @@ -0,0 +1,2815 @@ +/* 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 + */ + +/* + * tifftopnm.c - converts a Tagged Image File to a portable anymap + * + * Derived by Jef Poskanzer from tif2ras.c, which is: + * + * Copyright (c) 1990 by Sun Microsystems, Inc. + * + * Author: Patrick J. Naughton + * naughton@wind.sun.com + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and that + * both that copyright notice and this permission notice appear in + * supporting documentation. + * + * This file is provided AS IS with no warranties of any kind. The author + * shall have no liability with respect to the infringement of copyrights, + * trade secrets or any patents by this file or any part thereof. In no + * event will the author be liable for any lost revenue or profits or + * other special, indirect and consequential damages. + */ + +#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" +#include "file-tiff-load.h" + +#include "libgimp/stdplugins-intl.h" + + +#define PLUG_IN_ROLE "gimp-file-tiff-load" + + +typedef struct +{ + gint compression; + gint fillorder; + gboolean save_transp_pixels; +} TiffSaveVals; + +typedef struct +{ + gint32 ID; + GeglBuffer *buffer; + const Babl *format; + guchar *pixels; + guchar *pixel; +} ChannelData; + +typedef enum +{ + GIMP_TIFF_LOAD_ASSOCALPHA, + GIMP_TIFF_LOAD_UNASSALPHA, + GIMP_TIFF_LOAD_CHANNEL +} DefaultExtra; + +typedef enum +{ + GIMP_TIFF_DEFAULT, + GIMP_TIFF_INDEXED, + GIMP_TIFF_GRAY, + GIMP_TIFF_GRAY_MINISWHITE, +} TiffColorMode; + +/* Declare some local functions */ + +static GimpColorProfile * load_profile (TIFF *tif); + +static void load_rgba (TIFF *tif, + ChannelData *channel); +static void load_contiguous (TIFF *tif, + ChannelData *channel, + const Babl *type, + gushort bps, + gushort spp, + TiffColorMode tiff_mode, + gboolean is_signed, + gint extra); +static void load_separate (TIFF *tif, + ChannelData *channel, + const Babl *type, + gushort bps, + gushort spp, + TiffColorMode tiff_mode, + gboolean is_signed, + gint extra); +static void load_paths (TIFF *tif, + gint image, + gint width, + gint height, + gint offset_x, + gint offset_y); + +static gboolean is_non_conformant_tiff (gushort photomet, + gushort spp); +static gushort get_extra_channels_count (gushort photomet, + gushort spp, + gboolean alpha); + +static void fill_bit2byte (TiffColorMode tiff_mode); +static void fill_2bit2byte (TiffColorMode tiff_mode); +static void fill_4bit2byte (TiffColorMode tiff_mode); +static void convert_bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height); +static void convert_2bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height); +static void convert_4bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height); + +static void convert_miniswhite (guchar *buffer, + gint width, + gint height); +static void convert_int2uint (guchar *buffer, + gint bps, + gint spp, + gint width, + gint height, + gint stride); + +static gboolean load_dialog (const gchar *help_id, + TiffSelectedPages *pages, + const gchar *extra_message, + DefaultExtra *default_extra); + +static void tiff_dialog_show_reduced (GtkWidget *toggle, + gpointer data); + + +static TiffSaveVals tsvals = +{ + COMPRESSION_NONE, /* compression */ + TRUE, /* alpha handling */ +}; + +/* Grayscale conversion mappings */ +static const guchar _1_to_8_bitmap [2] = +{ + 0, 255 +}; + +static const guchar _1_to_8_bitmap_rev [2] = +{ + 255, 0 +}; + +static const guchar _2_to_8_bitmap [4] = +{ + 0, 85, 170, 255 +}; + +static const guchar _2_to_8_bitmap_rev [4] = +{ + 255, 170, 85, 0 +}; + +static const guchar _4_to_8_bitmap [16] = +{ + 0, 17, 34, 51, 68, 85, 102, 119, + 136, 153, 170, 187, 204, 221, 238, 255 +}; + +static const guchar _4_to_8_bitmap_rev [16] = +{ + 255, 238, 221, 204, 187, 170, 153, 136, + 119, 102, 85, 68, 51, 34, 17, 0 +}; + +static guchar bit2byte[256 * 8]; +static guchar _2bit2byte[256 * 4]; +static guchar _4bit2byte[256 * 2]; + + +/* returns a pointer into the TIFF */ +static const gchar * +tiff_get_page_name (TIFF *tif) +{ + static gchar *name; + + if (TIFFGetField (tif, TIFFTAG_PAGENAME, &name) && + g_utf8_validate (name, -1, NULL)) + { + return name; + } + + return NULL; +} + +/* is_non_conformant_tiff assumes TIFFTAG_EXTRASAMPLES was not set */ +static gboolean +is_non_conformant_tiff (gushort photomet, gushort spp) +{ + switch (photomet) + { + case PHOTOMETRIC_RGB: + case PHOTOMETRIC_YCBCR: + case PHOTOMETRIC_CIELAB: + case PHOTOMETRIC_ICCLAB: + case PHOTOMETRIC_ITULAB: + case PHOTOMETRIC_LOGLUV: + return (spp > 3 || (spp == 2 && photomet != PHOTOMETRIC_RGB)); + break; + case PHOTOMETRIC_SEPARATED: + return (spp > 4); + break; + default: + return (spp > 1); + break; + } +} + +/* get_extra_channels_count returns number of channels excluding + * alpha and color channels + */ +static gushort +get_extra_channels_count (gushort photomet, gushort spp, gboolean alpha) +{ + switch (photomet) + { + case PHOTOMETRIC_RGB: + case PHOTOMETRIC_YCBCR: + case PHOTOMETRIC_CIELAB: + case PHOTOMETRIC_ICCLAB: + case PHOTOMETRIC_ITULAB: + case PHOTOMETRIC_LOGLUV: + if (spp >= 3) + return spp - 3 - (alpha? 1 : 0); + else + return spp - 1 - (alpha? 1 : 0); + break; + case PHOTOMETRIC_SEPARATED: + return spp - 4 - (alpha? 1 : 0); + break; + default: + return spp - 1 - (alpha? 1 : 0); + break; + } +} + +GimpPDBStatusType +load_image (GFile *file, + GimpRunMode run_mode, + gint32 *image, + gboolean *resolution_loaded, + gboolean *profile_loaded, + GError **error) +{ + TIFF *tif; + TiffSelectedPages pages; + + GList *images_list = NULL; + DefaultExtra default_extra = GIMP_TIFF_LOAD_UNASSALPHA; + gint first_image_type = GIMP_RGB; + gint min_row = G_MAXINT; + gint min_col = G_MAXINT; + gint max_row = 0; + gint max_col = 0; + GimpColorProfile *first_profile = NULL; + const gchar *extra_message = NULL; + gint li; + gint selectable_pages; + + *image = 0; + gimp_progress_init_printf (_("Opening '%s'"), + gimp_file_get_utf8_name (file)); + + tif = tiff_open (file, "r", error); + if (! tif) + { + if (! (error && *error)) + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("Not a TIFF image or image is corrupt.")); + + return GIMP_PDB_EXECUTION_ERROR; + } + + pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS; + gimp_get_data (LOAD_PROC "-target", &pages.target); + + pages.keep_empty_space = TRUE; + gimp_get_data (LOAD_PROC "-keep-empty-space", + &pages.keep_empty_space); + + pages.n_pages = pages.o_pages = TIFFNumberOfDirectories (tif); + if (pages.n_pages == 0) + { + /* See #5837. + * It seems we might be able to rescue some data even though the + * TIFF is possibly syntactically wrong. + */ + + /* libtiff says max number of directory is 65535. */ + for (li = 0; li < 65536; li++) + { + if (TIFFSetDirectory (tif, li) == 0) + break; + } + pages.n_pages = li; + if (pages.n_pages == 0) + { + TIFFClose (tif); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("TIFF '%s' does not contain any directories"), + gimp_file_get_utf8_name (file)); + + return GIMP_PDB_EXECUTION_ERROR; + } + + TIFFSetDirectory (tif, 0); + g_message (ngettext ("TIFF '%s' directory count by header failed " + "though there seems to be %d page." + " Attempting to load the file with this assumption.", + "TIFF '%s' directory count by header failed " + "though there seem to be %d pages." + " Attempting to load the file with this assumption.", + pages.n_pages), + gimp_file_get_utf8_name (file), pages.n_pages); + } + + pages.pages = NULL; + pages.n_filtered_pages = pages.n_pages; + pages.n_reducedimage_pages = pages.n_pages; + + pages.filtered_pages = g_new0 (gint, pages.n_pages); + for (li = 0; li < pages.n_pages; li++) + pages.filtered_pages[li] = li; + + if (pages.n_pages == 1 || run_mode != GIMP_RUN_INTERACTIVE) + { + pages.pages = g_new0 (gint, pages.n_pages); + for (li = 0; li < pages.n_pages; li++) + pages.pages[li] = li; + pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS; + } + + /* Check all pages if any has an unspecified or unset channel. */ + for (li = 0; li < pages.n_pages; li++) + { + gushort spp; + gushort photomet; + gushort extra; + gushort *extra_types; + gushort file_type = 0; + gboolean first_page_old_jpeg = FALSE; + + if (TIFFSetDirectory (tif, li) == 0) + continue; + + TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) + { + guint16 compression; + + if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) && + (compression == COMPRESSION_CCITTFAX3 || + compression == COMPRESSION_CCITTFAX4 || + compression == COMPRESSION_CCITTRLE || + compression == COMPRESSION_CCITTRLEW)) + { + photomet = PHOTOMETRIC_MINISWHITE; + } + else + { + /* old AppleScan software misses out the photometric tag + * (and incidentally assumes min-is-white, but xv + * assumes min-is-black, so we follow xv's lead. It's + * not much hardship to invert the image later). + */ + photomet = PHOTOMETRIC_MINISBLACK; + } + } + if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) + extra = 0; + + /* Try to detect if a TIFF page is a thumbnail. + * Easy case: if subfiletype is set to FILETYPE_REDUCEDIMAGE. + * If no subfiletype is defined we try to detect it ourselves. + * We will consider it a thumbnail if: + * - It's the second page + * - PhotometricInterpretation is YCbCr + * - Compression is old style jpeg + * - First page uses a different compression or PhotometricInterpretation + * + * We could also add a check for the presence of TIFFTAG_EXIFIFD since + * this should usually be a thumbnail part of EXIF metadata. Since that + * probably won't make a difference, I will leave that out for now. + */ + if (li == 0) + { + guint16 compression; + + if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) && + compression == COMPRESSION_OJPEG && + photomet == PHOTOMETRIC_YCBCR) + first_page_old_jpeg = TRUE; + } + + if (TIFFGetField (tif, TIFFTAG_SUBFILETYPE, &file_type)) + { + if (file_type == FILETYPE_REDUCEDIMAGE) + { + /* file_type is a mask but we will only filter out pages + * that only have FILETYPE_REDUCEDIMAGE set */ + pages.filtered_pages[li] = TIFF_REDUCEDFILE; + pages.n_filtered_pages--; + g_debug ("Page %d is a FILETYPE_REDUCEDIMAGE thumbnail.\n", li); + } + } + else + { + if (li == 1 && photomet == PHOTOMETRIC_YCBCR && + ! first_page_old_jpeg) + { + guint16 compression; + + if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) && + compression == COMPRESSION_OJPEG) + { + pages.filtered_pages[li] = TIFF_MISC_THUMBNAIL; + pages.n_filtered_pages--; + /* This is used to conditionally show reduced images + * if they're not a thumbnail + */ + pages.n_reducedimage_pages--; + g_debug ("Page %d is most likely a thumbnail.\n", li); + } + } + } + + /* TODO: current code always assumes that the alpha channel + * will be the first extra channel, though the TIFF spec does + * not mandate such assumption. A future improvement should be + * to actually loop through the extra channels and save the + * alpha channel index. + * Of course, this is an edge case, as most image would likely + * have only a single extra channel anyway. But still we could + * be more accurate. + */ + if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)) + { + extra_message = _("Extra channels with unspecified data."); + break; + } + else if (extra == 0 && is_non_conformant_tiff (photomet, spp)) + { + /* ExtraSamples field not set, yet we have more channels than + * the PhotometricInterpretation field suggests. + * This should not happen as the spec clearly says "This field + * must be present if there are extra samples". So the files + * can be considered non-conformant. + * Let's ask what to do with the channel. + */ + extra_message = _("Non-conformant TIFF: extra channels without 'ExtraSamples' field."); + } + } + TIFFSetDirectory (tif, 0); + + pages.show_reduced = FALSE; + if (pages.n_reducedimage_pages - pages.n_filtered_pages > 1) + pages.show_reduced = TRUE; + + pages.tif = tif; + + if (run_mode == GIMP_RUN_INTERACTIVE && + (pages.n_pages > 1 || extra_message) && + ! load_dialog (LOAD_PROC, &pages, + extra_message, &default_extra)) + { + TIFFClose (tif); + g_clear_pointer (&pages.pages, g_free); + + return GIMP_PDB_CANCEL; + } + + selectable_pages = pages.n_filtered_pages; + if (pages.show_reduced) + selectable_pages = pages.n_reducedimage_pages; + + /* Adjust pages to take filtered out pages into account. */ + if (pages.o_pages > selectable_pages) + { + gint fi; + gint sel_index = 0; + gint sel_add = 0; + + for (fi = 0; fi < pages.o_pages && sel_index < pages.n_pages; fi++) + { + if ((pages.show_reduced && pages.filtered_pages[fi] == TIFF_MISC_THUMBNAIL) || + (! pages.show_reduced && pages.filtered_pages[fi] <= TIFF_MISC_THUMBNAIL)) + { + sel_add++; + } + if (pages.pages[sel_index] + sel_add == fi) + { + pages.pages[sel_index] = fi; + sel_index++; + } + } + } + + gimp_set_data (LOAD_PROC "-target", + &pages.target, sizeof (pages.target)); + gimp_set_data (LOAD_PROC "-keep-empty-space", + &pages.keep_empty_space, + sizeof (pages.keep_empty_space)); + + /* We will loop through the all pages in case of multipage TIFF + * and load every page as a separate layer. + */ + for (li = 0; li < pages.n_pages; li++) + { + gint ilayer; + gushort bps; + gushort spp; + gushort photomet; + gshort sampleformat; + GimpColorProfile *profile; + gboolean profile_linear = FALSE; + GimpPrecision image_precision; + const Babl *type; + const Babl *base_format = NULL; + const Babl *space = NULL; + guint16 orientation; + gint cols; + gint rows; + gboolean alpha; + gint image_type = GIMP_RGB; + gint layer; + gint layer_type = GIMP_RGB_IMAGE; + float layer_offset_x = 0.0; + float layer_offset_y = 0.0; + gint layer_offset_x_pixel = 0; + gint layer_offset_y_pixel = 0; + gushort extra; + gushort *extra_types; + ChannelData *channel = NULL; + uint16 planar = PLANARCONFIG_CONTIG; + TiffColorMode tiff_mode; + gboolean is_signed; + gint i; + gboolean worst_case = FALSE; + TiffSaveVals save_vals; + const gchar *name; + + if (TIFFSetDirectory (tif, pages.pages[li]) == 0) + { + g_message (_("Couldn't read page %d of %d. Image might be corrupt.\n"), + li+1, pages.n_pages); + continue; + } + ilayer = pages.pages[li]; + + gimp_progress_update (0.0); + + TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps); + + TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sampleformat); + + profile = load_profile (tif); + if (! profile && first_profile) + { + profile = first_profile; + g_object_ref (profile); + } + + if (profile) + { + profile_linear = gimp_color_profile_is_linear (profile); + + if (! first_profile) + { + first_profile = profile; + g_object_ref (first_profile); + + if (profile_linear && li > 0 && pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES) + g_message (_("This image has a linear color profile but " + "it was not set on the first layer. " + "The layers below layer # %d will be " + "interpreted as non linear."), li+1); + } + else if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES && + ! gimp_color_profile_is_equal (first_profile, profile)) + { + g_message (_("This image has multiple color profiles. " + "We will use the first one. If this leads " + "to incorrect results you should consider " + "loading each layer as a separate image.")); + } + + if (! *image) + *profile_loaded = TRUE; + } + + if (bps > 64) + { + g_message (_("Suspicious bit depth: %d for page %d. Image may be corrupt."), + bps, li+1); + continue; + } + + if (bps > 8 && bps != 8 && bps != 16 && bps != 32 && bps != 64) + worst_case = TRUE; /* Wrong sample width => RGBA */ + + switch (bps) + { + case 1: + case 2: + case 4: + case 8: + if (profile_linear) + image_precision = GIMP_PRECISION_U8_LINEAR; + else + image_precision = GIMP_PRECISION_U8_GAMMA; + + type = babl_type ("u8"); + break; + + case 16: + if (sampleformat == SAMPLEFORMAT_IEEEFP) + { + if (profile_linear) + image_precision = GIMP_PRECISION_HALF_LINEAR; + else + image_precision = GIMP_PRECISION_HALF_GAMMA; + + type = babl_type ("half"); + } + else + { + if (profile_linear) + image_precision = GIMP_PRECISION_U16_LINEAR; + else + image_precision = GIMP_PRECISION_U16_GAMMA; + + type = babl_type ("u16"); + } + break; + + case 32: + if (sampleformat == SAMPLEFORMAT_IEEEFP) + { + if (profile_linear) + image_precision = GIMP_PRECISION_FLOAT_LINEAR; + else + image_precision = GIMP_PRECISION_FLOAT_GAMMA; + + type = babl_type ("float"); + } + else + { + if (profile_linear) + image_precision = GIMP_PRECISION_U32_LINEAR; + else + image_precision = GIMP_PRECISION_U32_GAMMA; + + type = babl_type ("u32"); + } + break; + + case 64: + if (profile_linear) + image_precision = GIMP_PRECISION_DOUBLE_LINEAR; + else + image_precision = GIMP_PRECISION_DOUBLE_GAMMA; + + type = babl_type ("double"); + break; + + default: + g_message (_("Unsupported bit depth: %d for page %d."), + bps, li+1); + continue; + } + + g_printerr ("bps: %d\n", bps); + + TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp); + + if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types)) + extra = 0; + + if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols)) + { + TIFFClose (tif); + g_message (_("Could not get image width from '%s'"), + gimp_file_get_utf8_name (file)); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows)) + { + TIFFClose (tif); + g_message (_("Could not get image length from '%s'"), + gimp_file_get_utf8_name (file)); + return GIMP_PDB_EXECUTION_ERROR; + } + + if (cols > GIMP_MAX_IMAGE_SIZE || cols <= 0 || + rows > GIMP_MAX_IMAGE_SIZE || rows <= 0) + { + g_message (_("Invalid image dimensions (%u x %u) for page %d. " + "Image may be corrupt."), + (guint32) cols, (guint32) rows, li+1); + continue; + } + else + { + g_printerr ("Image dimensions: %u x %u.\n", + (guint32) cols, (guint32) rows); + } + + if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) + { + guint16 compression; + + if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) && + (compression == COMPRESSION_CCITTFAX3 || + compression == COMPRESSION_CCITTFAX4 || + compression == COMPRESSION_CCITTRLE || + compression == COMPRESSION_CCITTRLEW)) + { + g_message (_("Could not get photometric from '%s'. " + "Image is CCITT compressed, assuming min-is-white"), + gimp_file_get_utf8_name (file)); + photomet = PHOTOMETRIC_MINISWHITE; + } + else + { + g_message (_("Could not get photometric from '%s'. " + "Assuming min-is-black"), + gimp_file_get_utf8_name (file)); + + /* old AppleScan software misses out the photometric tag + * (and incidentally assumes min-is-white, but xv + * assumes min-is-black, so we follow xv's lead. It's + * not much hardship to invert the image later). + */ + photomet = PHOTOMETRIC_MINISBLACK; + } + } + + /* test if the extrasample represents an associated alpha channel... */ + if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) + { + alpha = TRUE; + tsvals.save_transp_pixels = FALSE; + extra--; + } + else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA)) + { + alpha = TRUE; + tsvals.save_transp_pixels = TRUE; + extra--; + } + else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED)) + { + if (run_mode != GIMP_RUN_INTERACTIVE) + /* In non-interactive mode, we assume unassociated alpha if unspecified. + * We don't output messages in interactive mode as the user + * has already the ability to choose through a dialog. */ + g_message (_("Alpha channel type not defined for %s. " + "Assuming alpha is not premultiplied"), + gimp_file_get_utf8_name (file)); + + switch (default_extra) + { + case GIMP_TIFF_LOAD_ASSOCALPHA: + alpha = TRUE; + tsvals.save_transp_pixels = FALSE; + break; + case GIMP_TIFF_LOAD_UNASSALPHA: + alpha = TRUE; + tsvals.save_transp_pixels = TRUE; + break; + default: /* GIMP_TIFF_LOAD_CHANNEL */ + alpha = FALSE; + break; + } + extra--; + } + else /* extra == 0 */ + { + if (is_non_conformant_tiff (photomet, spp)) + { + if (run_mode != GIMP_RUN_INTERACTIVE) + g_message (_("Image '%s' does not conform to the TIFF specification: " + "ExtraSamples field is not set while extra channels are present. " + "Assuming the first extra channel is non-premultiplied alpha."), + gimp_file_get_utf8_name (file)); + + switch (default_extra) + { + case GIMP_TIFF_LOAD_ASSOCALPHA: + alpha = TRUE; + tsvals.save_transp_pixels = FALSE; + break; + case GIMP_TIFF_LOAD_UNASSALPHA: + alpha = TRUE; + tsvals.save_transp_pixels = TRUE; + break; + default: /* GIMP_TIFF_LOAD_CHANNEL */ + alpha = FALSE; + break; + } + } + else + { + alpha = FALSE; + } + } + + extra = get_extra_channels_count (photomet, spp, alpha); + + tiff_mode = GIMP_TIFF_DEFAULT; + is_signed = sampleformat == SAMPLEFORMAT_INT; + + switch (photomet) + { + case PHOTOMETRIC_PALETTE: + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + /* Even for bps >= we may need to use tiff_mode, so always set it. + * Currently we use it to detect the need to convert 8 bps miniswhite. */ + if (photomet == PHOTOMETRIC_PALETTE) + tiff_mode = GIMP_TIFF_INDEXED; + else if (photomet == PHOTOMETRIC_MINISBLACK) + tiff_mode = GIMP_TIFF_GRAY; + else if (photomet == PHOTOMETRIC_MINISWHITE) + tiff_mode = GIMP_TIFF_GRAY_MINISWHITE; + + if (bps < 8) + { + /* FIXME: It should be a user choice whether this should be + * interpreted as indexed or grayscale. For now we will + * use indexed (see issue #6766). */ + image_type = GIMP_INDEXED; + layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE; + + if ((bps == 1 || bps == 2 || bps == 4) && ! alpha && spp == 1) + { + if (bps == 1) + fill_bit2byte (tiff_mode); + else if (bps == 2) + fill_2bit2byte (tiff_mode); + else if (bps == 4) + fill_4bit2byte (tiff_mode); + } + } + else + { + if (photomet == PHOTOMETRIC_PALETTE) + { + image_type = GIMP_INDEXED; + layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE; + } + else + { + image_type = GIMP_GRAY; + layer_type = alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE; + } + } + + if (photomet == PHOTOMETRIC_PALETTE) + { + /* Do nothing here, handled later. + * Didn't want more indenting in the next part. */ + } + else if (alpha) + { + if (tsvals.save_transp_pixels) + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("YA"), + type, + babl_component ("Y"), + babl_component ("A"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("Y'A"), + type, + babl_component ("Y'"), + babl_component ("A"), + NULL); + } + } + else + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("YaA"), + type, + babl_component ("Ya"), + babl_component ("A"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("Y'aA"), + type, + babl_component ("Y'a"), + babl_component ("A"), + NULL); + } + } + } + else + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("Y"), + type, + babl_component ("Y"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("Y'"), + type, + babl_component ("Y'"), + NULL); + } + } + break; + + case PHOTOMETRIC_RGB: + image_type = GIMP_RGB; + layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE; + + if (alpha) + { + if (tsvals.save_transp_pixels) + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("RGBA"), + type, + babl_component ("R"), + babl_component ("G"), + babl_component ("B"), + babl_component ("A"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("R'G'B'A"), + type, + babl_component ("R'"), + babl_component ("G'"), + babl_component ("B'"), + babl_component ("A"), + NULL); + } + } + else + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("RaGaBaA"), + type, + babl_component ("Ra"), + babl_component ("Ga"), + babl_component ("Ba"), + babl_component ("A"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("R'aG'aB'aA"), + type, + babl_component ("R'a"), + babl_component ("G'a"), + babl_component ("B'a"), + babl_component ("A"), + NULL); + } + } + } + else + { + if (profile_linear) + { + base_format = babl_format_new (babl_model ("RGB"), + type, + babl_component ("R"), + babl_component ("G"), + babl_component ("B"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("R'G'B'"), + type, + babl_component ("R'"), + babl_component ("G'"), + babl_component ("B'"), + NULL); + } + } + break; + + case PHOTOMETRIC_SEPARATED: + layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE; + /* It's possible that a CMYK image might not have an + * attached profile, so we'll check for it and set up + * space accordingly + */ + if (profile && gimp_color_profile_is_cmyk (profile)) + { + space = gimp_color_profile_get_space (profile, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + error); + g_clear_object (&profile); + } + else + { + space = NULL; + } + + if (alpha) + base_format = babl_format_new (babl_model ("CMYKA"), + type, + babl_component ("Cyan"), + babl_component ("Magenta"), + babl_component ("Yellow"), + babl_component ("Key"), + babl_component ("A"), + NULL); + else + base_format = babl_format_new (babl_model ("CMYK"), + type, + babl_component ("Cyan"), + babl_component ("Magenta"), + babl_component ("Yellow"), + babl_component ("Key"), + NULL); + + base_format = + babl_format_with_space (babl_format_get_encoding (base_format), + space); + break; + + default: + g_printerr ("photomet: %d (%d)\n", photomet, PHOTOMETRIC_PALETTE); + worst_case = TRUE; + break; + } + + /* attach a parasite containing the compression */ + { + guint16 compression = COMPRESSION_NONE; + + if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression)) + { + switch (compression) + { + case COMPRESSION_NONE: + case COMPRESSION_LZW: + case COMPRESSION_PACKBITS: + case COMPRESSION_DEFLATE: + case COMPRESSION_ADOBE_DEFLATE: + case COMPRESSION_JPEG: + case COMPRESSION_CCITTFAX3: + case COMPRESSION_CCITTFAX4: + break; + + case COMPRESSION_OJPEG: + worst_case = TRUE; + compression = COMPRESSION_JPEG; + break; + + default: + g_message (_("Invalid or unknown compression %u. " + "Setting compression to none."), + compression); + compression = COMPRESSION_NONE; + break; + } + } + + save_vals.compression = compression; + } + + if (worst_case) + { + image_type = GIMP_RGB; + layer_type = GIMP_RGBA_IMAGE; + + if (profile_linear) + { + base_format = babl_format_new (babl_model ("RaGaBaA"), + type, + babl_component ("Ra"), + babl_component ("Ga"), + babl_component ("Ba"), + babl_component ("A"), + NULL); + } + else + { + base_format = babl_format_new (babl_model ("R'aG'aB'aA"), + type, + babl_component ("R'a"), + babl_component ("G'a"), + babl_component ("B'a"), + babl_component ("A"), + NULL); + } + } + + if (pages.target == GIMP_PAGE_SELECTOR_TARGET_LAYERS) + { + if (li == 0) + { + first_image_type = image_type; + } + else if (image_type != first_image_type) + { + continue; + } + } + + if ((pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) || (! *image)) + { + *image = gimp_image_new_with_precision (cols, rows, image_type, + image_precision); + + if (*image < 1) + { + TIFFClose (tif); + g_message (_("Could not create a new image: %s"), + gimp_get_pdb_error ()); + return GIMP_PDB_EXECUTION_ERROR; + } + + gimp_image_undo_disable (*image); + + if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) + { + gchar *fname = g_strdup_printf ("%s-%d", g_file_get_uri (file), + ilayer); + + gimp_image_set_filename (*image, fname); + g_free (fname); + + images_list = g_list_prepend (images_list, + GINT_TO_POINTER (*image)); + } + else if (pages.o_pages != pages.n_pages) + { + gchar *fname = g_strdup_printf (_("%s-%d-of-%d-pages"), + g_file_get_uri (file), + pages.n_pages, pages.o_pages); + + gimp_image_set_filename (*image, fname); + g_free (fname); + } + else + { + gimp_image_set_filename (*image, g_file_get_uri (file)); + } + } + + /* attach non-CMYK color profile */ + if (profile) + { + if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES || profile == first_profile) + gimp_image_set_color_profile (*image, profile); + + g_object_unref (profile); + } + + /* attach parasites */ + { + GimpParasite *parasite; + const gchar *img_desc; + + parasite = gimp_parasite_new ("tiff-save-options", 0, + sizeof (save_vals), &save_vals); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + + /* Attach a parasite containing the image description. + * Pretend to be a gimp comment so other plugins will use this + * description as an image comment where appropriate. + */ + if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &img_desc) && + g_utf8_validate (img_desc, -1, NULL)) + { + parasite = gimp_parasite_new ("gimp-comment", + GIMP_PARASITE_PERSISTENT, + strlen (img_desc) + 1, img_desc); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + } + + /* Attach GeoTIFF Tags as Parasite, If available */ + { + GimpParasite *parasite = NULL; + void *geotag_data = NULL; + uint32 count = 0; + + if (TIFFGetField (tif, GEOTIFF_MODELPIXELSCALE, &count, &geotag_data)) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelPixelScale", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_DOUBLE) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + + if (TIFFGetField (tif, GEOTIFF_MODELTIEPOINT, &count, &geotag_data)) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTiePoint", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_DOUBLE) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + + if (TIFFGetField (tif, GEOTIFF_MODELTRANSFORMATION, &count, &geotag_data)) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTransformation", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_DOUBLE) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + + if (TIFFGetField (tif, GEOTIFF_KEYDIRECTORY, &count, &geotag_data) ) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_KeyDirectory", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_SHORT) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + + if (TIFFGetField (tif, GEOTIFF_DOUBLEPARAMS, &count, &geotag_data)) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_DoubleParams", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_DOUBLE) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + + if (TIFFGetField (tif, GEOTIFF_ASCIIPARAMS, &count, &geotag_data)) + { + parasite = gimp_parasite_new ("Gimp_GeoTIFF_Asciiparams", + GIMP_PARASITE_PERSISTENT, + (TIFFDataWidth (TIFF_ASCII) * count), + geotag_data); + gimp_image_attach_parasite (*image, parasite); + gimp_parasite_free (parasite); + } + } + + /* any resolution info in the file? */ + { + gfloat xres = 72.0; + gfloat yres = 72.0; + gushort read_unit; + GimpUnit unit = GIMP_UNIT_PIXEL; /* invalid unit */ + + if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres)) + { + if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres)) + { + if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT, + &read_unit)) + { + switch (read_unit) + { + case RESUNIT_NONE: + /* ImageMagick writes files with this silly resunit */ + break; + + case RESUNIT_INCH: + unit = GIMP_UNIT_INCH; + break; + + case RESUNIT_CENTIMETER: + xres *= 2.54; + yres *= 2.54; + unit = GIMP_UNIT_MM; /* this is our default metric unit */ + break; + + default: + g_message (_("Unknown resolution " + "unit type %d, assuming dpi"), read_unit); + break; + } + } + else + { + /* no res unit tag */ + + /* old AppleScan software produces these */ + g_message (_("Warning: resolution specified without " + "unit type, assuming dpi")); + } + } + else + { + /* xres but no yres */ + + g_message (_("Warning: no y resolution info, assuming same as x")); + yres = xres; + } + + /* now set the new image's resolution info */ + + /* If it is invalid, instead of forcing 72dpi, do not set + * the resolution at all. Gimp will then use the default + * set by the user + */ + if (read_unit != RESUNIT_NONE) + { + if (! isfinite (xres) || + xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION || + ! isfinite (yres) || + yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION) + { + g_message (_("Invalid image resolution info, using default")); + /* We need valid xres and yres for computing + * layer_offset_x_pixel and layer_offset_y_pixel. + */ + gimp_image_get_resolution (*image, &xres, &yres); + } + else + { + gimp_image_set_resolution (*image, xres, yres); + if (unit != GIMP_UNIT_PIXEL) + gimp_image_set_unit (*image, unit); + + *resolution_loaded = TRUE; + } + } + } + + /* no x res tag => we assume we have no resolution info, so we + * don't care. Older versions of this plugin used to write + * files with no resolution tags at all. + */ + + /* TODO: haven't caught the case where yres tag is present, + * but not xres. This is left as an exercise for the reader - + * they should feel free to shoot the author of the broken + * program that produced the damaged TIFF file in the first + * place. + */ + + /* handle layer offset */ + if (! TIFFGetField (tif, TIFFTAG_XPOSITION, &layer_offset_x)) + layer_offset_x = 0.0; + + if (! TIFFGetField (tif, TIFFTAG_YPOSITION, &layer_offset_y)) + layer_offset_y = 0.0; + + /* round floating point position to integer position required + * by GIMP + */ + layer_offset_x_pixel = ROUND (layer_offset_x * xres); + layer_offset_y_pixel = ROUND (layer_offset_y * yres); + } + + /* Install colormap for INDEXED images only */ + if (image_type == GIMP_INDEXED) + { + guchar cmap[768]; + + if (photomet == PHOTOMETRIC_PALETTE) + { + gushort *redmap; + gushort *greenmap; + gushort *bluemap; + gint i, j; + + if (! TIFFGetField (tif, TIFFTAG_COLORMAP, + &redmap, &greenmap, &bluemap)) + { + TIFFClose (tif); + g_message (_("Could not get colormaps from '%s'"), + gimp_file_get_utf8_name (file)); + return GIMP_PDB_EXECUTION_ERROR; + } + + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = redmap[i] >> 8; + cmap[j++] = greenmap[i] >> 8; + cmap[j++] = bluemap[i] >> 8; + } + + } + else if (photomet == PHOTOMETRIC_MINISBLACK) + { + gint i, j; + + if (bps == 1) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _1_to_8_bitmap[i]; + cmap[j++] = _1_to_8_bitmap[i]; + cmap[j++] = _1_to_8_bitmap[i]; + } + } + else if (bps == 2) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _2_to_8_bitmap[i]; + cmap[j++] = _2_to_8_bitmap[i]; + cmap[j++] = _2_to_8_bitmap[i]; + } + } + else if (bps == 4) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _4_to_8_bitmap[i]; + cmap[j++] = _4_to_8_bitmap[i]; + cmap[j++] = _4_to_8_bitmap[i]; + } + } + } + else if (photomet == PHOTOMETRIC_MINISWHITE) + { + gint i, j; + + if (bps == 1) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _1_to_8_bitmap_rev[i]; + cmap[j++] = _1_to_8_bitmap_rev[i]; + cmap[j++] = _1_to_8_bitmap_rev[i]; + } + } + else if (bps == 2) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _2_to_8_bitmap_rev[i]; + cmap[j++] = _2_to_8_bitmap_rev[i]; + cmap[j++] = _2_to_8_bitmap_rev[i]; + } + } + else if (bps == 4) + { + for (i = 0, j = 0; i < (1 << bps); i++) + { + cmap[j++] = _4_to_8_bitmap_rev[i]; + cmap[j++] = _4_to_8_bitmap_rev[i]; + cmap[j++] = _4_to_8_bitmap_rev[i]; + } + } + } + + gimp_image_set_colormap (*image, cmap, (1 << bps)); + } + + if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES) + load_paths (tif, *image, cols, rows, + layer_offset_x_pixel, layer_offset_y_pixel); + else + load_paths (tif, *image, cols, rows, 0, 0); + + if (extra > 99) + { + /* Validate number of channels to the same maximum as we use for + * Photoshop. A higher number most likely means a corrupt image + * and can cause GIMP to become unresponsive and/or stuck. + * See m2-d0f86ab189cbe900ec389ca6d7464713.tif from imagetestsuite + */ + g_message (_("Suspicious number of extra channels: %d. Possibly corrupt image."), extra); + extra = 99; + } + + /* Allocate ChannelData for all channels, even the background layer */ + channel = g_new0 (ChannelData, extra + 1); + + /* try and use layer name from tiff file */ + name = tiff_get_page_name (tif); + + if (name) + { + layer = gimp_layer_new (*image, name, + cols, rows, + layer_type, + 100, + gimp_image_get_default_new_layer_mode (*image)); + } + else + { + gchar *name; + + if (ilayer == 0) + name = g_strdup (_("Background")); + else + name = g_strdup_printf (_("Page %d"), ilayer); + + layer = gimp_layer_new (*image, name, + cols, rows, + layer_type, + 100, + gimp_image_get_default_new_layer_mode (*image)); + g_free (name); + } + + if (! base_format && image_type == GIMP_INDEXED) + { + /* can't create the palette format here, need to get it from + * an existing layer + */ + base_format = gimp_drawable_get_format (layer); + } + else if (! space) + { + base_format = + babl_format_with_space (babl_format_get_encoding (base_format), + gimp_drawable_get_format (layer)); + } + + channel[0].ID = layer; + channel[0].buffer = gimp_drawable_get_buffer (layer); + channel[0].format = base_format; + + if (extra > 0 && ! worst_case) + { + /* Add extra channels as appropriate */ + for (i = 1; i <= extra; i++) + { + GimpRGB color; + + gimp_rgb_set (&color, 0.0, 0.0, 0.0); + + channel[i].ID = gimp_channel_new (*image, _("TIFF Channel"), + cols, rows, + 100.0, &color); + gimp_image_insert_channel (*image, channel[i].ID, -1, 0); + channel[i].buffer = gimp_drawable_get_buffer (channel[i].ID); + channel[i].format = babl_format_new (babl_model ("Y'"), + type, + babl_component ("Y'"), + NULL); + } + } + + TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar); + + if (worst_case) + { + load_rgba (tif, channel); + } + else if (planar == PLANARCONFIG_CONTIG) + { + load_contiguous (tif, channel, type, bps, spp, + tiff_mode, is_signed, extra); + } + else + { + load_separate (tif, channel, type, bps, spp, + tiff_mode, is_signed, extra); + } + + if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation)) + { + gboolean flip_horizontal = FALSE; + gboolean flip_vertical = FALSE; + + switch (orientation) + { + case ORIENTATION_TOPLEFT: + break; + + case ORIENTATION_TOPRIGHT: + flip_horizontal = TRUE; + break; + + case ORIENTATION_BOTRIGHT: + flip_horizontal = TRUE; + flip_vertical = TRUE; + break; + + case ORIENTATION_BOTLEFT: + flip_vertical = TRUE; + break; + + default: + g_warning ("Orientation %d not handled yet!", orientation); + break; + } + + if (flip_horizontal) + gimp_item_transform_flip_simple (layer, + GIMP_ORIENTATION_HORIZONTAL, + TRUE /* auto_center */, + -1.0 /* axis */); + + if (flip_vertical) + gimp_item_transform_flip_simple (layer, + GIMP_ORIENTATION_VERTICAL, + TRUE /* auto_center */, + -1.0 /* axis */); + } + + for (i = 0; i <= extra; i++) + { + if (channel[i].buffer) + g_object_unref (channel[i].buffer); + } + + g_free (channel); + channel = NULL; + + if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES) + { + /* compute bounding box of all layers read so far */ + if (min_col > layer_offset_x_pixel) + min_col = layer_offset_x_pixel; + if (min_row > layer_offset_y_pixel) + min_row = layer_offset_y_pixel; + + if (max_col < layer_offset_x_pixel + cols) + max_col = layer_offset_x_pixel + cols; + if (max_row < layer_offset_y_pixel + rows) + max_row = layer_offset_y_pixel + rows; + + /* position the layer */ + if (layer_offset_x_pixel > 0 || + layer_offset_y_pixel > 0) + { + gimp_layer_set_offsets (layer, + layer_offset_x_pixel, + layer_offset_y_pixel); + } + } + + gimp_image_insert_layer (*image, layer, -1, -1); + + if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) + { + gimp_image_undo_enable (*image); + gimp_image_clean_all (*image); + } + + gimp_progress_update (1.0); + } + g_clear_object (&first_profile); + + if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) + { + GList *list = images_list; + + if (list) + { + *image = GPOINTER_TO_INT (list->data); + + list = g_list_next (list); + } + + for (; list; list = g_list_next (list)) + { + gimp_display_new (GPOINTER_TO_INT (list->data)); + } + + g_list_free (images_list); + } + else + { + if (! (*image)) + { + TIFFClose (tif); + g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, + _("No data could be read from TIFF '%s'. The file is probably corrupted."), + gimp_file_get_utf8_name (file)); + + return GIMP_PDB_EXECUTION_ERROR; + } + + if (pages.keep_empty_space) + { + /* unfortunately we have no idea about empty space + at the bottom/right of layers */ + min_col = 0; + min_row = 0; + } + + /* resize image to bounding box of all layers */ + gimp_image_resize (*image, + max_col - min_col, max_row - min_row, + -min_col, -min_row); + + gimp_image_undo_enable (*image); + } + + g_free (pages.pages); + TIFFClose (tif); + + return GIMP_PDB_SUCCESS; +} + +static GimpColorProfile * +load_profile (TIFF *tif) +{ + GimpColorProfile *profile = NULL; + +#ifdef TIFFTAG_ICCPROFILE + /* If TIFFTAG_ICCPROFILE is defined we are dealing with a + * libtiff version that can handle ICC profiles. Otherwise just + * return a NULL profile. + */ + uint32 profile_size; + guchar *icc_profile; + + /* set the ICC profile - if found in the TIFF */ + if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile)) + { + profile = gimp_color_profile_new_from_icc_profile (icc_profile, + profile_size, + NULL); + } +#endif + + return profile; +} + +static void +load_rgba (TIFF *tif, + ChannelData *channel) +{ + guint32 image_width; + guint32 image_height; + guint32 row; + guint32 *buffer; + + g_printerr ("%s\n", __func__); + + TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height); + + buffer = g_new (uint32, image_width * image_height); + + if (! TIFFReadRGBAImage (tif, image_width, image_height, buffer, 0)) + { + g_message (_("%s: Unsupported image format, no RGBA loader available"), + G_STRFUNC); + g_free (buffer); + return; + } + + for (row = 0; row < image_height; row++) + { +#if G_BYTE_ORDER != G_LITTLE_ENDIAN + /* Make sure our channels are in the right order */ + guint32 row_start = row * image_width; + guint32 row_end = row_start + image_width; + guint32 i; + + for (i = row_start; i < row_end; i++) + buffer[i] = GUINT32_TO_LE (buffer[i]); +#endif + + gegl_buffer_set (channel[0].buffer, + GEGL_RECTANGLE (0, image_height - row - 1, + image_width, 1), + 0, channel[0].format, + ((guchar *) buffer) + row * image_width * 4, + GEGL_AUTO_ROWSTRIDE); + + if ((row % 32) == 0) + gimp_progress_update ((gdouble) row / (gdouble) image_height); + } + + g_free (buffer); +} + +static void +load_paths (TIFF *tif, + gint image, + gint width, + gint height, + gint offset_x, + gint offset_y) +{ + gsize n_bytes; + gchar *bytes; + gint path_index; + gsize pos; + + if (! TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &n_bytes, &bytes)) + return; + + path_index = 0; + pos = 0; + + while (pos < n_bytes) + { + guint16 id; + gsize len; + gchar *name; + guint32 *val32; + guint16 *val16; + + if (n_bytes-pos < 7 || + strncmp (bytes + pos, "8BIM", 4) != 0) + break; + + pos += 4; + + val16 = (guint16 *) (bytes + pos); + id = GUINT16_FROM_BE (*val16); + pos += 2; + + /* g_printerr ("id: %x\n", id); */ + len = (guchar) bytes[pos]; + + if (n_bytes - pos < len + 1) + break; /* block not big enough */ + + /* do we have the UTF-marker? is it valid UTF-8? + * if so, we assume an utf-8 encoded name, otherwise we + * assume iso8859-1 + */ + name = bytes + pos + 1; + if (len >= 3 && + name[0] == '\xEF' && name[1] == '\xBB' && name[2] == '\xBF' && + g_utf8_validate (name, len, NULL)) + { + name = g_strndup (name + 3, len - 3); + } + else + { + name = g_convert (name, len, "utf-8", "iso8859-1", NULL, NULL, NULL); + } + + if (! name) + name = g_strdup ("(imported path)"); + + pos += len + 1; + + if (pos % 2) /* padding */ + pos++; + + if (n_bytes - pos < 4) + break; /* block not big enough */ + + val32 = (guint32 *) (bytes + pos); + len = GUINT32_FROM_BE (*val32); + pos += 4; + + if (n_bytes - pos < len) + break; /* block not big enough */ + + if (id >= 2000 && id <= 2998) + { + /* path information */ + guint16 type; + gint rec = pos; + gint32 vectors; + gdouble *points = NULL; + gint expected_points = 0; + gint pointcount = 0; + gboolean closed = FALSE; + + vectors = gimp_vectors_new (image, name); + gimp_image_insert_vectors (image, vectors, -1, path_index); + path_index++; + + while (rec < pos + len) + { + /* path records */ + val16 = (guint16 *) (bytes + rec); + type = GUINT16_FROM_BE (*val16); + + switch (type) + { + case 0: /* new closed subpath */ + case 3: /* new open subpath */ + val16 = (guint16 *) (bytes + rec + 2); + expected_points = GUINT16_FROM_BE (*val16); + pointcount = 0; + closed = (type == 0); + + if (n_bytes - rec < (expected_points + 1) * 26) + { + g_printerr ("not enough point records\n"); + rec = pos + len; + continue; + } + + if (points) + g_free (points); + points = g_new (gdouble, expected_points * 6); + break; + + case 1: /* closed subpath bezier knot, linked */ + case 2: /* closed subpath bezier knot, unlinked */ + case 4: /* open subpath bezier knot, linked */ + case 5: /* open subpath bezier knot, unlinked */ + /* since we already know if the subpath is open + * or closed and since we don't differentiate between + * linked and unlinked, just treat all the same... */ + + if (pointcount < expected_points) + { + gint j; + + for (j = 0; j < 6; j++) + { + gdouble f; + guint32 coord; + + const gint size = j % 2 ? width : height; + const gint offset = j % 2 ? offset_x : offset_y; + + val32 = (guint32 *) (bytes + rec + 2 + j * 4); + coord = GUINT32_FROM_BE (*val32); + + f = (double) ((gchar) ((coord >> 24) & 0xFF)) + + (double) (coord & 0x00FFFFFF) / + (double) 0xFFFFFF; + + /* coords are stored with vertical component + * first, gimp expects the horizontal + * component first. Sigh. + */ + points[pointcount * 6 + (j ^ 1)] = f * size + offset; + } + + pointcount++; + + if (pointcount == expected_points) + { + gimp_vectors_stroke_new_from_points (vectors, + GIMP_VECTORS_STROKE_TYPE_BEZIER, + pointcount * 6, + points, + closed); + } + } + else + { + g_printerr ("Oops - unexpected point record\n"); + } + + break; + + case 6: /* path fill rule record */ + case 7: /* clipboard record (?) */ + case 8: /* initial fill rule record (?) */ + /* we cannot use this information */ + + default: + break; + } + + rec += 26; + } + + if (points) + g_free (points); + } + + pos += len; + + if (pos % 2) /* padding */ + pos++; + + g_free (name); + } +} + + +static void +load_contiguous (TIFF *tif, + ChannelData *channel, + const Babl *type, + gushort bps, + gushort spp, + TiffColorMode tiff_mode, + gboolean is_signed, + gint extra) +{ + guint32 image_width; + guint32 image_height; + guint32 tile_width; + guint32 tile_height; + gint bytes_per_pixel; + const Babl *src_format; + guchar *buffer; + guchar *bw_buffer = NULL; + gdouble progress = 0.0; + gdouble one_row; + guint32 y; + gint i; + gboolean needs_upscale = FALSE; + + g_printerr ("%s\n", __func__); + + TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height); + + tile_width = image_width; + + if (TIFFIsTiled (tif)) + { + TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width); + TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height); + + buffer = g_malloc (TIFFTileSize (tif)); + } + else + { + tile_width = image_width; + tile_height = 1; + + buffer = g_malloc (TIFFScanlineSize (tif)); + } + + if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8) + { + needs_upscale = TRUE; + bw_buffer = g_malloc (tile_width * tile_height); + } + + one_row = (gdouble) tile_height / (gdouble) image_height; + + src_format = babl_format_n (type, spp); + + /* consistency check */ + bytes_per_pixel = 0; + for (i = 0; i <= extra; i++) + bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format); + + g_printerr ("bytes_per_pixel: %d, format: %d\n", + bytes_per_pixel, + babl_format_get_bytes_per_pixel (src_format)); + + for (y = 0; y < image_height; y += tile_height) + { + guint32 x; + + for (x = 0; x < image_width; x += tile_width) + { + GeglBuffer *src_buf; + guint32 rows; + guint32 cols; + gint offset; + + gimp_progress_update (progress + one_row * + ((gdouble) x / (gdouble) image_width)); + + if (TIFFIsTiled (tif)) + { + if (TIFFReadTile (tif, buffer, x, y, 0, 0) == -1) + { + g_message (_("Reading tile failed. Image may be corrupt at line %d."), y); + g_free (buffer); + g_free (bw_buffer); + return; + } + } + else if (TIFFReadScanline (tif, buffer, y, 0) == -1) + { + /* Error reading scanline, stop loading */ + g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y); + g_free (buffer); + g_free (bw_buffer); + return; + } + + cols = MIN (image_width - x, tile_width); + rows = MIN (image_height - y, tile_height); + + if (needs_upscale) + { + if (bps == 1) + convert_bit2byte (buffer, bw_buffer, cols, rows); + else if (bps == 2) + convert_2bit2byte (buffer, bw_buffer, cols, rows); + else if (bps == 4) + convert_4bit2byte (buffer, bw_buffer, cols, rows); + } + else if (is_signed) + { + convert_int2uint (buffer, bps, spp, cols, rows, + tile_width * bytes_per_pixel); + } + + if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8) + { + convert_miniswhite (buffer, cols, rows); + } + + src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer, + src_format, + GEGL_RECTANGLE (0, 0, cols, rows), + tile_width * bytes_per_pixel, + NULL, NULL); + + offset = 0; + + for (i = 0; i <= extra; i++) + { + GeglBufferIterator *iter; + gint src_bpp; + gint dest_bpp; + + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format); + + iter = gegl_buffer_iterator_new (src_buf, + GEGL_RECTANGLE (0, 0, cols, rows), + 0, NULL, + GEGL_ACCESS_READ, + GEGL_ABYSS_NONE, 2); + gegl_buffer_iterator_add (iter, channel[i].buffer, + GEGL_RECTANGLE (x, y, cols, rows), + 0, channel[i].format, + GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + guchar *s = iter->items[0].data; + guchar *d = iter->items[1].data; + gint length = iter->length; + + s += offset; + + while (length--) + { + memcpy (d, s, dest_bpp); + d += dest_bpp; + s += src_bpp; + } + } + + offset += dest_bpp; + } + + g_object_unref (src_buf); + } + + progress += one_row; + } + + g_free (buffer); + g_free (bw_buffer); +} + + +static void +load_separate (TIFF *tif, + ChannelData *channel, + const Babl *type, + gushort bps, + gushort spp, + TiffColorMode tiff_mode, + gboolean is_signed, + gint extra) +{ + guint32 image_width; + guint32 image_height; + guint32 tile_width; + guint32 tile_height; + gint bytes_per_pixel; + const Babl *src_format; + guchar *buffer; + guchar *bw_buffer = NULL; + gdouble progress = 0.0; + gdouble one_row; + gint i, compindex; + gboolean needs_upscale = FALSE; + + g_printerr ("%s\n", __func__); + + TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width); + TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height); + + tile_width = image_width; + + if (TIFFIsTiled (tif)) + { + TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width); + TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height); + + buffer = g_malloc (TIFFTileSize (tif)); + } + else + { + tile_width = image_width; + tile_height = 1; + + buffer = g_malloc (TIFFScanlineSize (tif)); + } + + if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8) + { + needs_upscale = TRUE; + bw_buffer = g_malloc (tile_width * tile_height); + } + + one_row = (gdouble) tile_height / (gdouble) image_height; + + src_format = babl_format_n (type, 1); + + /* consistency check */ + bytes_per_pixel = 0; + for (i = 0; i <= extra; i++) + bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format); + + g_printerr ("bytes_per_pixel: %d, format: %d\n", + bytes_per_pixel, + babl_format_get_bytes_per_pixel (src_format)); + + compindex = 0; + + for (i = 0; i <= extra; i++) + { + gint n_comps; + gint src_bpp; + gint dest_bpp; + gint offset; + gint j; + + n_comps = babl_format_get_n_components (channel[i].format); + src_bpp = babl_format_get_bytes_per_pixel (src_format); + dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format); + + offset = 0; + + for (j = 0; j < n_comps; j++) + { + guint32 y; + + for (y = 0; y < image_height; y += tile_height) + { + guint32 x; + + for (x = 0; x < image_width; x += tile_width) + { + GeglBuffer *src_buf; + GeglBufferIterator *iter; + guint32 rows; + guint32 cols; + + gimp_progress_update (progress + one_row * + ((gdouble) x / (gdouble) image_width)); + + if (TIFFIsTiled (tif)) + { + if (TIFFReadTile (tif, buffer, x, y, 0, compindex) == -1) + { + g_message (_("Reading tile failed. Image may be corrupt at line %d."), y); + g_free (buffer); + g_free (bw_buffer); + return; + } + } + else if (TIFFReadScanline (tif, buffer, y, compindex) == -1) + { + /* Error reading scanline, stop loading */ + g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y); + g_free (buffer); + g_free (bw_buffer); + return; + } + + cols = MIN (image_width - x, tile_width); + rows = MIN (image_height - y, tile_height); + + if (needs_upscale) + { + if (bps == 1) + convert_bit2byte (buffer, bw_buffer, cols, rows); + else if (bps == 2) + convert_2bit2byte (buffer, bw_buffer, cols, rows); + else if (bps == 4) + convert_4bit2byte (buffer, bw_buffer, cols, rows); + } + else if (is_signed) + { + convert_int2uint (buffer, bps, 1, cols, rows, + tile_width * bytes_per_pixel); + } + + if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8) + { + convert_miniswhite (buffer, cols, rows); + } + + src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer, + src_format, + GEGL_RECTANGLE (0, 0, cols, rows), + GEGL_AUTO_ROWSTRIDE, + NULL, NULL); + + iter = gegl_buffer_iterator_new (src_buf, + GEGL_RECTANGLE (0, 0, cols, rows), + 0, NULL, + GEGL_ACCESS_READ, + GEGL_ABYSS_NONE, 2); + gegl_buffer_iterator_add (iter, channel[i].buffer, + GEGL_RECTANGLE (x, y, cols, rows), + 0, channel[i].format, + GEGL_ACCESS_READWRITE, + GEGL_ABYSS_NONE); + + while (gegl_buffer_iterator_next (iter)) + { + guchar *s = iter->items[0].data; + guchar *d = iter->items[1].data; + gint length = iter->length; + + d += offset; + + while (length--) + { + memcpy (d, s, src_bpp); + d += dest_bpp; + s += src_bpp; + } + } + + g_object_unref (src_buf); + } + } + + offset += src_bpp; + compindex++; + } + + progress += one_row; + } + + g_free (buffer); + g_free (bw_buffer); +} + +static void +fill_bit2byte (TiffColorMode tiff_mode) +{ + static gboolean filled = FALSE; + + guchar *dest; + gint i, j; + + if (filled) + return; + + dest = bit2byte; + + if (tiff_mode == GIMP_TIFF_INDEXED) + { + for (j = 0; j < 256; j++) + for (i = 7; i >= 0; i--) + { + *(dest++) = ((j & (1 << i)) != 0); + } + } + else if (tiff_mode != GIMP_TIFF_DEFAULT) + { + guchar *_to_8_bitmap = NULL; + + if (tiff_mode == GIMP_TIFF_GRAY) + _to_8_bitmap = (guchar *) &_1_to_8_bitmap; + else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE) + _to_8_bitmap = (guchar *) &_1_to_8_bitmap_rev; + + for (j = 0; j < 256; j++) + for (i = 7; i >= 0; i--) + { + gint idx; + + idx = ((j & (1 << i)) != 0); + *(dest++) = _to_8_bitmap[idx]; + } + } + + filled = TRUE; +} + +static void +fill_2bit2byte (TiffColorMode tiff_mode) +{ + static gboolean filled2 = FALSE; + + guchar *dest; + gint i, j; + + if (filled2) + return; + + dest = _2bit2byte; + + if (tiff_mode == GIMP_TIFF_INDEXED) + { + for (j = 0; j < 256; j++) + { + for (i = 3; i >= 0; i--) + { + *(dest++) = ((j & (3 << (2*i))) >> (2*i)); + } + } + } + else if (tiff_mode != GIMP_TIFF_DEFAULT) + { + guchar *_to_8_bitmap = NULL; + + if (tiff_mode == GIMP_TIFF_GRAY) + _to_8_bitmap = (guchar *) &_2_to_8_bitmap; + else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE) + _to_8_bitmap = (guchar *) &_2_to_8_bitmap_rev; + + for (j = 0; j < 256; j++) + { + for (i = 3; i >= 0; i--) + { + gint idx; + + idx = ((j & (3 << (2*i))) >> (2*i)); + *(dest++) = _to_8_bitmap[idx]; + } + } + } + + filled2 = TRUE; +} + +static void +fill_4bit2byte (TiffColorMode tiff_mode) +{ + static gboolean filled4 = FALSE; + + guchar *dest; + gint i, j; + + if (filled4) + return; + + dest = _4bit2byte; + + if (tiff_mode == GIMP_TIFF_INDEXED) + { + for (j = 0; j < 256; j++) + { + for (i = 1; i >= 0; i--) + { + *(dest++) = ((j & (15 << (4*i))) >> (4*i)); + } + } + } + else if (tiff_mode != GIMP_TIFF_DEFAULT) + { + guchar *_to_8_bitmap = NULL; + + if (tiff_mode == GIMP_TIFF_GRAY) + _to_8_bitmap = (guchar *) &_4_to_8_bitmap; + else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE) + _to_8_bitmap = (guchar *) &_4_to_8_bitmap_rev; + + for (j = 0; j < 256; j++) + { + for (i = 1; i >= 0; i--) + { + gint idx; + + idx = ((j & (15 << (4*i))) >> (4*i)); + *(dest++) = _to_8_bitmap[idx]; + } + } + } + + filled4 = TRUE; +} + +static void +convert_bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height) +{ + gint64 x = width * height; + + while (x >= 8) + { + memcpy (dest, bit2byte + *src * 8, 8); + dest += 8; + x -= 8; + src++; + } + + if (x > 0) + { + memcpy (dest, bit2byte + *src * 8, x); + dest += x; + src++; + } +} + +static void +convert_2bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height) +{ + gint64 x = width * height; + + while (x >= 4) + { + memcpy (dest, _2bit2byte + *src * 4, 4); + dest += 4; + x -= 4; + src++; + } + + if (x > 0) + { + memcpy (dest, _2bit2byte + *src * 4, x); + dest += x; + src++; + } +} + +static void +convert_4bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height) +{ + gint64 x = width * height; + + while (x >= 2) + { + memcpy (dest, _4bit2byte + *src * 2, 2); + dest += 2; + x -= 2; + src++; + } + + if (x > 0) + { + memcpy (dest, _4bit2byte + *src * 2, x); + dest += x; + src++; + } +} + +static void +convert_miniswhite (guchar *buffer, + gint width, + gint height) +{ + gint y; + guchar *buf = buffer; + + for (y = 0; y < height; y++) + { + gint x; + + for (x = 0; x < width; x++) + { + *buf = ~*buf; + buf++; + } + } +} + +static void +convert_int2uint (guchar *buffer, + gint bps, + gint spp, + gint width, + gint height, + gint stride) +{ + gint bytes_per_pixel = bps / 8; + gint y; + + for (y = 0; y < height; y++) + { + guchar *d = buffer + stride * y; + gint x; + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + d += bytes_per_pixel - 1; +#endif + + for (x = 0; x < width * spp; x++) + { + *d ^= 0x80; + + d += bytes_per_pixel; + } + } +} + +static gboolean +load_dialog (const gchar *help_id, + TiffSelectedPages *pages, + const gchar *extra_message, + DefaultExtra *default_extra) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *show_reduced = NULL; + GtkWidget *crop_option = NULL; + GtkWidget *extra_radio = NULL; + gboolean run; + + pages->selector = NULL; + + dialog = gimp_dialog_new (_("Import from TIFF"), PLUG_IN_ROLE, + NULL, 0, + gimp_standard_help_func, help_id, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Import"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gimp_window_set_transient (GTK_WINDOW (dialog)); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + vbox, TRUE, TRUE, 0); + + show_reduced = gtk_check_button_new_with_mnemonic (_("_Show reduced images")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_reduced), + pages->show_reduced); + gtk_box_pack_start (GTK_BOX (vbox), show_reduced, TRUE, TRUE, 0); + + g_signal_connect (show_reduced, "toggled", + G_CALLBACK (tiff_dialog_show_reduced), + pages); + + if (pages->n_pages > 1) + { + /* Page Selector */ + pages->selector = gimp_page_selector_new (); + gtk_widget_set_size_request (pages->selector, 300, 200); + gtk_box_pack_start (GTK_BOX (vbox), pages->selector, TRUE, TRUE, 0); + + gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector), + pages->n_filtered_pages); + gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (pages->selector), + pages->target); + + /* Load a set number of pages, based on whether "Show Reduced Images" + * is checked + */ + tiff_dialog_show_reduced (show_reduced, pages); + + g_signal_connect_swapped (pages->selector, "activate", + G_CALLBACK (gtk_window_activate_default), + dialog); + + /* Option to shrink the loaded image to its bounding box + or keep as much empty space as possible. + Note that there seems to be no way to keep the empty + space on the right and bottom. */ + crop_option = gtk_check_button_new_with_mnemonic (_("_Keep empty space around imported layers")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (crop_option), + pages->keep_empty_space); + gtk_box_pack_start (GTK_BOX (vbox), crop_option, TRUE, TRUE, 0); + } + + if (extra_message) + { + GtkWidget *warning; + + warning = g_object_new (GIMP_TYPE_HINT_BOX, + "icon-name", GIMP_ICON_DIALOG_WARNING, + "hint", extra_message, + NULL); + gtk_box_pack_start (GTK_BOX (vbox), warning, TRUE, TRUE, 0); + gtk_widget_show (warning); + + extra_radio = gimp_int_radio_group_new (TRUE, _("Process extra channel as:"), + (GCallback) gimp_radio_button_update, + default_extra, GIMP_TIFF_LOAD_UNASSALPHA, + _("_Non-premultiplied alpha"), GIMP_TIFF_LOAD_UNASSALPHA, NULL, + _("Pre_multiplied alpha"), GIMP_TIFF_LOAD_ASSOCALPHA, NULL, + _("Channe_l"), GIMP_TIFF_LOAD_CHANNEL, NULL, + NULL); + gtk_box_pack_start (GTK_BOX (vbox), extra_radio, TRUE, TRUE, 0); + gtk_widget_show (extra_radio); + } + + /* Setup done; display the dialog */ + gtk_widget_show_all (dialog); + + /* run the dialog */ + run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK); + + if (run) + { + if (pages->n_pages > 1) + { + pages->target = + gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (pages->selector)); + + pages->pages = + gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector), + &pages->n_pages); + + pages->keep_empty_space = + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crop_option)); + + /* select all if none selected */ + if (pages->n_pages == 0) + { + gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (pages->selector)); + + pages->pages = + gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector), + &pages->n_pages); + } + } + } + + return run; +} + +static void +tiff_dialog_show_reduced (GtkWidget *toggle, + gpointer data) +{ + gint selectable_pages; + gint i, j; + TiffSelectedPages *pages = (TiffSelectedPages *) data; + + pages->show_reduced = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)); + + /* Clear current pages from selection */ + gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector), 0); + /* Jump back to start of the TIFF file */ + TIFFSetDirectory (pages->tif, 0); + + selectable_pages = pages->n_filtered_pages; + if (pages->show_reduced) + selectable_pages = pages->n_reducedimage_pages; + + gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector), + selectable_pages); + + for (i = 0, j = 0; i < pages->n_pages && j < selectable_pages; i++) + { + if ((pages->show_reduced && pages->filtered_pages[i] != TIFF_MISC_THUMBNAIL) || + (! pages->show_reduced && pages->filtered_pages[i] > TIFF_MISC_THUMBNAIL)) + { + const gchar *name = tiff_get_page_name (pages->tif); + + if (name) + gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (pages->selector), + j, name); + j++; + } + + TIFFReadDirectory (pages->tif); + } +} |