gimp/plug-ins/file-tiff/file-tiff-load.c

2918 lines
98 KiB
C

/* 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 <gio/gio.h>
#include <glib/gstdio.h>
#include <tiffio.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
#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);
}
}