mirror of https://github.com/GNOME/gimp.git
1982 lines
65 KiB
C
1982 lines
65 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 <tiffio.h>
|
|
|
|
#include <libgimp/gimp.h>
|
|
#include <libgimp/gimpui.h>
|
|
|
|
#include "file-tiff-io.h"
|
|
#include "file-tiff-load.h"
|
|
|
|
#include "libgimp/stdplugins-intl.h"
|
|
|
|
|
|
#define PLUG_IN_ROLE "gimp-file-tiff-load"
|
|
|
|
|
|
typedef struct
|
|
{
|
|
gint compression;
|
|
gint fillorder;
|
|
gboolean save_transp_pixels;
|
|
} TiffSaveVals;
|
|
|
|
typedef struct
|
|
{
|
|
gint32 ID;
|
|
GeglBuffer *buffer;
|
|
const Babl *format;
|
|
guchar *pixels;
|
|
guchar *pixel;
|
|
} ChannelData;
|
|
|
|
typedef enum
|
|
{
|
|
GIMP_TIFF_LOAD_ASSOCALPHA,
|
|
GIMP_TIFF_LOAD_UNASSALPHA,
|
|
GIMP_TIFF_LOAD_CHANNEL
|
|
} DefaultExtra;
|
|
|
|
/* Declare some local functions */
|
|
|
|
static GimpColorProfile * load_profile (TIFF *tif);
|
|
|
|
static void load_rgba (TIFF *tif,
|
|
ChannelData *channel);
|
|
static void load_contiguous (TIFF *tif,
|
|
ChannelData *channel,
|
|
const Babl *type,
|
|
gushort bps,
|
|
gushort spp,
|
|
gboolean is_bw,
|
|
gint extra);
|
|
static void load_separate (TIFF *tif,
|
|
ChannelData *channel,
|
|
const Babl *type,
|
|
gushort bps,
|
|
gushort spp,
|
|
gboolean is_bw,
|
|
gint extra);
|
|
static void load_paths (TIFF *tif,
|
|
gint image,
|
|
gint width,
|
|
gint height,
|
|
gint offset_x,
|
|
gint offset_y);
|
|
|
|
static void fill_bit2byte (void);
|
|
static void convert_bit2byte (const guchar *src,
|
|
guchar *dest,
|
|
gint width,
|
|
gint height);
|
|
|
|
static gboolean load_dialog (TIFF *tif,
|
|
const gchar *help_id,
|
|
TiffSelectedPages *pages,
|
|
const gchar *extra_message,
|
|
DefaultExtra *default_extra);
|
|
|
|
|
|
static TiffSaveVals tsvals =
|
|
{
|
|
COMPRESSION_NONE, /* compression */
|
|
TRUE, /* alpha handling */
|
|
};
|
|
|
|
|
|
/* returns a pointer into the TIFF */
|
|
static const gchar *
|
|
tiff_get_page_name (TIFF *tif)
|
|
{
|
|
static gchar *name;
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_PAGENAME, &name) &&
|
|
g_utf8_validate (name, -1, NULL))
|
|
{
|
|
return name;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GimpPDBStatusType
|
|
load_image (GFile *file,
|
|
GimpRunMode run_mode,
|
|
gint32 *image,
|
|
gboolean *resolution_loaded,
|
|
gboolean *profile_loaded,
|
|
GError **error)
|
|
{
|
|
TIFF *tif;
|
|
TiffSelectedPages pages;
|
|
|
|
GList *images_list = NULL;
|
|
DefaultExtra default_extra = GIMP_TIFF_LOAD_UNASSALPHA;
|
|
gint first_image_type = GIMP_RGB;
|
|
gint min_row = G_MAXINT;
|
|
gint min_col = G_MAXINT;
|
|
gint max_row = 0;
|
|
gint max_col = 0;
|
|
gint li;
|
|
|
|
*image = 0;
|
|
gimp_progress_init_printf (_("Opening '%s'"),
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
tif = tiff_open (file, "r", error);
|
|
if (! tif)
|
|
return GIMP_PDB_EXECUTION_ERROR;
|
|
|
|
pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
|
|
gimp_get_data (LOAD_PROC "-target", &pages.target);
|
|
|
|
pages.keep_empty_space = TRUE;
|
|
gimp_get_data (LOAD_PROC "-keep-empty-space",
|
|
&pages.keep_empty_space);
|
|
|
|
pages.n_pages = pages.o_pages = TIFFNumberOfDirectories (tif);
|
|
if (pages.n_pages == 0)
|
|
{
|
|
TIFFClose (tif);
|
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
|
_("TIFF '%s' does not contain any directories"),
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
return GIMP_PDB_EXECUTION_ERROR;
|
|
}
|
|
|
|
pages.pages = NULL;
|
|
if (run_mode != GIMP_RUN_INTERACTIVE)
|
|
{
|
|
pages.pages = g_new (gint, pages.n_pages);
|
|
|
|
for (li = 0; li < pages.n_pages; li++)
|
|
pages.pages[li] = li;
|
|
}
|
|
else
|
|
{
|
|
const gchar *extra_message = NULL;
|
|
|
|
if (pages.n_pages == 1)
|
|
{
|
|
pages.pages = g_new0 (gint, pages.n_pages);
|
|
pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
|
|
}
|
|
|
|
/* Check all pages if any has an unspecified or unset channel. */
|
|
for (li = 0; li < pages.n_pages; li++)
|
|
{
|
|
gushort spp;
|
|
gushort photomet;
|
|
gushort extra;
|
|
gushort *extra_types;
|
|
|
|
TIFFSetDirectory (tif, li);
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
|
|
if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
|
|
{
|
|
guint16 compression;
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
|
|
(compression == COMPRESSION_CCITTFAX3 ||
|
|
compression == COMPRESSION_CCITTFAX4 ||
|
|
compression == COMPRESSION_CCITTRLE ||
|
|
compression == COMPRESSION_CCITTRLEW))
|
|
{
|
|
photomet = PHOTOMETRIC_MINISWHITE;
|
|
}
|
|
else
|
|
{
|
|
/* old AppleScan software misses out the photometric tag
|
|
* (and incidentally assumes min-is-white, but xv
|
|
* assumes min-is-black, so we follow xv's lead. It's
|
|
* not much hardship to invert the image later).
|
|
*/
|
|
photomet = PHOTOMETRIC_MINISBLACK;
|
|
}
|
|
}
|
|
if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
|
extra = 0;
|
|
|
|
/* TODO: current code always assumes that the alpha channel
|
|
* will be the first extra channel, though the TIFF spec does
|
|
* not mandate such assumption. A future improvement should be
|
|
* to actually loop through the extra channels and save the
|
|
* alpha channel index.
|
|
* Of course, this is an edge case, as most image would likely
|
|
* have only a single extra channel anyway. But still we could
|
|
* be more accurate.
|
|
*/
|
|
if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
|
|
{
|
|
extra_message = _("Extra channels with unspecified data.");
|
|
break;
|
|
}
|
|
else if (extra == 0 &&
|
|
((photomet == PHOTOMETRIC_RGB && spp > 3) ||
|
|
/* All other color space expect 1 channel (grayscale,
|
|
* palette, mask). */
|
|
(photomet != PHOTOMETRIC_RGB && spp > 1)))
|
|
{
|
|
/* ExtraSamples field not set, yet we have more channels than
|
|
* the PhotometricInterpretation field suggests.
|
|
* This should not happen as the spec clearly says "This field
|
|
* must be present if there are extra samples". So the files
|
|
* can be considered non-conformant.
|
|
* Let's ask what to do with the channel.
|
|
*/
|
|
extra_message = _("Non-conformant TIFF: extra channels without 'ExtraSamples' field.");
|
|
break;
|
|
}
|
|
}
|
|
TIFFSetDirectory (tif, 0);
|
|
|
|
if ((pages.n_pages > 1 || extra_message) &&
|
|
! load_dialog (tif, LOAD_PROC, &pages,
|
|
extra_message, &default_extra))
|
|
{
|
|
TIFFClose (tif);
|
|
g_clear_pointer (&pages.pages, g_free);
|
|
|
|
return GIMP_PDB_CANCEL;
|
|
}
|
|
}
|
|
|
|
gimp_set_data (LOAD_PROC "-target",
|
|
&pages.target, sizeof (pages.target));
|
|
gimp_set_data (LOAD_PROC "-keep-empty-space",
|
|
&pages.keep_empty_space,
|
|
sizeof (pages.keep_empty_space));
|
|
|
|
/* We will loop through the all pages in case of multipage TIFF
|
|
* and load every page as a separate layer.
|
|
*/
|
|
for (li = 0; li < pages.n_pages; li++)
|
|
{
|
|
gint ilayer;
|
|
gushort bps;
|
|
gushort spp;
|
|
gushort photomet;
|
|
gshort sampleformat;
|
|
GimpColorProfile *profile;
|
|
gboolean profile_linear = FALSE;
|
|
GimpPrecision image_precision;
|
|
const Babl *type;
|
|
const Babl *base_format = NULL;
|
|
guint16 orientation;
|
|
gint cols;
|
|
gint rows;
|
|
gboolean alpha;
|
|
gint image_type = GIMP_RGB;
|
|
gint layer;
|
|
gint layer_type = GIMP_RGB_IMAGE;
|
|
float layer_offset_x = 0.0;
|
|
float layer_offset_y = 0.0;
|
|
gint layer_offset_x_pixel = 0;
|
|
gint layer_offset_y_pixel = 0;
|
|
gushort extra;
|
|
gushort *extra_types;
|
|
ChannelData *channel = NULL;
|
|
uint16 planar = PLANARCONFIG_CONTIG;
|
|
gboolean is_bw;
|
|
gint i;
|
|
gboolean worst_case = FALSE;
|
|
TiffSaveVals save_vals;
|
|
const gchar *name;
|
|
|
|
TIFFSetDirectory (tif, pages.pages[li]);
|
|
ilayer = pages.pages[li];
|
|
|
|
gimp_progress_update (0.0);
|
|
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
|
|
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
|
|
|
|
profile = load_profile (tif);
|
|
if (profile)
|
|
{
|
|
if (! *image)
|
|
*profile_loaded = TRUE;
|
|
|
|
profile_linear = gimp_color_profile_is_linear (profile);
|
|
}
|
|
|
|
if (bps > 8 && bps != 8 && bps != 16 && bps != 32 && bps != 64)
|
|
worst_case = TRUE; /* Wrong sample width => RGBA */
|
|
|
|
switch (bps)
|
|
{
|
|
case 1:
|
|
case 8:
|
|
if (profile_linear)
|
|
image_precision = GIMP_PRECISION_U8_LINEAR;
|
|
else
|
|
image_precision = GIMP_PRECISION_U8_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:
|
|
if (profile_linear)
|
|
image_precision = GIMP_PRECISION_U16_LINEAR;
|
|
else
|
|
image_precision = GIMP_PRECISION_U16_NON_LINEAR;
|
|
|
|
type = babl_type ("u16");
|
|
}
|
|
|
|
g_printerr ("bps: %d\n", bps);
|
|
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
|
extra = 0;
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols))
|
|
{
|
|
TIFFClose (tif);
|
|
g_message ("Could not get image width from '%s'",
|
|
gimp_file_get_utf8_name (file));
|
|
return GIMP_PDB_EXECUTION_ERROR;
|
|
}
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows))
|
|
{
|
|
TIFFClose (tif);
|
|
g_message ("Could not get image length from '%s'",
|
|
gimp_file_get_utf8_name (file));
|
|
return GIMP_PDB_EXECUTION_ERROR;
|
|
}
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
|
|
{
|
|
guint16 compression;
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
|
|
(compression == COMPRESSION_CCITTFAX3 ||
|
|
compression == COMPRESSION_CCITTFAX4 ||
|
|
compression == COMPRESSION_CCITTRLE ||
|
|
compression == COMPRESSION_CCITTRLEW))
|
|
{
|
|
g_message ("Could not get photometric from '%s'. "
|
|
"Image is CCITT compressed, assuming min-is-white",
|
|
gimp_file_get_utf8_name (file));
|
|
photomet = PHOTOMETRIC_MINISWHITE;
|
|
}
|
|
else
|
|
{
|
|
g_message ("Could not get photometric from '%s'. "
|
|
"Assuming min-is-black",
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
/* old AppleScan software misses out the photometric tag
|
|
* (and incidentally assumes min-is-white, but xv
|
|
* assumes min-is-black, so we follow xv's lead. It's
|
|
* not much hardship to invert the image later).
|
|
*/
|
|
photomet = PHOTOMETRIC_MINISBLACK;
|
|
}
|
|
}
|
|
|
|
/* test if the extrasample represents an associated alpha channel... */
|
|
if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA))
|
|
{
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = FALSE;
|
|
extra--;
|
|
}
|
|
else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA))
|
|
{
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = TRUE;
|
|
extra--;
|
|
}
|
|
else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
|
|
{
|
|
if (run_mode != GIMP_RUN_INTERACTIVE)
|
|
/* In non-interactive mode, we assume unassociated alpha if unspecified.
|
|
* We don't output messages in interactive mode as the user
|
|
* has already the ability to choose through a dialog. */
|
|
g_message ("alpha channel type not defined for file %s. "
|
|
"Assuming alpha is not premultiplied",
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
switch (default_extra)
|
|
{
|
|
case GIMP_TIFF_LOAD_ASSOCALPHA:
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = FALSE;
|
|
break;
|
|
case GIMP_TIFF_LOAD_UNASSALPHA:
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = TRUE;
|
|
break;
|
|
default: /* GIMP_TIFF_LOAD_CHANNEL */
|
|
alpha = FALSE;
|
|
break;
|
|
}
|
|
extra--;
|
|
}
|
|
else /* extra == 0 */
|
|
{
|
|
if ((photomet == PHOTOMETRIC_RGB && spp > 3) ||
|
|
(photomet != PHOTOMETRIC_RGB && spp > 1))
|
|
{
|
|
if (run_mode != GIMP_RUN_INTERACTIVE)
|
|
g_message ("File '%s' does not conform to TIFF specification: "
|
|
"ExtraSamples field is not set while extra channels are present. "
|
|
"Assuming the first extra channel as non-premultiplied alpha.",
|
|
gimp_file_get_utf8_name (file));
|
|
|
|
switch (default_extra)
|
|
{
|
|
case GIMP_TIFF_LOAD_ASSOCALPHA:
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = FALSE;
|
|
break;
|
|
case GIMP_TIFF_LOAD_UNASSALPHA:
|
|
alpha = TRUE;
|
|
tsvals.save_transp_pixels = TRUE;
|
|
break;
|
|
default: /* GIMP_TIFF_LOAD_CHANNEL */
|
|
alpha = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
alpha = FALSE;
|
|
}
|
|
}
|
|
|
|
if (photomet == PHOTOMETRIC_RGB && spp > 3 + (alpha ? 1 : 0) + extra)
|
|
extra = spp - 3 - (alpha ? 1 : 0);
|
|
else if (photomet != PHOTOMETRIC_RGB && spp > 1 + (alpha ? 1 : 0) + extra)
|
|
extra = spp - 1 - (alpha ? 1 : 0);
|
|
|
|
is_bw = FALSE;
|
|
|
|
switch (photomet)
|
|
{
|
|
case PHOTOMETRIC_MINISBLACK:
|
|
case PHOTOMETRIC_MINISWHITE:
|
|
if (bps == 1 && ! alpha && spp == 1)
|
|
{
|
|
image_type = GIMP_INDEXED;
|
|
layer_type = GIMP_INDEXED_IMAGE;
|
|
|
|
is_bw = TRUE;
|
|
fill_bit2byte ();
|
|
}
|
|
else
|
|
{
|
|
image_type = GIMP_GRAY;
|
|
layer_type = alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
|
|
|
|
if (alpha)
|
|
{
|
|
if (tsvals.save_transp_pixels)
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("YA"),
|
|
type,
|
|
babl_component ("Y"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("Y'A"),
|
|
type,
|
|
babl_component ("Y'"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("YaA"),
|
|
type,
|
|
babl_component ("Ya"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("Y'aA"),
|
|
type,
|
|
babl_component ("Y'a"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("Y"),
|
|
type,
|
|
babl_component ("Y"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("Y'"),
|
|
type,
|
|
babl_component ("Y'"),
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PHOTOMETRIC_RGB:
|
|
image_type = GIMP_RGB;
|
|
layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
|
|
|
|
if (alpha)
|
|
{
|
|
if (tsvals.save_transp_pixels)
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("RGBA"),
|
|
type,
|
|
babl_component ("R"),
|
|
babl_component ("G"),
|
|
babl_component ("B"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("R'G'B'A"),
|
|
type,
|
|
babl_component ("R'"),
|
|
babl_component ("G'"),
|
|
babl_component ("B'"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("RaGaBaA"),
|
|
type,
|
|
babl_component ("Ra"),
|
|
babl_component ("Ga"),
|
|
babl_component ("Ba"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
|
|
type,
|
|
babl_component ("R'a"),
|
|
babl_component ("G'a"),
|
|
babl_component ("B'a"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("RGB"),
|
|
type,
|
|
babl_component ("R"),
|
|
babl_component ("G"),
|
|
babl_component ("B"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("R'G'B'"),
|
|
type,
|
|
babl_component ("R'"),
|
|
babl_component ("G'"),
|
|
babl_component ("B'"),
|
|
NULL);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case PHOTOMETRIC_PALETTE:
|
|
image_type = GIMP_INDEXED;
|
|
layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
|
|
break;
|
|
|
|
default:
|
|
g_printerr ("photomet: %d (%d)\n", photomet, PHOTOMETRIC_PALETTE);
|
|
worst_case = TRUE;
|
|
break;
|
|
}
|
|
|
|
/* attach a parasite containing the compression */
|
|
{
|
|
guint16 compression = COMPRESSION_NONE;
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression))
|
|
{
|
|
switch (compression)
|
|
{
|
|
case COMPRESSION_NONE:
|
|
case COMPRESSION_LZW:
|
|
case COMPRESSION_PACKBITS:
|
|
case COMPRESSION_DEFLATE:
|
|
case COMPRESSION_ADOBE_DEFLATE:
|
|
case COMPRESSION_JPEG:
|
|
case COMPRESSION_CCITTFAX3:
|
|
case COMPRESSION_CCITTFAX4:
|
|
break;
|
|
|
|
case COMPRESSION_OJPEG:
|
|
worst_case = TRUE;
|
|
compression = COMPRESSION_JPEG;
|
|
break;
|
|
|
|
default:
|
|
compression = COMPRESSION_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
save_vals.compression = compression;
|
|
}
|
|
|
|
if (worst_case)
|
|
{
|
|
image_type = GIMP_RGB;
|
|
layer_type = GIMP_RGBA_IMAGE;
|
|
|
|
if (profile_linear)
|
|
{
|
|
base_format = babl_format_new (babl_model ("RaGaBaA"),
|
|
type,
|
|
babl_component ("Ra"),
|
|
babl_component ("Ga"),
|
|
babl_component ("Ba"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
|
|
type,
|
|
babl_component ("R'a"),
|
|
babl_component ("G'a"),
|
|
babl_component ("B'a"),
|
|
babl_component ("A"),
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (pages.target == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
|
|
{
|
|
if (li == 0)
|
|
{
|
|
first_image_type = image_type;
|
|
}
|
|
else if (image_type != first_image_type)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) || (! *image))
|
|
{
|
|
*image = gimp_image_new_with_precision (cols, rows, image_type,
|
|
image_precision);
|
|
|
|
if (*image < 1)
|
|
{
|
|
TIFFClose (tif);
|
|
g_message ("Could not create a new image: %s",
|
|
gimp_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)
|
|
{
|
|
gchar *fname = g_strdup_printf ("%s-%d", g_file_get_uri (file),
|
|
ilayer);
|
|
|
|
gimp_image_set_filename (*image, fname);
|
|
g_free (fname);
|
|
|
|
images_list = g_list_prepend (images_list,
|
|
GINT_TO_POINTER (*image));
|
|
}
|
|
else if (pages.o_pages != pages.n_pages)
|
|
{
|
|
gchar *fname = g_strdup_printf (_("%s-%d-of-%d-pages"),
|
|
g_file_get_uri (file),
|
|
pages.n_pages, pages.o_pages);
|
|
|
|
gimp_image_set_filename (*image, fname);
|
|
g_free (fname);
|
|
}
|
|
else
|
|
{
|
|
gimp_image_set_filename (*image, g_file_get_uri (file));
|
|
}
|
|
}
|
|
|
|
/* attach color profile */
|
|
|
|
if (profile)
|
|
{
|
|
gimp_image_set_color_profile (*image, profile);
|
|
g_object_unref (profile);
|
|
}
|
|
|
|
/* attach parasites */
|
|
{
|
|
GimpParasite *parasite;
|
|
const gchar *img_desc;
|
|
|
|
parasite = gimp_parasite_new ("tiff-save-options", 0,
|
|
sizeof (save_vals), &save_vals);
|
|
gimp_image_attach_parasite (*image, parasite);
|
|
gimp_parasite_free (parasite);
|
|
|
|
/* Attach a parasite containing the image description.
|
|
* Pretend to be a gimp comment so other plugins will use this
|
|
* description as an image comment where appropriate.
|
|
*/
|
|
if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &img_desc) &&
|
|
g_utf8_validate (img_desc, -1, NULL))
|
|
{
|
|
parasite = gimp_parasite_new ("gimp-comment",
|
|
GIMP_PARASITE_PERSISTENT,
|
|
strlen (img_desc) + 1, img_desc);
|
|
gimp_image_attach_parasite (*image, parasite);
|
|
gimp_parasite_free (parasite);
|
|
}
|
|
}
|
|
|
|
/* any resolution info in the file? */
|
|
{
|
|
gfloat xres = 72.0;
|
|
gfloat yres = 72.0;
|
|
gushort read_unit;
|
|
GimpUnit unit = GIMP_UNIT_PIXEL; /* invalid unit */
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres))
|
|
{
|
|
if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres))
|
|
{
|
|
if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT,
|
|
&read_unit))
|
|
{
|
|
switch (read_unit)
|
|
{
|
|
case RESUNIT_NONE:
|
|
/* ImageMagick writes files with this silly resunit */
|
|
break;
|
|
|
|
case RESUNIT_INCH:
|
|
unit = GIMP_UNIT_INCH;
|
|
break;
|
|
|
|
case RESUNIT_CENTIMETER:
|
|
xres *= 2.54;
|
|
yres *= 2.54;
|
|
unit = GIMP_UNIT_MM; /* this is our default metric unit */
|
|
break;
|
|
|
|
default:
|
|
g_message ("File error: unknown resolution "
|
|
"unit type %d, assuming dpi", read_unit);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* no res unit tag */
|
|
|
|
/* old AppleScan software produces these */
|
|
g_message ("Warning: resolution specified without "
|
|
"any units tag, assuming dpi");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* xres but no yres */
|
|
|
|
g_message ("Warning: no y resolution info, assuming same as x");
|
|
yres = xres;
|
|
}
|
|
|
|
/* now set the new image's resolution info */
|
|
|
|
/* If it is invalid, instead of forcing 72dpi, do not set
|
|
* the resolution at all. Gimp will then use the default
|
|
* set by the user
|
|
*/
|
|
if (read_unit != RESUNIT_NONE)
|
|
{
|
|
gimp_image_set_resolution (*image, xres, yres);
|
|
if (unit != GIMP_UNIT_PIXEL)
|
|
gimp_image_set_unit (*image, unit);
|
|
|
|
*resolution_loaded = TRUE;
|
|
}
|
|
}
|
|
|
|
/* no x res tag => we assume we have no resolution info, so we
|
|
* don't care. Older versions of this plugin used to write
|
|
* files with no resolution tags at all.
|
|
*/
|
|
|
|
/* TODO: haven't caught the case where yres tag is present,
|
|
* but not xres. This is left as an exercise for the reader -
|
|
* they should feel free to shoot the author of the broken
|
|
* program that produced the damaged TIFF file in the first
|
|
* place.
|
|
*/
|
|
|
|
/* handle layer offset */
|
|
if (! TIFFGetField (tif, TIFFTAG_XPOSITION, &layer_offset_x))
|
|
layer_offset_x = 0.0;
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_YPOSITION, &layer_offset_y))
|
|
layer_offset_y = 0.0;
|
|
|
|
/* round floating point position to integer position required
|
|
* by GIMP
|
|
*/
|
|
layer_offset_x_pixel = ROUND (layer_offset_x * xres);
|
|
layer_offset_y_pixel = ROUND (layer_offset_y * yres);
|
|
}
|
|
|
|
/* Install colormap for INDEXED images only */
|
|
if (image_type == GIMP_INDEXED)
|
|
{
|
|
guchar cmap[768];
|
|
|
|
if (is_bw)
|
|
{
|
|
if (photomet == PHOTOMETRIC_MINISWHITE)
|
|
{
|
|
cmap[0] = cmap[1] = cmap[2] = 255;
|
|
cmap[3] = cmap[4] = cmap[5] = 0;
|
|
}
|
|
else
|
|
{
|
|
cmap[0] = cmap[1] = cmap[2] = 0;
|
|
cmap[3] = cmap[4] = cmap[5] = 255;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gushort *redmap;
|
|
gushort *greenmap;
|
|
gushort *bluemap;
|
|
gint i, j;
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_COLORMAP,
|
|
&redmap, &greenmap, &bluemap))
|
|
{
|
|
TIFFClose (tif);
|
|
g_message ("Could not get colormaps from '%s'",
|
|
gimp_file_get_utf8_name (file));
|
|
return GIMP_PDB_EXECUTION_ERROR;
|
|
}
|
|
|
|
for (i = 0, j = 0; i < (1 << bps); i++)
|
|
{
|
|
cmap[j++] = redmap[i] >> 8;
|
|
cmap[j++] = greenmap[i] >> 8;
|
|
cmap[j++] = bluemap[i] >> 8;
|
|
}
|
|
}
|
|
|
|
gimp_image_set_colormap (*image, cmap, (1 << bps));
|
|
}
|
|
|
|
if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
|
|
load_paths (tif, *image, cols, rows,
|
|
layer_offset_x_pixel, layer_offset_y_pixel);
|
|
else
|
|
load_paths (tif, *image, cols, rows, 0, 0);
|
|
|
|
/* Allocate ChannelData for all channels, even the background layer */
|
|
channel = g_new0 (ChannelData, extra + 1);
|
|
|
|
/* try and use layer name from tiff file */
|
|
name = tiff_get_page_name (tif);
|
|
|
|
if (name)
|
|
{
|
|
layer = gimp_layer_new (*image, name,
|
|
cols, rows,
|
|
layer_type,
|
|
100,
|
|
gimp_image_get_default_new_layer_mode (*image));
|
|
}
|
|
else
|
|
{
|
|
gchar *name;
|
|
|
|
if (ilayer == 0)
|
|
name = g_strdup (_("Background"));
|
|
else
|
|
name = g_strdup_printf (_("Page %d"), ilayer);
|
|
|
|
layer = gimp_layer_new (*image, name,
|
|
cols, rows,
|
|
layer_type,
|
|
100,
|
|
gimp_image_get_default_new_layer_mode (*image));
|
|
g_free (name);
|
|
}
|
|
|
|
if (! base_format && image_type == GIMP_INDEXED)
|
|
{
|
|
/* can't create the palette format here, need to get it from
|
|
* an existing layer
|
|
*/
|
|
base_format = gimp_drawable_get_format (layer);
|
|
}
|
|
else
|
|
{
|
|
base_format =
|
|
babl_format_with_space (babl_format_get_encoding (base_format),
|
|
gimp_drawable_get_format (layer));
|
|
}
|
|
|
|
channel[0].ID = layer;
|
|
channel[0].buffer = gimp_drawable_get_buffer (layer);
|
|
channel[0].format = base_format;
|
|
|
|
if (extra > 0 && ! worst_case)
|
|
{
|
|
/* Add extra channels as appropriate */
|
|
for (i = 1; i <= extra; i++)
|
|
{
|
|
GimpRGB color;
|
|
|
|
gimp_rgb_set (&color, 0.0, 0.0, 0.0);
|
|
|
|
channel[i].ID = gimp_channel_new (*image, _("TIFF Channel"),
|
|
cols, rows,
|
|
100.0, &color);
|
|
gimp_image_insert_channel (*image, channel[i].ID, -1, 0);
|
|
channel[i].buffer = gimp_drawable_get_buffer (channel[i].ID);
|
|
|
|
/* 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, is_bw, extra);
|
|
}
|
|
else
|
|
{
|
|
load_separate (tif, channel, type, bps, spp, is_bw, extra);
|
|
}
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation))
|
|
{
|
|
gboolean flip_horizontal = FALSE;
|
|
gboolean flip_vertical = FALSE;
|
|
|
|
switch (orientation)
|
|
{
|
|
case ORIENTATION_TOPLEFT:
|
|
break;
|
|
|
|
case ORIENTATION_TOPRIGHT:
|
|
flip_horizontal = TRUE;
|
|
break;
|
|
|
|
case ORIENTATION_BOTRIGHT:
|
|
flip_horizontal = TRUE;
|
|
flip_vertical = TRUE;
|
|
break;
|
|
|
|
case ORIENTATION_BOTLEFT:
|
|
flip_vertical = TRUE;
|
|
break;
|
|
|
|
default:
|
|
g_warning ("Orientation %d not handled yet!", orientation);
|
|
break;
|
|
}
|
|
|
|
if (flip_horizontal)
|
|
gimp_item_transform_flip_simple (layer,
|
|
GIMP_ORIENTATION_HORIZONTAL,
|
|
TRUE /* auto_center */,
|
|
-1.0 /* axis */);
|
|
|
|
if (flip_vertical)
|
|
gimp_item_transform_flip_simple (layer,
|
|
GIMP_ORIENTATION_VERTICAL,
|
|
TRUE /* auto_center */,
|
|
-1.0 /* axis */);
|
|
}
|
|
|
|
for (i = 0; i <= extra; i++)
|
|
{
|
|
if (channel[i].buffer)
|
|
g_object_unref (channel[i].buffer);
|
|
}
|
|
|
|
g_free (channel);
|
|
channel = NULL;
|
|
|
|
if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
|
|
{
|
|
/* compute bounding box of all layers read so far */
|
|
if (min_col > layer_offset_x_pixel)
|
|
min_col = layer_offset_x_pixel;
|
|
if (min_row > layer_offset_y_pixel)
|
|
min_row = layer_offset_y_pixel;
|
|
|
|
if (max_col < layer_offset_x_pixel + cols)
|
|
max_col = layer_offset_x_pixel + cols;
|
|
if (max_row < layer_offset_y_pixel + rows)
|
|
max_row = layer_offset_y_pixel + rows;
|
|
|
|
/* position the layer */
|
|
if (layer_offset_x_pixel > 0 ||
|
|
layer_offset_y_pixel > 0)
|
|
{
|
|
gimp_layer_set_offsets (layer,
|
|
layer_offset_x_pixel,
|
|
layer_offset_y_pixel);
|
|
}
|
|
}
|
|
|
|
gimp_image_insert_layer (*image, layer, -1, -1);
|
|
|
|
if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
|
|
{
|
|
gimp_image_undo_enable (*image);
|
|
gimp_image_clean_all (*image);
|
|
}
|
|
|
|
gimp_progress_update (1.0);
|
|
}
|
|
|
|
if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
|
|
{
|
|
GList *list = images_list;
|
|
|
|
if (list)
|
|
{
|
|
*image = GPOINTER_TO_INT (list->data);
|
|
|
|
list = g_list_next (list);
|
|
}
|
|
|
|
for (; list; list = g_list_next (list))
|
|
{
|
|
gimp_display_new (GPOINTER_TO_INT (list->data));
|
|
}
|
|
|
|
g_list_free (images_list);
|
|
}
|
|
else
|
|
{
|
|
if (pages.keep_empty_space)
|
|
{
|
|
/* unfortunately we have no idea about empty space
|
|
at the bottom/right of layers */
|
|
min_col = 0;
|
|
min_row = 0;
|
|
}
|
|
|
|
/* resize image to bounding box of all layers */
|
|
gimp_image_resize (*image,
|
|
max_col - min_col, max_row - min_row,
|
|
-min_col, -min_row);
|
|
|
|
gimp_image_undo_enable (*image);
|
|
}
|
|
|
|
g_free (pages.pages);
|
|
TIFFClose (tif);
|
|
|
|
return GIMP_PDB_SUCCESS;
|
|
}
|
|
|
|
static GimpColorProfile *
|
|
load_profile (TIFF *tif)
|
|
{
|
|
GimpColorProfile *profile = NULL;
|
|
|
|
#ifdef TIFFTAG_ICCPROFILE
|
|
/* If TIFFTAG_ICCPROFILE is defined we are dealing with a
|
|
* libtiff version that can handle ICC profiles. Otherwise just
|
|
* return a NULL profile.
|
|
*/
|
|
uint32 profile_size;
|
|
guchar *icc_profile;
|
|
|
|
/* set the ICC profile - if found in the TIFF */
|
|
if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile))
|
|
{
|
|
profile = gimp_color_profile_new_from_icc_profile (icc_profile,
|
|
profile_size,
|
|
NULL);
|
|
}
|
|
#endif
|
|
|
|
return profile;
|
|
}
|
|
|
|
static void
|
|
load_rgba (TIFF *tif,
|
|
ChannelData *channel)
|
|
{
|
|
guint32 image_width;
|
|
guint32 image_height;
|
|
guint32 row;
|
|
guint32 *buffer;
|
|
|
|
g_printerr ("%s\n", __func__);
|
|
|
|
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
|
|
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
|
|
|
|
buffer = g_new (uint32, image_width * image_height);
|
|
|
|
if (! TIFFReadRGBAImage (tif, image_width, image_height, buffer, 0))
|
|
g_message ("Unsupported layout, no RGBA loader");
|
|
|
|
for (row = 0; row < image_height; row++)
|
|
{
|
|
#if G_BYTE_ORDER != G_LITTLE_ENDIAN
|
|
/* Make sure our channels are in the right order */
|
|
guint32 row_start = row * image_width;
|
|
guint32 row_end = row_start + image_width;
|
|
guint32 i;
|
|
|
|
for (i = row_start; i < row_end; i++)
|
|
buffer[i] = GUINT32_TO_LE (buffer[i]);
|
|
#endif
|
|
|
|
gegl_buffer_set (channel[0].buffer,
|
|
GEGL_RECTANGLE (0, image_height - row - 1,
|
|
image_width, 1),
|
|
0, channel[0].format,
|
|
((guchar *) buffer) + row * image_width * 4,
|
|
GEGL_AUTO_ROWSTRIDE);
|
|
|
|
if ((row % 32) == 0)
|
|
gimp_progress_update ((gdouble) row / (gdouble) image_height);
|
|
}
|
|
|
|
g_free (buffer);
|
|
}
|
|
|
|
static void
|
|
load_paths (TIFF *tif,
|
|
gint image,
|
|
gint width,
|
|
gint height,
|
|
gint offset_x,
|
|
gint offset_y)
|
|
{
|
|
gsize n_bytes;
|
|
gchar *bytes;
|
|
gint path_index;
|
|
gsize pos;
|
|
|
|
if (! TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &n_bytes, &bytes))
|
|
return;
|
|
|
|
path_index = 0;
|
|
pos = 0;
|
|
|
|
while (pos < n_bytes)
|
|
{
|
|
guint16 id;
|
|
gsize len;
|
|
gchar *name;
|
|
guint32 *val32;
|
|
guint16 *val16;
|
|
|
|
if (n_bytes-pos < 7 ||
|
|
strncmp (bytes + pos, "8BIM", 4) != 0)
|
|
break;
|
|
|
|
pos += 4;
|
|
|
|
val16 = (guint16 *) (bytes + pos);
|
|
id = GUINT16_FROM_BE (*val16);
|
|
pos += 2;
|
|
|
|
/* g_printerr ("id: %x\n", id); */
|
|
len = (guchar) bytes[pos];
|
|
|
|
if (n_bytes - pos < len + 1)
|
|
break; /* block not big enough */
|
|
|
|
/* do we have the UTF-marker? is it valid UTF-8?
|
|
* if so, we assume an utf-8 encoded name, otherwise we
|
|
* assume iso8859-1
|
|
*/
|
|
name = bytes + pos + 1;
|
|
if (len >= 3 &&
|
|
name[0] == '\xEF' && name[1] == '\xBB' && name[2] == '\xBF' &&
|
|
g_utf8_validate (name, len, NULL))
|
|
{
|
|
name = g_strndup (name + 3, len - 3);
|
|
}
|
|
else
|
|
{
|
|
name = g_convert (name, len, "utf-8", "iso8859-1", NULL, NULL, NULL);
|
|
}
|
|
|
|
if (! name)
|
|
name = g_strdup ("(imported path)");
|
|
|
|
pos += len + 1;
|
|
|
|
if (pos % 2) /* padding */
|
|
pos++;
|
|
|
|
if (n_bytes - pos < 4)
|
|
break; /* block not big enough */
|
|
|
|
val32 = (guint32 *) (bytes + pos);
|
|
len = GUINT32_FROM_BE (*val32);
|
|
pos += 4;
|
|
|
|
if (n_bytes - pos < len)
|
|
break; /* block not big enough */
|
|
|
|
if (id >= 2000 && id <= 2998)
|
|
{
|
|
/* path information */
|
|
guint16 type;
|
|
gint rec = pos;
|
|
gint32 vectors;
|
|
gdouble *points = NULL;
|
|
gint expected_points = 0;
|
|
gint pointcount = 0;
|
|
gboolean closed = FALSE;
|
|
|
|
vectors = gimp_vectors_new (image, name);
|
|
gimp_image_insert_vectors (image, vectors, -1, path_index);
|
|
path_index++;
|
|
|
|
while (rec < pos + len)
|
|
{
|
|
/* path records */
|
|
val16 = (guint16 *) (bytes + rec);
|
|
type = GUINT16_FROM_BE (*val16);
|
|
|
|
switch (type)
|
|
{
|
|
case 0: /* new closed subpath */
|
|
case 3: /* new open subpath */
|
|
val16 = (guint16 *) (bytes + rec + 2);
|
|
expected_points = GUINT16_FROM_BE (*val16);
|
|
pointcount = 0;
|
|
closed = (type == 0);
|
|
|
|
if (n_bytes - rec < (expected_points + 1) * 26)
|
|
{
|
|
g_printerr ("not enough point records\n");
|
|
rec = pos + len;
|
|
continue;
|
|
}
|
|
|
|
if (points)
|
|
g_free (points);
|
|
points = g_new (gdouble, expected_points * 6);
|
|
break;
|
|
|
|
case 1: /* closed subpath bezier knot, linked */
|
|
case 2: /* closed subpath bezier knot, unlinked */
|
|
case 4: /* open subpath bezier knot, linked */
|
|
case 5: /* open subpath bezier knot, unlinked */
|
|
/* since we already know if the subpath is open
|
|
* or closed and since we don't differentiate between
|
|
* linked and unlinked, just treat all the same... */
|
|
|
|
if (pointcount < expected_points)
|
|
{
|
|
gint j;
|
|
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
gdouble f;
|
|
guint32 coord;
|
|
|
|
const gint size = j % 2 ? width : height;
|
|
const gint offset = j % 2 ? offset_x : offset_y;
|
|
|
|
val32 = (guint32 *) (bytes + rec + 2 + j * 4);
|
|
coord = GUINT32_FROM_BE (*val32);
|
|
|
|
f = (double) ((gchar) ((coord >> 24) & 0xFF)) +
|
|
(double) (coord & 0x00FFFFFF) /
|
|
(double) 0xFFFFFF;
|
|
|
|
/* coords are stored with vertical component
|
|
* first, gimp expects the horizontal
|
|
* component first. Sigh.
|
|
*/
|
|
points[pointcount * 6 + (j ^ 1)] = f * size + offset;
|
|
}
|
|
|
|
pointcount++;
|
|
|
|
if (pointcount == expected_points)
|
|
{
|
|
gimp_vectors_stroke_new_from_points (vectors,
|
|
GIMP_VECTORS_STROKE_TYPE_BEZIER,
|
|
pointcount * 6,
|
|
points,
|
|
closed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
g_printerr ("Oops - unexpected point record\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case 6: /* path fill rule record */
|
|
case 7: /* clipboard record (?) */
|
|
case 8: /* initial fill rule record (?) */
|
|
/* we cannot use this information */
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
rec += 26;
|
|
}
|
|
|
|
if (points)
|
|
g_free (points);
|
|
}
|
|
|
|
pos += len;
|
|
|
|
if (pos % 2) /* padding */
|
|
pos++;
|
|
|
|
g_free (name);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
load_contiguous (TIFF *tif,
|
|
ChannelData *channel,
|
|
const Babl *type,
|
|
gushort bps,
|
|
gushort spp,
|
|
gboolean is_bw,
|
|
gint extra)
|
|
{
|
|
guint32 image_width;
|
|
guint32 image_height;
|
|
guint32 tile_width;
|
|
guint32 tile_height;
|
|
gint bytes_per_pixel;
|
|
const Babl *src_format;
|
|
guchar *buffer;
|
|
guchar *bw_buffer = NULL;
|
|
gdouble progress = 0.0;
|
|
gdouble one_row;
|
|
guint32 y;
|
|
gint i;
|
|
|
|
g_printerr ("%s\n", __func__);
|
|
|
|
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
|
|
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
|
|
|
|
tile_width = image_width;
|
|
|
|
if (TIFFIsTiled (tif))
|
|
{
|
|
TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
|
|
TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
|
|
|
|
buffer = g_malloc (TIFFTileSize (tif));
|
|
}
|
|
else
|
|
{
|
|
tile_width = image_width;
|
|
tile_height = 1;
|
|
|
|
buffer = g_malloc (TIFFScanlineSize (tif));
|
|
}
|
|
|
|
if (is_bw)
|
|
bw_buffer = g_malloc (tile_width * tile_height);
|
|
|
|
one_row = (gdouble) tile_height / (gdouble) image_height;
|
|
|
|
src_format = babl_format_n (type, spp);
|
|
|
|
/* consistency check */
|
|
bytes_per_pixel = 0;
|
|
for (i = 0; i <= extra; i++)
|
|
bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
|
|
|
|
g_printerr ("bytes_per_pixel: %d, format: %d\n",
|
|
bytes_per_pixel,
|
|
babl_format_get_bytes_per_pixel (src_format));
|
|
|
|
for (y = 0; y < image_height; y += tile_height)
|
|
{
|
|
guint32 x;
|
|
|
|
for (x = 0; x < image_width; x += tile_width)
|
|
{
|
|
GeglBuffer *src_buf;
|
|
guint32 rows;
|
|
guint32 cols;
|
|
gint offset;
|
|
|
|
gimp_progress_update (progress + one_row *
|
|
((gdouble) x / (gdouble) image_width));
|
|
|
|
if (TIFFIsTiled (tif))
|
|
TIFFReadTile (tif, buffer, x, y, 0, 0);
|
|
else
|
|
TIFFReadScanline (tif, buffer, y, 0);
|
|
|
|
cols = MIN (image_width - x, tile_width);
|
|
rows = MIN (image_height - y, tile_height);
|
|
|
|
if (is_bw)
|
|
convert_bit2byte (buffer, bw_buffer, cols, rows);
|
|
|
|
src_buf = gegl_buffer_linear_new_from_data (is_bw ? bw_buffer : buffer,
|
|
src_format,
|
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
|
tile_width * bytes_per_pixel,
|
|
NULL, NULL);
|
|
|
|
offset = 0;
|
|
|
|
for (i = 0; i <= extra; i++)
|
|
{
|
|
GeglBufferIterator *iter;
|
|
gint src_bpp;
|
|
gint dest_bpp;
|
|
|
|
src_bpp = babl_format_get_bytes_per_pixel (src_format);
|
|
dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
|
|
|
|
iter = gegl_buffer_iterator_new (src_buf,
|
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
|
0, NULL,
|
|
GEGL_ACCESS_READ,
|
|
GEGL_ABYSS_NONE, 2);
|
|
gegl_buffer_iterator_add (iter, channel[i].buffer,
|
|
GEGL_RECTANGLE (x, y, cols, rows),
|
|
0, channel[i].format,
|
|
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
|
|
|
|
while (gegl_buffer_iterator_next (iter))
|
|
{
|
|
guchar *s = iter->items[0].data;
|
|
guchar *d = iter->items[1].data;
|
|
gint length = iter->length;
|
|
|
|
s += offset;
|
|
|
|
while (length--)
|
|
{
|
|
memcpy (d, s, dest_bpp);
|
|
d += dest_bpp;
|
|
s += src_bpp;
|
|
}
|
|
}
|
|
|
|
offset += dest_bpp;
|
|
}
|
|
|
|
g_object_unref (src_buf);
|
|
}
|
|
|
|
progress += one_row;
|
|
}
|
|
|
|
g_free (buffer);
|
|
g_free (bw_buffer);
|
|
}
|
|
|
|
|
|
static void
|
|
load_separate (TIFF *tif,
|
|
ChannelData *channel,
|
|
const Babl *type,
|
|
gushort bps,
|
|
gushort spp,
|
|
gboolean is_bw,
|
|
gint extra)
|
|
{
|
|
guint32 image_width;
|
|
guint32 image_height;
|
|
guint32 tile_width;
|
|
guint32 tile_height;
|
|
gint bytes_per_pixel;
|
|
const Babl *src_format;
|
|
guchar *buffer;
|
|
guchar *bw_buffer = NULL;
|
|
gdouble progress = 0.0;
|
|
gdouble one_row;
|
|
gint i, compindex;
|
|
|
|
g_printerr ("%s\n", __func__);
|
|
|
|
TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
|
|
TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
|
|
|
|
tile_width = image_width;
|
|
|
|
if (TIFFIsTiled (tif))
|
|
{
|
|
TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
|
|
TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
|
|
|
|
buffer = g_malloc (TIFFTileSize (tif));
|
|
}
|
|
else
|
|
{
|
|
tile_width = image_width;
|
|
tile_height = 1;
|
|
|
|
buffer = g_malloc (TIFFScanlineSize (tif));
|
|
}
|
|
|
|
if (is_bw)
|
|
bw_buffer = g_malloc (tile_width * tile_height);
|
|
|
|
one_row = (gdouble) tile_height / (gdouble) image_height;
|
|
|
|
src_format = babl_format_n (type, 1);
|
|
|
|
/* consistency check */
|
|
bytes_per_pixel = 0;
|
|
for (i = 0; i <= extra; i++)
|
|
bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
|
|
|
|
g_printerr ("bytes_per_pixel: %d, format: %d\n",
|
|
bytes_per_pixel,
|
|
babl_format_get_bytes_per_pixel (src_format));
|
|
|
|
compindex = 0;
|
|
|
|
for (i = 0; i <= extra; i++)
|
|
{
|
|
gint n_comps;
|
|
gint src_bpp;
|
|
gint dest_bpp;
|
|
gint offset;
|
|
gint j;
|
|
|
|
n_comps = babl_format_get_n_components (channel[i].format);
|
|
src_bpp = babl_format_get_bytes_per_pixel (src_format);
|
|
dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
|
|
|
|
offset = 0;
|
|
|
|
for (j = 0; j < n_comps; j++)
|
|
{
|
|
guint32 y;
|
|
|
|
for (y = 0; y < image_height; y += tile_height)
|
|
{
|
|
guint32 x;
|
|
|
|
for (x = 0; x < image_width; x += tile_width)
|
|
{
|
|
GeglBuffer *src_buf;
|
|
GeglBufferIterator *iter;
|
|
guint32 rows;
|
|
guint32 cols;
|
|
|
|
gimp_progress_update (progress + one_row *
|
|
((gdouble) x / (gdouble) image_width));
|
|
|
|
if (TIFFIsTiled (tif))
|
|
TIFFReadTile (tif, buffer, x, y, 0, compindex);
|
|
else
|
|
TIFFReadScanline (tif, buffer, y, compindex);
|
|
|
|
cols = MIN (image_width - x, tile_width);
|
|
rows = MIN (image_height - y, tile_height);
|
|
|
|
if (is_bw)
|
|
convert_bit2byte (buffer, bw_buffer, cols, rows);
|
|
|
|
src_buf = gegl_buffer_linear_new_from_data (is_bw ? bw_buffer : buffer,
|
|
src_format,
|
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
|
GEGL_AUTO_ROWSTRIDE,
|
|
NULL, NULL);
|
|
|
|
iter = gegl_buffer_iterator_new (src_buf,
|
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
|
0, NULL,
|
|
GEGL_ACCESS_READ,
|
|
GEGL_ABYSS_NONE, 2);
|
|
gegl_buffer_iterator_add (iter, channel[i].buffer,
|
|
GEGL_RECTANGLE (x, y, cols, rows),
|
|
0, channel[i].format,
|
|
GEGL_ACCESS_READWRITE,
|
|
GEGL_ABYSS_NONE);
|
|
|
|
while (gegl_buffer_iterator_next (iter))
|
|
{
|
|
guchar *s = iter->items[0].data;
|
|
guchar *d = iter->items[1].data;
|
|
gint length = iter->length;
|
|
|
|
d += offset;
|
|
|
|
while (length--)
|
|
{
|
|
memcpy (d, s, src_bpp);
|
|
d += dest_bpp;
|
|
s += src_bpp;
|
|
}
|
|
}
|
|
|
|
g_object_unref (src_buf);
|
|
}
|
|
}
|
|
|
|
offset += src_bpp;
|
|
compindex++;
|
|
}
|
|
|
|
progress += one_row;
|
|
}
|
|
|
|
g_free (buffer);
|
|
g_free (bw_buffer);
|
|
}
|
|
|
|
|
|
static guchar bit2byte[256 * 8];
|
|
|
|
static void
|
|
fill_bit2byte (void)
|
|
{
|
|
static gboolean filled = FALSE;
|
|
|
|
guchar *dest;
|
|
gint i, j;
|
|
|
|
if (filled)
|
|
return;
|
|
|
|
dest = bit2byte;
|
|
|
|
for (j = 0; j < 256; j++)
|
|
for (i = 7; i >= 0; i--)
|
|
*(dest++) = ((j & (1 << i)) != 0);
|
|
|
|
filled = TRUE;
|
|
}
|
|
|
|
static void
|
|
convert_bit2byte (const guchar *src,
|
|
guchar *dest,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
gint y;
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
gint x = width;
|
|
|
|
while (x >= 8)
|
|
{
|
|
memcpy (dest, bit2byte + *src * 8, 8);
|
|
dest += 8;
|
|
x -= 8;
|
|
src++;
|
|
}
|
|
|
|
if (x > 0)
|
|
{
|
|
memcpy (dest, bit2byte + *src * 8, x);
|
|
dest += x;
|
|
src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
load_dialog (TIFF *tif,
|
|
const gchar *help_id,
|
|
TiffSelectedPages *pages,
|
|
const gchar *extra_message,
|
|
DefaultExtra *default_extra)
|
|
{
|
|
GtkWidget *dialog;
|
|
GtkWidget *vbox;
|
|
GtkWidget *selector = NULL;
|
|
GtkWidget *crop_option = NULL;
|
|
GtkWidget *extra_radio = NULL;
|
|
gboolean run;
|
|
|
|
dialog = gimp_dialog_new (_("Import from TIFF"), PLUG_IN_ROLE,
|
|
NULL, 0,
|
|
gimp_standard_help_func, help_id,
|
|
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
_("_Import"), GTK_RESPONSE_OK,
|
|
|
|
NULL);
|
|
|
|
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
|
GTK_RESPONSE_OK,
|
|
GTK_RESPONSE_CANCEL,
|
|
-1);
|
|
|
|
gimp_window_set_transient (GTK_WINDOW (dialog));
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
|
vbox, TRUE, TRUE, 0);
|
|
|
|
if (pages->n_pages > 1)
|
|
{
|
|
gint i;
|
|
|
|
/* Page Selector */
|
|
selector = gimp_page_selector_new ();
|
|
gtk_widget_set_size_request (selector, 300, 200);
|
|
gtk_box_pack_start (GTK_BOX (vbox), selector, TRUE, TRUE, 0);
|
|
|
|
gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (selector),
|
|
pages->n_pages);
|
|
gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (selector), pages->target);
|
|
|
|
for (i = 0; i < pages->n_pages; i++)
|
|
{
|
|
const gchar *name = tiff_get_page_name (tif);
|
|
|
|
if (name)
|
|
gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (selector),
|
|
i, name);
|
|
|
|
TIFFReadDirectory (tif);
|
|
}
|
|
|
|
g_signal_connect_swapped (selector, "activate",
|
|
G_CALLBACK (gtk_window_activate_default),
|
|
dialog);
|
|
|
|
/* Option to shrink the loaded image to its bounding box
|
|
or keep as much empty space as possible.
|
|
Note that there seems to be no way to keep the empty
|
|
space on the right and bottom. */
|
|
crop_option = gtk_check_button_new_with_label (_("Keep empty space around imported layers"));
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (crop_option),
|
|
pages->keep_empty_space);
|
|
gtk_box_pack_start (GTK_BOX (vbox), crop_option, TRUE, TRUE, 0);
|
|
}
|
|
|
|
if (extra_message)
|
|
{
|
|
GtkWidget *warning;
|
|
|
|
warning = g_object_new (GIMP_TYPE_HINT_BOX,
|
|
"icon-name", GIMP_ICON_DIALOG_WARNING,
|
|
"hint", extra_message,
|
|
NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), warning, TRUE, TRUE, 0);
|
|
gtk_widget_show (warning);
|
|
|
|
extra_radio = gimp_int_radio_group_new (TRUE, _("Process extra channel as:"),
|
|
(GCallback) gimp_radio_button_update,
|
|
default_extra, GIMP_TIFF_LOAD_UNASSALPHA,
|
|
_("Non-premultiplied alpha"), GIMP_TIFF_LOAD_UNASSALPHA, NULL,
|
|
_("Premultiplied alpha"), GIMP_TIFF_LOAD_ASSOCALPHA, NULL,
|
|
_("Channel"), GIMP_TIFF_LOAD_CHANNEL, NULL,
|
|
NULL);
|
|
gtk_box_pack_start (GTK_BOX (vbox), extra_radio, TRUE, TRUE, 0);
|
|
gtk_widget_show (extra_radio);
|
|
}
|
|
|
|
/* Setup done; display the dialog */
|
|
gtk_widget_show_all (dialog);
|
|
|
|
/* run the dialog */
|
|
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
|
|
|
|
if (run)
|
|
{
|
|
if (pages->n_pages > 1)
|
|
{
|
|
pages->target =
|
|
gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (selector));
|
|
|
|
pages->pages =
|
|
gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (selector),
|
|
&pages->n_pages);
|
|
|
|
pages->keep_empty_space =
|
|
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crop_option));
|
|
|
|
/* select all if none selected */
|
|
if (pages->n_pages == 0)
|
|
{
|
|
gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (selector));
|
|
|
|
pages->pages =
|
|
gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (selector),
|
|
&pages->n_pages);
|
|
}
|
|
}
|
|
}
|
|
|
|
return run;
|
|
}
|