diff options
Diffstat (limited to 'plug-ins/file-tiff/file-tiff-load.c')
-rw-r--r-- | plug-ins/file-tiff/file-tiff-load.c | 2018 |
1 files changed, 2018 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..03ebca0 --- /dev/null +++ b/plug-ins/file-tiff/file-tiff-load.c @@ -0,0 +1,2018 @@ +/* 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; + +/* 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, + gboolean is_bw, + gboolean is_signed, + gint extra); +static void load_separate (TIFF *tif, + ChannelData *channel, + const Babl *type, + gushort bps, + gushort spp, + gboolean is_bw, + gboolean is_signed, + gint extra); +static void load_paths (TIFF *tif, + gint image, + gint width, + gint height, + gint offset_x, + gint offset_y); + +static void fill_bit2byte (void); +static void convert_bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height); + +static void convert_int2uint (guchar *buffer, + gint bps, + gint spp, + gint width, + gint height, + gint stride); + +static gboolean load_dialog (TIFF *tif, + const gchar *help_id, + TiffSelectedPages *pages, + const gchar *extra_message, + DefaultExtra *default_extra); + + +static TiffSaveVals tsvals = +{ + COMPRESSION_NONE, /* compression */ + TRUE, /* alpha handling */ +}; + + +/* 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; +} + +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; + gint li; + + *image = 0; + gimp_progress_init_printf (_("Opening '%s'"), + gimp_file_get_utf8_name (file)); + + tif = tiff_open (file, "r", error); + if (! tif) + 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) + { + 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; + } + + pages.pages = NULL; + if (run_mode != GIMP_RUN_INTERACTIVE) + { + pages.pages = g_new (gint, pages.n_pages); + + for (li = 0; li < pages.n_pages; li++) + pages.pages[li] = li; + } + else + { + const gchar *extra_message = NULL; + + if (pages.n_pages == 1) + { + pages.pages = g_new0 (gint, pages.n_pages); + 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; + + TIFFSetDirectory (tif, li); + 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; + + /* 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 && + ((photomet == PHOTOMETRIC_RGB && spp > 3) || + /* All other color space expect 1 channel (grayscale, + * palette, mask). */ + (photomet != PHOTOMETRIC_RGB && spp > 1))) + { + /* 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."); + break; + } + } + TIFFSetDirectory (tif, 0); + + if ((pages.n_pages > 1 || extra_message) && + ! load_dialog (tif, LOAD_PROC, &pages, + extra_message, &default_extra)) + { + TIFFClose (tif); + g_clear_pointer (&pages.pages, g_free); + + return GIMP_PDB_CANCEL; + } + } + + 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; + 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; + gboolean is_bw; + gboolean is_signed; + gint i; + gboolean worst_case = FALSE; + TiffSaveVals save_vals; + const gchar *name; + + TIFFSetDirectory (tif, pages.pages[li]); + 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) + { + if (! *image) + *profile_loaded = TRUE; + + profile_linear = gimp_color_profile_is_linear (profile); + } + + if (bps > 8 && bps != 8 && bps != 16 && bps != 32 && bps != 64) + worst_case = TRUE; /* Wrong sample width => RGBA */ + + switch (bps) + { + case 1: + 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: + if (profile_linear) + image_precision = GIMP_PRECISION_U16_LINEAR; + else + image_precision = GIMP_PRECISION_U16_GAMMA; + + type = babl_type ("u16"); + } + + 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 (! 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 file %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 ((photomet == PHOTOMETRIC_RGB && spp > 3) || + (photomet != PHOTOMETRIC_RGB && spp > 1)) + { + if (run_mode != GIMP_RUN_INTERACTIVE) + g_message ("File '%s' does not conform to TIFF specification: " + "ExtraSamples field is not set while extra channels are present. " + "Assuming the first extra channel as 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; + } + } + + if (photomet == PHOTOMETRIC_RGB && spp > 3 + (alpha ? 1 : 0) + extra) + extra = spp - 3 - (alpha ? 1 : 0); + else if (photomet != PHOTOMETRIC_RGB && spp > 1 + (alpha ? 1 : 0) + extra) + extra = spp - 1 - (alpha ? 1 : 0); + + is_bw = FALSE; + is_signed = sampleformat == SAMPLEFORMAT_INT; + + switch (photomet) + { + case PHOTOMETRIC_MINISBLACK: + case PHOTOMETRIC_MINISWHITE: + if (bps == 1 && ! alpha && spp == 1) + { + image_type = GIMP_INDEXED; + layer_type = GIMP_INDEXED_IMAGE; + + is_bw = TRUE; + fill_bit2byte (); + } + else + { + image_type = GIMP_GRAY; + layer_type = alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE; + + 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_PALETTE: + image_type = GIMP_INDEXED; + layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE; + 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: + 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 color profile */ + + if (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); + } + } + + /* 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 ("File error: 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 " + "any units tag, 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) + { + 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 (is_bw) + { + if (photomet == PHOTOMETRIC_MINISWHITE) + { + cmap[0] = cmap[1] = cmap[2] = 255; + cmap[3] = cmap[4] = cmap[5] = 0; + } + else + { + cmap[0] = cmap[1] = cmap[2] = 0; + cmap[3] = cmap[4] = cmap[5] = 255; + } + } + else + { + 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; + } + } + + 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); + + /* 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); + } + + 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, + is_bw, is_signed, extra); + } + else + { + load_separate (tif, channel, type, bps, spp, + is_bw, 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); + } + + 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 (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 ("Unsupported layout, no RGBA loader"); + + 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, + gboolean is_bw, + 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; + + 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 (is_bw) + 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)) + TIFFReadTile (tif, buffer, x, y, 0, 0); + else + TIFFReadScanline (tif, buffer, y, 0); + + cols = MIN (image_width - x, tile_width); + rows = MIN (image_height - y, tile_height); + + if (is_bw) + { + convert_bit2byte (buffer, bw_buffer, cols, rows); + } + else if (is_signed) + { + convert_int2uint (buffer, bps, spp, cols, rows, + tile_width * bytes_per_pixel); + } + + src_buf = gegl_buffer_linear_new_from_data (is_bw ? 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, + gboolean is_bw, + 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; + + 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 (is_bw) + 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)) + TIFFReadTile (tif, buffer, x, y, 0, compindex); + else + TIFFReadScanline (tif, buffer, y, compindex); + + cols = MIN (image_width - x, tile_width); + rows = MIN (image_height - y, tile_height); + + if (is_bw) + { + convert_bit2byte (buffer, bw_buffer, cols, rows); + } + else if (is_signed) + { + convert_int2uint (buffer, bps, 1, cols, rows, + tile_width * bytes_per_pixel); + } + + src_buf = gegl_buffer_linear_new_from_data (is_bw ? 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 guchar bit2byte[256 * 8]; + +static void +fill_bit2byte (void) +{ + static gboolean filled = FALSE; + + guchar *dest; + gint i, j; + + if (filled) + return; + + dest = bit2byte; + + for (j = 0; j < 256; j++) + for (i = 7; i >= 0; i--) + *(dest++) = ((j & (1 << i)) != 0); + + filled = TRUE; +} + +static void +convert_bit2byte (const guchar *src, + guchar *dest, + gint width, + gint height) +{ + gint y; + + for (y = 0; y < height; y++) + { + gint x = width; + + 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_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 (TIFF *tif, + const gchar *help_id, + TiffSelectedPages *pages, + const gchar *extra_message, + DefaultExtra *default_extra) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *selector = NULL; + GtkWidget *crop_option = NULL; + GtkWidget *extra_radio = NULL; + gboolean run; + + 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); + + if (pages->n_pages > 1) + { + gint i; + + /* Page Selector */ + selector = gimp_page_selector_new (); + gtk_widget_set_size_request (selector, 300, 200); + gtk_box_pack_start (GTK_BOX (vbox), selector, TRUE, TRUE, 0); + + gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector), + pages->n_pages); + gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector), pages->target); + + for (i = 0; i < pages->n_pages; i++) + { + const gchar *name = tiff_get_page_name (tif); + + if (name) + gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (selector), + i, name); + + TIFFReadDirectory (tif); + } + + g_signal_connect_swapped (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 (selector)); + + pages->pages = + gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (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 (selector)); + + pages->pages = + gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (selector), + &pages->n_pages); + } + } + } + + return run; +} |