/* 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 #include #include #include #include #include #include #include "file-tiff.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 { GimpDrawable *drawable; 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_sketchbook_layers (TIFF *tif, GimpImage *image); 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 (GimpProcedure *procedure, GimpProcedureConfig *config, TiffSelectedPages *pages, const gchar *extra_message, DefaultExtra *default_extra); static void tiff_dialog_show_reduced (GtkWidget *toggle, gpointer data); /* 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 (GimpProcedure *procedure, GFile *file, GimpRunMode run_mode, GimpImage **image, gboolean *resolution_loaded, gboolean *profile_loaded, gboolean *ps_metadata_loaded, GimpProcedureConfig *config, 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; gboolean save_transp_pixels = FALSE; GimpColorProfile *first_profile = NULL; const gchar *extra_message = NULL; gint li; gint selectable_pages; gchar *photoshop_data; gint32 photoshop_len; gboolean is_cmyk = FALSE; gchar *sketchbook_info; gint sketchbook_len; gboolean sketchbook_layers = FALSE; *image = NULL; 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; } g_object_get (config, "target", &pages.target, NULL); g_object_get (config, "keep-empty-space", &pages.keep_empty_space, NULL); 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); /* Check if there exist layers saved from Alias/AutoDesk Sketchbook */ sketchbook_layers = TIFFGetField (tif, TIFFTAG_ALIAS_LAYER_METADATA, &sketchbook_info, &sketchbook_len); 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 (procedure, config, &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++; } } } g_object_set (config, "target", pages.target, NULL); g_object_set (config, "keep-empty-space", pages.keep_empty_space, NULL); /* 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; GimpLayer *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_t planar = PLANARCONFIG_CONTIG; TiffColorMode tiff_mode; gboolean is_signed; gint i; gboolean worst_case = FALSE; gint gimp_compression = GIMP_COMPRESSION_NONE; 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_NON_LINEAR; 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_NON_LINEAR; type = babl_type ("half"); } else { if (profile_linear) image_precision = GIMP_PRECISION_U16_LINEAR; else image_precision = GIMP_PRECISION_U16_NON_LINEAR; 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_NON_LINEAR; type = babl_type ("float"); } else { if (profile_linear) image_precision = GIMP_PRECISION_U32_LINEAR; else image_precision = GIMP_PRECISION_U32_NON_LINEAR; type = babl_type ("u32"); } break; case 64: if (profile_linear) image_precision = GIMP_PRECISION_DOUBLE_LINEAR; else image_precision = GIMP_PRECISION_DOUBLE_NON_LINEAR; 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; save_transp_pixels = FALSE; extra--; } else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA)) { alpha = TRUE; 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; save_transp_pixels = FALSE; break; case GIMP_TIFF_LOAD_UNASSALPHA: alpha = TRUE; 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; save_transp_pixels = FALSE; break; case GIMP_TIFF_LOAD_UNASSALPHA: alpha = TRUE; 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 (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 (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 */ is_cmyk = TRUE; if (profile && gimp_color_profile_is_cmyk (profile)) { space = gimp_color_profile_get_space (profile, GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, error); } 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; } } gimp_compression = tiff_compression_to_gimp_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) { TIFFClose (tif); g_message (_("Could not create a new image: %s"), gimp_pdb_get_last_error (gimp_get_pdb ())); return GIMP_PDB_EXECUTION_ERROR; } gimp_image_undo_disable (*image); if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) images_list = g_list_prepend (images_list, *image); } /* attach CMYK profile to GimpImage if applicable */ if (profile && gimp_color_profile_is_cmyk (profile)) { gimp_image_set_simulation_profile (*image, profile); g_clear_object (&profile); } /* 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 */ { GString *string; GimpConfigWriter *writer; GimpParasite *parasite; const gchar *img_desc; /* construct the save parasite manually instead of simply * creating and saving a save config object, because we want * it to contain only some properties */ string = g_string_new (NULL); writer = gimp_config_writer_new_from_string (string); gimp_config_writer_open (writer, "compression"); gimp_config_writer_printf (writer, "%d", gimp_compression); gimp_config_writer_close (writer); gimp_config_writer_finish (writer, NULL, NULL); parasite = gimp_parasite_new ("GimpProcedureConfig-file-tiff-save-last", GIMP_PARASITE_PERSISTENT, string->len + 1, string->str); gimp_image_attach_parasite (*image, parasite); gimp_parasite_free (parasite); g_string_free (string, TRUE); /* 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_t 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? */ { gdouble xres = 72.0; gdouble yres = 72.0; gushort read_unit = RESUNIT_NONE; 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 (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; } /* Stop here if we have Alias/AutoDesk Sketchbook layers, as this * would just be the composite image */ if (sketchbook_layers) break; /* 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 (GIMP_DRAWABLE (layer)); } else if (! space) { base_format = babl_format_with_space (babl_format_get_encoding (base_format), gimp_drawable_get_format (GIMP_DRAWABLE (layer))); } channel[0].drawable = GIMP_DRAWABLE (layer); channel[0].buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); channel[0].format = base_format; if (extra > 0 && ! worst_case) { /* Add extra channels as appropriate */ for (i = 1; i <= extra; i++) { GeglColor *color = gegl_color_new ("black"); channel[i].drawable = GIMP_DRAWABLE (gimp_channel_new (*image, _("TIFF Channel"), cols, rows, 100.0, color)); g_object_unref (color); gimp_image_insert_channel (*image, GIMP_CHANNEL (channel[i].drawable), NULL, 0); channel[i].buffer = gimp_drawable_get_buffer (channel[i].drawable); /* Unlike color channels, we don't care about the source * TRC for extra channels. We just want to import them * as-is without any value conversion. Since extra * channels are always linear in GIMP, we consider TIFF * extra channels with unspecified data to be linear too. * Only exception are 8-bit non-linear images whose * channel are Y' for legacy/compatibility reasons. */ if (image_precision == GIMP_PRECISION_U8_NON_LINEAR) channel[i].format = babl_format_new (babl_model ("Y'"), type, babl_component ("Y'"), NULL); else 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 (GIMP_ITEM (layer), GIMP_ORIENTATION_HORIZONTAL, TRUE /* auto_center */, -1.0 /* axis */); if (flip_vertical) gimp_item_transform_flip_simple (GIMP_ITEM (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, NULL, -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 this TIF was created in Alias/AutoDesk Sketchbook, it may have layers. */ if (sketchbook_layers) load_sketchbook_layers (tif, *image); g_clear_object (&first_profile); if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) { GList *list = images_list; if (list) { *image = list->data; list = g_list_next (list); } for (; list; list = g_list_next (list)) { gimp_display_new (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 */ if (! sketchbook_layers) gimp_image_resize (*image, max_col - min_col, max_row - min_row, -min_col, -min_row); gimp_image_undo_enable (*image); } /* Load Photoshop layer metadata */ if (TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &photoshop_len, &photoshop_data)) { FILE *fp; GFile *temp_file = NULL; GimpProcedure *procedure; GimpValueArray *return_vals = NULL; temp_file = gimp_temp_file ("tmp"); fp = g_fopen (g_file_peek_path (temp_file), "wb"); if (! fp) { g_message (_("Error trying to open temporary %s file '%s' " "for tiff metadata loading: %s"), "tmp", gimp_file_get_utf8_name (temp_file), g_strerror (errno)); } fwrite (photoshop_data, sizeof (guchar), photoshop_len, fp); fclose (fp); procedure = gimp_pdb_lookup_procedure (gimp_get_pdb (), "file-psd-load-metadata"); return_vals = gimp_procedure_run (procedure, "run-mode", GIMP_RUN_NONINTERACTIVE, "file", temp_file, "size", photoshop_len, "image", *image, "metadata-type", FALSE, NULL); g_file_delete (temp_file, NULL, NULL); g_object_unref (temp_file); gimp_value_array_unref (return_vals); *ps_metadata_loaded = TRUE; } if (TIFFGetField (tif, TIFFTAG_IMAGESOURCEDATA, &photoshop_len, &photoshop_data)) { FILE *fp; GFile *temp_file = NULL; GimpProcedure *procedure; GimpValueArray *return_vals = NULL; /* Photoshop metadata starts with 'Adobe Photoshop Document Data Block' * so we need to skip past that for the data. */ photoshop_data += 36; photoshop_len -= 36; temp_file = gimp_temp_file ("tmp"); fp = g_fopen (g_file_peek_path (temp_file), "wb"); if (! fp) { g_message (_("Error trying to open temporary %s file '%s' " "for tiff metadata loading: %s"), "tmp", gimp_file_get_utf8_name (temp_file), g_strerror (errno)); } fwrite (photoshop_data, sizeof (guchar), photoshop_len, fp); fclose (fp); procedure = gimp_pdb_lookup_procedure (gimp_get_pdb (), "file-psd-load-metadata"); /* We would like to use run_mode below. That way we could show a dialog * when unsupported Photoshop data is detected in interactive mode. * However, in interactive mode saved config values take precedence over * these values set below, so that won't work. */ return_vals = gimp_procedure_run (procedure, "run-mode", GIMP_RUN_NONINTERACTIVE, "file", temp_file, "size", photoshop_len, "image", *image, "metadata-type", TRUE, "cmyk", is_cmyk, NULL); g_file_delete (temp_file, NULL, NULL); g_object_unref (temp_file); gimp_value_array_unref (return_vals); *ps_metadata_loaded = TRUE; } 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_t 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_t, 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_FROM_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_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); } /* Loads layers stored by the Alias/AutoDesk Sketchbook program */ static void load_sketchbook_layers (TIFF *tif, GimpImage *image) { gchar *alias_layer_info; gint alias_data_len; guint32 image_height = gimp_image_get_height (image); guint32 image_width = gimp_image_get_width (image); GeglColor *fill_color; GeglColor *foreground_color; GimpLayer *background_layer; GimpLayerMode default_mode; const Babl *format = NULL; gchar **image_settings; gchar *hex_color; gint layer_count = 0; gint sub_len; void *ptr; gchar *endptr = NULL; default_mode = gimp_image_get_default_new_layer_mode (image); TIFFSetDirectory (tif, 0); TIFFGetField (tif, TIFFTAG_ALIAS_LAYER_METADATA, &alias_data_len, &alias_layer_info); if (! g_utf8_validate (alias_layer_info, -1, NULL)) return; /* Create background layer. Fill it with the hex color from * the image-level ALIAS_LAYER_METADATA tag. The hex color * is in AGBR format so we need to reverse it */ image_settings = g_strsplit (alias_layer_info, ", ", 15); if (image_settings[0] != NULL) layer_count = g_ascii_strtoll (image_settings[0], &endptr, 10); if (image_settings[2] != NULL && strlen (image_settings[2]) >= 8) hex_color = g_strdup_printf ("#%s%s%s%s", g_utf8_substring (image_settings[2], 6, 8), g_utf8_substring (image_settings[2], 4, 6), g_utf8_substring (image_settings[2], 2, 4), g_utf8_substring (image_settings[2], 0, 2)); else hex_color = g_strdup ("transparent"); fill_color = gegl_color_new (hex_color); g_free (hex_color); foreground_color = gegl_color_duplicate (gimp_context_get_foreground ()); gimp_context_set_foreground (fill_color); background_layer = gimp_layer_new (image, _("Background"), image_width, image_height, GIMP_RGBA_IMAGE, 100, default_mode); gimp_image_insert_layer (image, background_layer, NULL, -1); gimp_drawable_fill (GIMP_DRAWABLE (background_layer), GIMP_FILL_FOREGROUND); g_object_unref (fill_color); gimp_context_set_foreground (foreground_color); g_object_unref (foreground_color); /* The layers are stored in BGRA format */ format = babl_format_new (babl_model ("R~G~B~A"), babl_type ("u8"), babl_component ("B~"), babl_component ("G~"), babl_component ("R~"), babl_component ("A"), NULL); /* Layers are stored in SubIFDs of the first directory */ if (TIFFGetField (tif, TIFFTAG_SUBIFD, &sub_len, &ptr)) { toff_t offsets[sub_len]; gint count = 0; memcpy (offsets, ptr, sub_len * sizeof (offsets[0])); for (gint i = 0; i < sub_len; i++) { gchar *alias_sublayer_info = NULL; gint32 alias_sublayer_len = 0; if (! TIFFSetSubDirectory (tif, offsets[i])) break; if (TIFFGetField (tif, TIFFTAG_ALIAS_LAYER_METADATA, &alias_sublayer_len, &alias_sublayer_info) && g_utf8_validate (alias_sublayer_info, -1, NULL)) { gchar **layer_settings; GimpProcedure *procedure; GimpLayer *layer; GeglBuffer *buffer; const gchar *layer_name; guint32 layer_width = 0; guint32 layer_height = 0; gfloat x_pos = 0; gfloat y_pos = 0; gfloat opacity = 100; gboolean visible = TRUE; gboolean locked = FALSE; guint32 *pixels; guint32 row; layer_settings = g_strsplit (alias_sublayer_info, ", ", 10); if (layer_settings[0] != NULL) { opacity = (gfloat) g_ascii_strtod (layer_settings[0], &endptr); opacity *= 100.0f; } if (layer_settings[2] != NULL) visible = g_ascii_strtoll (layer_settings[2], &endptr, 10); if (layer_settings[3] != NULL) locked = g_ascii_strtoll (layer_settings[3], &endptr, 10); /* Additional tags in SubIFD */ layer_name = tiff_get_page_name (tif); TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &layer_width); TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &layer_height); if (! TIFFGetField (tif, TIFFTAG_XPOSITION, &x_pos)) x_pos = 0.0f; if (! TIFFGetField (tif, TIFFTAG_YPOSITION, &y_pos)) y_pos = 0.0f; layer = gimp_layer_new (image, layer_name, layer_width, layer_height, GIMP_RGBA_IMAGE, opacity, default_mode); gimp_image_insert_layer (image, layer, NULL, -1); /* Loading pixel data */ pixels = g_new (uint32_t, layer_width * layer_height); if (! TIFFReadRGBAImage (tif, layer_width, layer_height, pixels, 0)) { g_free (pixels); continue; } buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); for (row = 0; row < layer_height; row++) { #if G_BYTE_ORDER != G_LITTLE_ENDIAN guint32 row_start = row * layer_width; guint32 row_end = row_start + layer_width; guint32 i; /* Make sure our channels are in the right order */ for (i = row_start; i < row_end; i++) pixels[i] = GUINT32_FROM_LE (pixels[i]); #endif gegl_buffer_set (buffer, GEGL_RECTANGLE (0, layer_height - row - 1, layer_width, 1), 0, format, ((guchar *) pixels) + row * layer_width * 4, GEGL_AUTO_ROWSTRIDE); } g_object_unref (buffer); g_free (pixels); /* The layers seem to have excessive padding that affects the * offset, since it's calculated from the bottom-left corner * of the layer. We can crop the layers to fix the y position * offset. Since the layer width can also shrink due to the * crop, we calculate the before and after difference and * adjust the x offset too. */ procedure = gimp_pdb_lookup_procedure (gimp_get_pdb (), "plug-in-autocrop-layer"); gimp_procedure_run (procedure, "run-mode", GIMP_RUN_NONINTERACTIVE, "image", image, "drawable", layer, NULL); x_pos += (layer_width - gimp_drawable_get_width (GIMP_DRAWABLE (layer))); y_pos = image_height - gimp_drawable_get_height (GIMP_DRAWABLE (layer)) - y_pos; gimp_layer_set_offsets (layer, ROUND (x_pos), ROUND (y_pos)); /* In Alias/Autodesk Sketchbook, the layers are the same size as the canvas */ gimp_layer_resize_to_image_size (layer); gimp_item_set_visible (GIMP_ITEM (layer), visible); /* Set locks after copying pixel data over */ gimp_item_set_lock_content (GIMP_ITEM (layer), locked); gimp_layer_set_lock_alpha (layer, locked); count++; gimp_progress_update ((gdouble) count / (gdouble) layer_count); } } } } 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 (GimpProcedure *procedure, GimpProcedureConfig *config, TiffSelectedPages *pages, const gchar *extra_message, DefaultExtra *default_extra) { GtkWidget *dialog; GtkWidget *vbox; GtkWidget *toggle = NULL; GtkWidget *extra_radio = NULL; gboolean run; pages->selector = NULL; dialog = gimp_procedure_dialog_new (procedure, GIMP_PROCEDURE_CONFIG (config), _("Import from TIFF")); vbox = gimp_procedure_dialog_fill_box (GIMP_PROCEDURE_DIALOG (dialog), "tiff-vbox", "keep-empty-space", NULL); gimp_procedure_dialog_fill (GIMP_PROCEDURE_DIALOG (dialog), "tiff-vbox", NULL); toggle = gtk_check_button_new_with_mnemonic (_("_Show reduced images")); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pages->show_reduced); gtk_widget_set_margin_bottom (toggle, 6); gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); gtk_widget_set_visible (toggle, TRUE); g_signal_connect (toggle, "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); gtk_widget_set_visible (pages->selector, TRUE); 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 (toggle, pages); g_signal_connect_swapped (pages->selector, "activate", G_CALLBACK (gtk_window_activate_default), dialog); } 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_set_visible (warning, TRUE); extra_radio = gimp_int_radio_group_new (TRUE, _("Process extra channel as:"), (GCallback) gimp_radio_button_update, default_extra, NULL, 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_set_visible (extra_radio, TRUE); } toggle = gimp_procedure_dialog_get_widget (GIMP_PROCEDURE_DIALOG (dialog), "keep-empty-space", G_TYPE_NONE); gtk_widget_set_margin_top (toggle, 6); gtk_widget_set_margin_bottom (toggle, 6); gtk_box_reorder_child (GTK_BOX (vbox), toggle, -1); /* Setup done; display the dialog */ gtk_widget_set_visible (dialog, TRUE); /* run the dialog */ run = gimp_procedure_dialog_run (GIMP_PROCEDURE_DIALOG (dialog)); if (run) { if (pages->n_pages > 1) { g_object_get (config, "keep-empty-space", &pages->keep_empty_space, NULL); 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); /* 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); } }