2705 lines
89 KiB
C
2705 lines
89 KiB
C
|
/* tiff loading for PIKA
|
||
|
* -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 <libpika/pika.h>
|
||
|
#include <libpika/pikaui.h>
|
||
|
|
||
|
#include "file-tiff.h"
|
||
|
#include "file-tiff-io.h"
|
||
|
#include "file-tiff-load.h"
|
||
|
|
||
|
#include "libpika/stdplugins-intl.h"
|
||
|
|
||
|
|
||
|
#define PLUG_IN_ROLE "pika-file-tiff-load"
|
||
|
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
PikaDrawable *drawable;
|
||
|
GeglBuffer *buffer;
|
||
|
const Babl *format;
|
||
|
guchar *pixels;
|
||
|
guchar *pixel;
|
||
|
} ChannelData;
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
PIKA_TIFF_LOAD_ASSOCALPHA,
|
||
|
PIKA_TIFF_LOAD_UNASSALPHA,
|
||
|
PIKA_TIFF_LOAD_CHANNEL
|
||
|
} DefaultExtra;
|
||
|
|
||
|
typedef enum
|
||
|
{
|
||
|
PIKA_TIFF_DEFAULT,
|
||
|
PIKA_TIFF_INDEXED,
|
||
|
PIKA_TIFF_GRAY,
|
||
|
PIKA_TIFF_GRAY_MINISWHITE,
|
||
|
} TiffColorMode;
|
||
|
|
||
|
/* Declare some local functions */
|
||
|
|
||
|
static PikaColorProfile * 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 gboolean is_non_conformant_tiff (gushort photomet,
|
||
|
gushort spp);
|
||
|
static gushort get_extra_channels_count (gushort photomet,
|
||
|
gushort spp,
|
||
|
gboolean alpha);
|
||
|
|
||
|
static void fill_bit2byte (TiffColorMode tiff_mode);
|
||
|
static void fill_2bit2byte (TiffColorMode tiff_mode);
|
||
|
static void fill_4bit2byte (TiffColorMode tiff_mode);
|
||
|
static void convert_bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
static void convert_2bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
static void convert_4bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
|
||
|
static void convert_miniswhite (guchar *buffer,
|
||
|
gint width,
|
||
|
gint height);
|
||
|
static void convert_int2uint (guchar *buffer,
|
||
|
gint bps,
|
||
|
gint spp,
|
||
|
gint width,
|
||
|
gint height,
|
||
|
gint stride);
|
||
|
|
||
|
static gboolean load_dialog (const gchar *help_id,
|
||
|
TiffSelectedPages *pages,
|
||
|
const gchar *extra_message,
|
||
|
DefaultExtra *default_extra);
|
||
|
|
||
|
static void tiff_dialog_show_reduced (GtkWidget *toggle,
|
||
|
gpointer data);
|
||
|
|
||
|
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PikaPDBStatusType
|
||
|
load_image (GFile *file,
|
||
|
PikaRunMode run_mode,
|
||
|
PikaImage **image,
|
||
|
gboolean *resolution_loaded,
|
||
|
gboolean *profile_loaded,
|
||
|
gboolean *ps_metadata_loaded,
|
||
|
GError **error)
|
||
|
{
|
||
|
TIFF *tif;
|
||
|
TiffSelectedPages pages;
|
||
|
|
||
|
GList *images_list = NULL;
|
||
|
DefaultExtra default_extra = PIKA_TIFF_LOAD_UNASSALPHA;
|
||
|
gint first_image_type = PIKA_RGB;
|
||
|
gint min_row = G_MAXINT;
|
||
|
gint min_col = G_MAXINT;
|
||
|
gint max_row = 0;
|
||
|
gint max_col = 0;
|
||
|
gboolean save_transp_pixels = FALSE;
|
||
|
PikaColorProfile *first_profile = NULL;
|
||
|
const gchar *extra_message = NULL;
|
||
|
gint li;
|
||
|
gint selectable_pages;
|
||
|
gchar *photoshop_data;
|
||
|
gint32 photoshop_len;
|
||
|
gboolean is_cmyk = FALSE;
|
||
|
|
||
|
*image = NULL;
|
||
|
pika_progress_init_printf (_("Opening '%s'"),
|
||
|
pika_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 PIKA_PDB_EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
pages.target = PIKA_PAGE_SELECTOR_TARGET_LAYERS;
|
||
|
pika_get_data (LOAD_PROC "-target", &pages.target);
|
||
|
|
||
|
pages.keep_empty_space = TRUE;
|
||
|
pika_get_data (LOAD_PROC "-keep-empty-space",
|
||
|
&pages.keep_empty_space);
|
||
|
|
||
|
pages.n_pages = pages.o_pages = TIFFNumberOfDirectories (tif);
|
||
|
if (pages.n_pages == 0)
|
||
|
{
|
||
|
/* See #5837.
|
||
|
* It seems we might be able to rescue some data even though the
|
||
|
* TIFF is possibly syntactically wrong.
|
||
|
*/
|
||
|
|
||
|
/* libtiff says max number of directory is 65535. */
|
||
|
for (li = 0; li < 65536; li++)
|
||
|
{
|
||
|
if (TIFFSetDirectory (tif, li) == 0)
|
||
|
break;
|
||
|
}
|
||
|
pages.n_pages = li;
|
||
|
if (pages.n_pages == 0)
|
||
|
{
|
||
|
TIFFClose (tif);
|
||
|
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
|
||
|
_("TIFF '%s' does not contain any directories"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
|
||
|
return PIKA_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),
|
||
|
pika_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 != PIKA_RUN_INTERACTIVE)
|
||
|
{
|
||
|
pages.pages = g_new0 (gint, pages.n_pages);
|
||
|
for (li = 0; li < pages.n_pages; li++)
|
||
|
pages.pages[li] = li;
|
||
|
pages.target = PIKA_PAGE_SELECTOR_TARGET_LAYERS;
|
||
|
}
|
||
|
|
||
|
/* Check all pages if any has an unspecified or unset channel. */
|
||
|
for (li = 0; li < pages.n_pages; li++)
|
||
|
{
|
||
|
gushort spp;
|
||
|
gushort photomet;
|
||
|
gushort extra;
|
||
|
gushort *extra_types;
|
||
|
gushort file_type = 0;
|
||
|
gboolean first_page_old_jpeg = FALSE;
|
||
|
|
||
|
if (TIFFSetDirectory (tif, li) == 0)
|
||
|
continue;
|
||
|
|
||
|
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
|
||
|
if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
|
||
|
{
|
||
|
guint16 compression;
|
||
|
|
||
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
|
||
|
(compression == COMPRESSION_CCITTFAX3 ||
|
||
|
compression == COMPRESSION_CCITTFAX4 ||
|
||
|
compression == COMPRESSION_CCITTRLE ||
|
||
|
compression == COMPRESSION_CCITTRLEW))
|
||
|
{
|
||
|
photomet = PHOTOMETRIC_MINISWHITE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* old AppleScan software misses out the photometric tag
|
||
|
* (and incidentally assumes min-is-white, but xv
|
||
|
* assumes min-is-black, so we follow xv's lead. It's
|
||
|
* not much hardship to invert the image later).
|
||
|
*/
|
||
|
photomet = PHOTOMETRIC_MINISBLACK;
|
||
|
}
|
||
|
}
|
||
|
if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
||
|
extra = 0;
|
||
|
|
||
|
/* Try to detect if a TIFF page is a thumbnail.
|
||
|
* Easy case: if subfiletype is set to FILETYPE_REDUCEDIMAGE.
|
||
|
* If no subfiletype is defined we try to detect it ourselves.
|
||
|
* We will consider it a thumbnail if:
|
||
|
* - It's the second page
|
||
|
* - PhotometricInterpretation is YCbCr
|
||
|
* - Compression is old style jpeg
|
||
|
* - First page uses a different compression or PhotometricInterpretation
|
||
|
*
|
||
|
* We could also add a check for the presence of TIFFTAG_EXIFIFD since
|
||
|
* this should usually be a thumbnail part of EXIF metadata. Since that
|
||
|
* probably won't make a difference, I will leave that out for now.
|
||
|
*/
|
||
|
if (li == 0)
|
||
|
{
|
||
|
guint16 compression;
|
||
|
|
||
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
|
||
|
compression == COMPRESSION_OJPEG &&
|
||
|
photomet == PHOTOMETRIC_YCBCR)
|
||
|
first_page_old_jpeg = TRUE;
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, TIFFTAG_SUBFILETYPE, &file_type))
|
||
|
{
|
||
|
if (file_type == FILETYPE_REDUCEDIMAGE)
|
||
|
{
|
||
|
/* file_type is a mask but we will only filter out pages
|
||
|
* that only have FILETYPE_REDUCEDIMAGE set */
|
||
|
pages.filtered_pages[li] = TIFF_REDUCEDFILE;
|
||
|
pages.n_filtered_pages--;
|
||
|
g_debug ("Page %d is a FILETYPE_REDUCEDIMAGE thumbnail.\n", li);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (li == 1 && photomet == PHOTOMETRIC_YCBCR &&
|
||
|
! first_page_old_jpeg)
|
||
|
{
|
||
|
guint16 compression;
|
||
|
|
||
|
if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
|
||
|
compression == COMPRESSION_OJPEG)
|
||
|
{
|
||
|
pages.filtered_pages[li] = TIFF_MISC_THUMBNAIL;
|
||
|
pages.n_filtered_pages--;
|
||
|
/* This is used to conditionally show reduced images
|
||
|
* if they're not a thumbnail
|
||
|
*/
|
||
|
pages.n_reducedimage_pages--;
|
||
|
g_debug ("Page %d is most likely a thumbnail.\n", li);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* TODO: current code always assumes that the alpha channel
|
||
|
* will be the first extra channel, though the TIFF spec does
|
||
|
* not mandate such assumption. A future improvement should be
|
||
|
* to actually loop through the extra channels and save the
|
||
|
* alpha channel index.
|
||
|
* Of course, this is an edge case, as most image would likely
|
||
|
* have only a single extra channel anyway. But still we could
|
||
|
* be more accurate.
|
||
|
*/
|
||
|
if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
|
||
|
{
|
||
|
extra_message = _("Extra channels with unspecified data.");
|
||
|
break;
|
||
|
}
|
||
|
else if (extra == 0 && is_non_conformant_tiff (photomet, spp))
|
||
|
{
|
||
|
/* ExtraSamples field not set, yet we have more channels than
|
||
|
* the PhotometricInterpretation field suggests.
|
||
|
* This should not happen as the spec clearly says "This field
|
||
|
* must be present if there are extra samples". So the files
|
||
|
* can be considered non-conformant.
|
||
|
* Let's ask what to do with the channel.
|
||
|
*/
|
||
|
extra_message = _("Non-conformant TIFF: extra channels without 'ExtraSamples' field.");
|
||
|
}
|
||
|
}
|
||
|
TIFFSetDirectory (tif, 0);
|
||
|
|
||
|
pages.show_reduced = FALSE;
|
||
|
if (pages.n_reducedimage_pages - pages.n_filtered_pages > 1)
|
||
|
pages.show_reduced = TRUE;
|
||
|
|
||
|
pages.tif = tif;
|
||
|
|
||
|
if (run_mode == PIKA_RUN_INTERACTIVE &&
|
||
|
(pages.n_pages > 1 || extra_message) &&
|
||
|
! load_dialog (LOAD_PROC, &pages,
|
||
|
extra_message, &default_extra))
|
||
|
{
|
||
|
TIFFClose (tif);
|
||
|
g_clear_pointer (&pages.pages, g_free);
|
||
|
|
||
|
return PIKA_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++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_set_data (LOAD_PROC "-target",
|
||
|
&pages.target, sizeof (pages.target));
|
||
|
pika_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;
|
||
|
PikaColorProfile *profile;
|
||
|
gboolean profile_linear = FALSE;
|
||
|
PikaPrecision 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 = PIKA_RGB;
|
||
|
PikaLayer *layer;
|
||
|
gint layer_type = PIKA_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 pika_compression = PIKA_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];
|
||
|
|
||
|
pika_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 = pika_color_profile_is_linear (profile);
|
||
|
|
||
|
if (! first_profile)
|
||
|
{
|
||
|
first_profile = profile;
|
||
|
g_object_ref (first_profile);
|
||
|
|
||
|
if (profile_linear && li > 0 && pages.target != PIKA_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 != PIKA_PAGE_SELECTOR_TARGET_IMAGES &&
|
||
|
! pika_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 = PIKA_PRECISION_U8_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_PRECISION_U8_NON_LINEAR;
|
||
|
|
||
|
type = babl_type ("u8");
|
||
|
break;
|
||
|
|
||
|
case 16:
|
||
|
if (sampleformat == SAMPLEFORMAT_IEEEFP)
|
||
|
{
|
||
|
if (profile_linear)
|
||
|
image_precision = PIKA_PRECISION_HALF_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_PRECISION_HALF_NON_LINEAR;
|
||
|
|
||
|
type = babl_type ("half");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (profile_linear)
|
||
|
image_precision = PIKA_PRECISION_U16_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_PRECISION_U16_NON_LINEAR;
|
||
|
|
||
|
type = babl_type ("u16");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 32:
|
||
|
if (sampleformat == SAMPLEFORMAT_IEEEFP)
|
||
|
{
|
||
|
if (profile_linear)
|
||
|
image_precision = PIKA_PRECISION_FLOAT_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_PRECISION_FLOAT_NON_LINEAR;
|
||
|
|
||
|
type = babl_type ("float");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (profile_linear)
|
||
|
image_precision = PIKA_PRECISION_U32_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_PRECISION_U32_NON_LINEAR;
|
||
|
|
||
|
type = babl_type ("u32");
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 64:
|
||
|
if (profile_linear)
|
||
|
image_precision = PIKA_PRECISION_DOUBLE_LINEAR;
|
||
|
else
|
||
|
image_precision = PIKA_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'"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
return PIKA_PDB_EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows))
|
||
|
{
|
||
|
TIFFClose (tif);
|
||
|
g_message (_("Could not get image length from '%s'"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
return PIKA_PDB_EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
if (cols > PIKA_MAX_IMAGE_SIZE || cols <= 0 ||
|
||
|
rows > PIKA_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"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
photomet = PHOTOMETRIC_MINISWHITE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
g_message (_("Could not get photometric from '%s'. "
|
||
|
"Assuming min-is-black"),
|
||
|
pika_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 != PIKA_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"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
|
||
|
switch (default_extra)
|
||
|
{
|
||
|
case PIKA_TIFF_LOAD_ASSOCALPHA:
|
||
|
alpha = TRUE;
|
||
|
save_transp_pixels = FALSE;
|
||
|
break;
|
||
|
case PIKA_TIFF_LOAD_UNASSALPHA:
|
||
|
alpha = TRUE;
|
||
|
save_transp_pixels = TRUE;
|
||
|
break;
|
||
|
default: /* PIKA_TIFF_LOAD_CHANNEL */
|
||
|
alpha = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
extra--;
|
||
|
}
|
||
|
else /* extra == 0 */
|
||
|
{
|
||
|
if (is_non_conformant_tiff (photomet, spp))
|
||
|
{
|
||
|
if (run_mode != PIKA_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."),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
|
||
|
switch (default_extra)
|
||
|
{
|
||
|
case PIKA_TIFF_LOAD_ASSOCALPHA:
|
||
|
alpha = TRUE;
|
||
|
save_transp_pixels = FALSE;
|
||
|
break;
|
||
|
case PIKA_TIFF_LOAD_UNASSALPHA:
|
||
|
alpha = TRUE;
|
||
|
save_transp_pixels = TRUE;
|
||
|
break;
|
||
|
default: /* PIKA_TIFF_LOAD_CHANNEL */
|
||
|
alpha = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
alpha = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
extra = get_extra_channels_count (photomet, spp, alpha);
|
||
|
|
||
|
tiff_mode = PIKA_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 = PIKA_TIFF_INDEXED;
|
||
|
else if (photomet == PHOTOMETRIC_MINISBLACK)
|
||
|
tiff_mode = PIKA_TIFF_GRAY;
|
||
|
else if (photomet == PHOTOMETRIC_MINISWHITE)
|
||
|
tiff_mode = PIKA_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 = PIKA_INDEXED;
|
||
|
layer_type = alpha ? PIKA_INDEXEDA_IMAGE : PIKA_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 = PIKA_INDEXED;
|
||
|
layer_type = alpha ? PIKA_INDEXEDA_IMAGE : PIKA_INDEXED_IMAGE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
image_type = PIKA_GRAY;
|
||
|
layer_type = alpha ? PIKA_GRAYA_IMAGE : PIKA_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 = PIKA_RGB;
|
||
|
layer_type = alpha ? PIKA_RGBA_IMAGE : PIKA_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 ? PIKA_RGBA_IMAGE : PIKA_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 && pika_color_profile_is_cmyk (profile))
|
||
|
{
|
||
|
space = pika_color_profile_get_space (profile,
|
||
|
PIKA_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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_compression = tiff_compression_to_pika_compression (compression);
|
||
|
}
|
||
|
|
||
|
if (worst_case)
|
||
|
{
|
||
|
image_type = PIKA_RGB;
|
||
|
layer_type = PIKA_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 == PIKA_PAGE_SELECTOR_TARGET_LAYERS)
|
||
|
{
|
||
|
if (li == 0)
|
||
|
{
|
||
|
first_image_type = image_type;
|
||
|
}
|
||
|
else if (image_type != first_image_type)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((pages.target == PIKA_PAGE_SELECTOR_TARGET_IMAGES) || (! *image))
|
||
|
{
|
||
|
*image = pika_image_new_with_precision (cols, rows, image_type,
|
||
|
image_precision);
|
||
|
|
||
|
if (! *image)
|
||
|
{
|
||
|
TIFFClose (tif);
|
||
|
g_message (_("Could not create a new image: %s"),
|
||
|
pika_pdb_get_last_error (pika_get_pdb ()));
|
||
|
return PIKA_PDB_EXECUTION_ERROR;
|
||
|
}
|
||
|
|
||
|
pika_image_undo_disable (*image);
|
||
|
|
||
|
if (pages.target == PIKA_PAGE_SELECTOR_TARGET_IMAGES)
|
||
|
images_list = g_list_prepend (images_list, *image);
|
||
|
}
|
||
|
|
||
|
/* attach CMYK profile to PikaImage if applicable */
|
||
|
if (profile && pika_color_profile_is_cmyk (profile))
|
||
|
{
|
||
|
pika_image_set_simulation_profile (*image, profile);
|
||
|
g_clear_object (&profile);
|
||
|
}
|
||
|
|
||
|
/* attach non-CMYK color profile */
|
||
|
if (profile)
|
||
|
{
|
||
|
if (pages.target == PIKA_PAGE_SELECTOR_TARGET_IMAGES || profile == first_profile)
|
||
|
pika_image_set_color_profile (*image, profile);
|
||
|
|
||
|
g_object_unref (profile);
|
||
|
}
|
||
|
|
||
|
/* attach parasites */
|
||
|
{
|
||
|
GString *string;
|
||
|
PikaConfigWriter *writer;
|
||
|
PikaParasite *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 = pika_config_writer_new_from_string (string);
|
||
|
|
||
|
pika_config_writer_open (writer, "compression");
|
||
|
pika_config_writer_printf (writer, "%d", pika_compression);
|
||
|
pika_config_writer_close (writer);
|
||
|
|
||
|
pika_config_writer_finish (writer, NULL, NULL);
|
||
|
|
||
|
parasite = pika_parasite_new ("PikaProcedureConfig-file-tiff-save-last",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
string->len + 1, string->str);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
|
||
|
g_string_free (string, TRUE);
|
||
|
|
||
|
/* Attach a parasite containing the image description.
|
||
|
* Pretend to be a pika 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 = pika_parasite_new ("pika-comment",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
strlen (img_desc) + 1, img_desc);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Attach GeoTIFF Tags as Parasite, If available */
|
||
|
{
|
||
|
PikaParasite *parasite = NULL;
|
||
|
void *geotag_data = NULL;
|
||
|
uint32_t count = 0;
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_MODELPIXELSCALE, &count, &geotag_data))
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_ModelPixelScale",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_DOUBLE) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_MODELTIEPOINT, &count, &geotag_data))
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_ModelTiePoint",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_DOUBLE) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_MODELTRANSFORMATION, &count, &geotag_data))
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_ModelTransformation",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_DOUBLE) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_KEYDIRECTORY, &count, &geotag_data) )
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_KeyDirectory",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_SHORT) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_DOUBLEPARAMS, &count, &geotag_data))
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_DoubleParams",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_DOUBLE) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, GEOTIFF_ASCIIPARAMS, &count, &geotag_data))
|
||
|
{
|
||
|
parasite = pika_parasite_new ("Pika_GeoTIFF_Asciiparams",
|
||
|
PIKA_PARASITE_PERSISTENT,
|
||
|
(TIFFDataWidth (TIFF_ASCII) * count),
|
||
|
geotag_data);
|
||
|
pika_image_attach_parasite (*image, parasite);
|
||
|
pika_parasite_free (parasite);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* any resolution info in the file? */
|
||
|
{
|
||
|
gdouble xres = 72.0;
|
||
|
gdouble yres = 72.0;
|
||
|
gushort read_unit;
|
||
|
PikaUnit unit = PIKA_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 = PIKA_UNIT_INCH;
|
||
|
break;
|
||
|
|
||
|
case RESUNIT_CENTIMETER:
|
||
|
xres *= 2.54;
|
||
|
yres *= 2.54;
|
||
|
unit = PIKA_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. Pika will then use the default
|
||
|
* set by the user
|
||
|
*/
|
||
|
if (read_unit != RESUNIT_NONE)
|
||
|
{
|
||
|
if (! isfinite (xres) ||
|
||
|
xres < PIKA_MIN_RESOLUTION || xres > PIKA_MAX_RESOLUTION ||
|
||
|
! isfinite (yres) ||
|
||
|
yres < PIKA_MIN_RESOLUTION || yres > PIKA_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.
|
||
|
*/
|
||
|
pika_image_get_resolution (*image, &xres, &yres);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pika_image_set_resolution (*image, xres, yres);
|
||
|
if (unit != PIKA_UNIT_PIXEL)
|
||
|
pika_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 PIKA
|
||
|
*/
|
||
|
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 == PIKA_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'"),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
return PIKA_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];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_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 PIKA to become unresponsive and/or stuck.
|
||
|
* See m2-d0f86ab189cbe900ec389ca6d7464713.tif from imagetestsuite
|
||
|
*/
|
||
|
g_message (_("Suspicious number of extra channels: %d. Possibly corrupt image."), extra);
|
||
|
extra = 99;
|
||
|
}
|
||
|
|
||
|
/* Allocate ChannelData for all channels, even the background layer */
|
||
|
channel = g_new0 (ChannelData, extra + 1);
|
||
|
|
||
|
/* try and use layer name from tiff file */
|
||
|
name = tiff_get_page_name (tif);
|
||
|
|
||
|
if (name)
|
||
|
{
|
||
|
layer = pika_layer_new (*image, name,
|
||
|
cols, rows,
|
||
|
layer_type,
|
||
|
100,
|
||
|
pika_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 = pika_layer_new (*image, name,
|
||
|
cols, rows,
|
||
|
layer_type,
|
||
|
100,
|
||
|
pika_image_get_default_new_layer_mode (*image));
|
||
|
g_free (name);
|
||
|
}
|
||
|
|
||
|
if (! base_format && image_type == PIKA_INDEXED)
|
||
|
{
|
||
|
/* can't create the palette format here, need to get it from
|
||
|
* an existing layer
|
||
|
*/
|
||
|
base_format = pika_drawable_get_format (PIKA_DRAWABLE (layer));
|
||
|
}
|
||
|
else if (! space)
|
||
|
{
|
||
|
base_format =
|
||
|
babl_format_with_space (babl_format_get_encoding (base_format),
|
||
|
pika_drawable_get_format (PIKA_DRAWABLE (layer)));
|
||
|
}
|
||
|
|
||
|
channel[0].drawable = PIKA_DRAWABLE (layer);
|
||
|
channel[0].buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||
|
channel[0].format = base_format;
|
||
|
|
||
|
if (extra > 0 && ! worst_case)
|
||
|
{
|
||
|
/* Add extra channels as appropriate */
|
||
|
for (i = 1; i <= extra; i++)
|
||
|
{
|
||
|
PikaRGB color;
|
||
|
|
||
|
pika_rgb_set (&color, 0.0, 0.0, 0.0);
|
||
|
|
||
|
channel[i].drawable = PIKA_DRAWABLE (pika_channel_new (*image, _("TIFF Channel"),
|
||
|
cols, rows,
|
||
|
100.0, &color));
|
||
|
pika_image_insert_channel (*image, PIKA_CHANNEL (channel[i].drawable), NULL, 0);
|
||
|
channel[i].buffer = pika_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 PIKA, 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 == PIKA_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)
|
||
|
pika_item_transform_flip_simple (PIKA_ITEM (layer),
|
||
|
PIKA_ORIENTATION_HORIZONTAL,
|
||
|
TRUE /* auto_center */,
|
||
|
-1.0 /* axis */);
|
||
|
|
||
|
if (flip_vertical)
|
||
|
pika_item_transform_flip_simple (PIKA_ITEM (layer),
|
||
|
PIKA_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 != PIKA_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)
|
||
|
{
|
||
|
pika_layer_set_offsets (layer,
|
||
|
layer_offset_x_pixel,
|
||
|
layer_offset_y_pixel);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pika_image_insert_layer (*image, layer, NULL, -1);
|
||
|
|
||
|
if (pages.target == PIKA_PAGE_SELECTOR_TARGET_IMAGES)
|
||
|
{
|
||
|
pika_image_undo_enable (*image);
|
||
|
pika_image_clean_all (*image);
|
||
|
}
|
||
|
|
||
|
pika_progress_update (1.0);
|
||
|
}
|
||
|
g_clear_object (&first_profile);
|
||
|
|
||
|
if (pages.target == PIKA_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))
|
||
|
{
|
||
|
pika_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."),
|
||
|
pika_file_get_utf8_name (file));
|
||
|
|
||
|
return PIKA_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 */
|
||
|
pika_image_resize (*image,
|
||
|
max_col - min_col, max_row - min_row,
|
||
|
-min_col, -min_row);
|
||
|
|
||
|
pika_image_undo_enable (*image);
|
||
|
}
|
||
|
|
||
|
/* Load Photoshop layer metadata */
|
||
|
if (TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &photoshop_len, &photoshop_data))
|
||
|
{
|
||
|
FILE *fp;
|
||
|
GFile *temp_file = NULL;
|
||
|
PikaValueArray *return_vals = NULL;
|
||
|
|
||
|
temp_file = pika_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", pika_file_get_utf8_name (temp_file),
|
||
|
g_strerror (errno));
|
||
|
}
|
||
|
|
||
|
fwrite (photoshop_data, sizeof (guchar), photoshop_len, fp);
|
||
|
fclose (fp);
|
||
|
|
||
|
return_vals =
|
||
|
pika_pdb_run_procedure (pika_get_pdb (),
|
||
|
"file-psd-load-metadata",
|
||
|
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
|
||
|
G_TYPE_FILE, temp_file,
|
||
|
G_TYPE_INT, photoshop_len,
|
||
|
PIKA_TYPE_IMAGE, *image,
|
||
|
G_TYPE_BOOLEAN, FALSE,
|
||
|
G_TYPE_NONE);
|
||
|
|
||
|
g_file_delete (temp_file, NULL, NULL);
|
||
|
g_object_unref (temp_file);
|
||
|
pika_value_array_unref (return_vals);
|
||
|
|
||
|
*ps_metadata_loaded = TRUE;
|
||
|
}
|
||
|
|
||
|
if (TIFFGetField (tif, TIFFTAG_IMAGESOURCEDATA, &photoshop_len, &photoshop_data))
|
||
|
{
|
||
|
FILE *fp;
|
||
|
GFile *temp_file = NULL;
|
||
|
PikaValueArray *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 = pika_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", pika_file_get_utf8_name (temp_file),
|
||
|
g_strerror (errno));
|
||
|
}
|
||
|
|
||
|
fwrite (photoshop_data, sizeof (guchar), photoshop_len, fp);
|
||
|
fclose (fp);
|
||
|
|
||
|
return_vals =
|
||
|
pika_pdb_run_procedure (pika_get_pdb (),
|
||
|
"file-psd-load-metadata",
|
||
|
PIKA_TYPE_RUN_MODE, run_mode,
|
||
|
G_TYPE_FILE, temp_file,
|
||
|
G_TYPE_INT, photoshop_len,
|
||
|
PIKA_TYPE_IMAGE, *image,
|
||
|
G_TYPE_BOOLEAN, TRUE,
|
||
|
G_TYPE_BOOLEAN, is_cmyk,
|
||
|
G_TYPE_NONE);
|
||
|
|
||
|
g_file_delete (temp_file, NULL, NULL);
|
||
|
g_object_unref (temp_file);
|
||
|
pika_value_array_unref (return_vals);
|
||
|
|
||
|
*ps_metadata_loaded = TRUE;
|
||
|
}
|
||
|
|
||
|
g_free (pages.pages);
|
||
|
TIFFClose (tif);
|
||
|
|
||
|
return PIKA_PDB_SUCCESS;
|
||
|
}
|
||
|
|
||
|
static PikaColorProfile *
|
||
|
load_profile (TIFF *tif)
|
||
|
{
|
||
|
PikaColorProfile *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 = pika_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_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)
|
||
|
pika_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 != PIKA_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;
|
||
|
|
||
|
pika_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 == PIKA_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 != PIKA_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;
|
||
|
|
||
|
pika_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 == PIKA_TIFF_GRAY_MINISWHITE && bps == 8)
|
||
|
{
|
||
|
convert_miniswhite (buffer, cols, rows);
|
||
|
}
|
||
|
|
||
|
src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer,
|
||
|
src_format,
|
||
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
||
|
GEGL_AUTO_ROWSTRIDE,
|
||
|
NULL, NULL);
|
||
|
|
||
|
iter = gegl_buffer_iterator_new (src_buf,
|
||
|
GEGL_RECTANGLE (0, 0, cols, rows),
|
||
|
0, NULL,
|
||
|
GEGL_ACCESS_READ,
|
||
|
GEGL_ABYSS_NONE, 2);
|
||
|
gegl_buffer_iterator_add (iter, channel[i].buffer,
|
||
|
GEGL_RECTANGLE (x, y, cols, rows),
|
||
|
0, channel[i].format,
|
||
|
GEGL_ACCESS_READWRITE,
|
||
|
GEGL_ABYSS_NONE);
|
||
|
|
||
|
while (gegl_buffer_iterator_next (iter))
|
||
|
{
|
||
|
guchar *s = iter->items[0].data;
|
||
|
guchar *d = iter->items[1].data;
|
||
|
gint length = iter->length;
|
||
|
|
||
|
d += offset;
|
||
|
|
||
|
while (length--)
|
||
|
{
|
||
|
memcpy (d, s, src_bpp);
|
||
|
d += dest_bpp;
|
||
|
s += src_bpp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
g_object_unref (src_buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
offset += src_bpp;
|
||
|
compindex++;
|
||
|
}
|
||
|
|
||
|
progress += one_row;
|
||
|
}
|
||
|
|
||
|
g_free (buffer);
|
||
|
g_free (bw_buffer);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
fill_bit2byte (TiffColorMode tiff_mode)
|
||
|
{
|
||
|
static gboolean filled = FALSE;
|
||
|
|
||
|
guchar *dest;
|
||
|
gint i, j;
|
||
|
|
||
|
if (filled)
|
||
|
return;
|
||
|
|
||
|
dest = bit2byte;
|
||
|
|
||
|
if (tiff_mode == PIKA_TIFF_INDEXED)
|
||
|
{
|
||
|
for (j = 0; j < 256; j++)
|
||
|
for (i = 7; i >= 0; i--)
|
||
|
{
|
||
|
*(dest++) = ((j & (1 << i)) != 0);
|
||
|
}
|
||
|
}
|
||
|
else if (tiff_mode != PIKA_TIFF_DEFAULT)
|
||
|
{
|
||
|
guchar *_to_8_bitmap = NULL;
|
||
|
|
||
|
if (tiff_mode == PIKA_TIFF_GRAY)
|
||
|
_to_8_bitmap = (guchar *) &_1_to_8_bitmap;
|
||
|
else if (tiff_mode == PIKA_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 == PIKA_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 != PIKA_TIFF_DEFAULT)
|
||
|
{
|
||
|
guchar *_to_8_bitmap = NULL;
|
||
|
|
||
|
if (tiff_mode == PIKA_TIFF_GRAY)
|
||
|
_to_8_bitmap = (guchar *) &_2_to_8_bitmap;
|
||
|
else if (tiff_mode == PIKA_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 == PIKA_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 != PIKA_TIFF_DEFAULT)
|
||
|
{
|
||
|
guchar *_to_8_bitmap = NULL;
|
||
|
|
||
|
if (tiff_mode == PIKA_TIFF_GRAY)
|
||
|
_to_8_bitmap = (guchar *) &_4_to_8_bitmap;
|
||
|
else if (tiff_mode == PIKA_TIFF_GRAY_MINISWHITE)
|
||
|
_to_8_bitmap = (guchar *) &_4_to_8_bitmap_rev;
|
||
|
|
||
|
for (j = 0; j < 256; j++)
|
||
|
{
|
||
|
for (i = 1; i >= 0; i--)
|
||
|
{
|
||
|
gint idx;
|
||
|
|
||
|
idx = ((j & (15 << (4*i))) >> (4*i));
|
||
|
*(dest++) = _to_8_bitmap[idx];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
filled4 = TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
convert_bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height)
|
||
|
{
|
||
|
gint64 x = width * height;
|
||
|
|
||
|
while (x >= 8)
|
||
|
{
|
||
|
memcpy (dest, bit2byte + *src * 8, 8);
|
||
|
dest += 8;
|
||
|
x -= 8;
|
||
|
src++;
|
||
|
}
|
||
|
|
||
|
if (x > 0)
|
||
|
{
|
||
|
memcpy (dest, bit2byte + *src * 8, x);
|
||
|
dest += x;
|
||
|
src++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
convert_2bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height)
|
||
|
{
|
||
|
gint64 x = width * height;
|
||
|
|
||
|
while (x >= 4)
|
||
|
{
|
||
|
memcpy (dest, _2bit2byte + *src * 4, 4);
|
||
|
dest += 4;
|
||
|
x -= 4;
|
||
|
src++;
|
||
|
}
|
||
|
|
||
|
if (x > 0)
|
||
|
{
|
||
|
memcpy (dest, _2bit2byte + *src * 4, x);
|
||
|
dest += x;
|
||
|
src++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
convert_4bit2byte (const guchar *src,
|
||
|
guchar *dest,
|
||
|
gint width,
|
||
|
gint height)
|
||
|
{
|
||
|
gint64 x = width * height;
|
||
|
|
||
|
while (x >= 2)
|
||
|
{
|
||
|
memcpy (dest, _4bit2byte + *src * 2, 2);
|
||
|
dest += 2;
|
||
|
x -= 2;
|
||
|
src++;
|
||
|
}
|
||
|
|
||
|
if (x > 0)
|
||
|
{
|
||
|
memcpy (dest, _4bit2byte + *src * 2, x);
|
||
|
dest += x;
|
||
|
src++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
convert_miniswhite (guchar *buffer,
|
||
|
gint width,
|
||
|
gint height)
|
||
|
{
|
||
|
gint y;
|
||
|
guchar *buf = buffer;
|
||
|
|
||
|
for (y = 0; y < height; y++)
|
||
|
{
|
||
|
gint x;
|
||
|
|
||
|
for (x = 0; x < width; x++)
|
||
|
{
|
||
|
*buf = ~*buf;
|
||
|
buf++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
convert_int2uint (guchar *buffer,
|
||
|
gint bps,
|
||
|
gint spp,
|
||
|
gint width,
|
||
|
gint height,
|
||
|
gint stride)
|
||
|
{
|
||
|
gint bytes_per_pixel = bps / 8;
|
||
|
gint y;
|
||
|
|
||
|
for (y = 0; y < height; y++)
|
||
|
{
|
||
|
guchar *d = buffer + stride * y;
|
||
|
gint x;
|
||
|
|
||
|
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||
|
d += bytes_per_pixel - 1;
|
||
|
#endif
|
||
|
|
||
|
for (x = 0; x < width * spp; x++)
|
||
|
{
|
||
|
*d ^= 0x80;
|
||
|
|
||
|
d += bytes_per_pixel;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static gboolean
|
||
|
load_dialog (const gchar *help_id,
|
||
|
TiffSelectedPages *pages,
|
||
|
const gchar *extra_message,
|
||
|
DefaultExtra *default_extra)
|
||
|
{
|
||
|
GtkWidget *dialog;
|
||
|
GtkWidget *vbox;
|
||
|
GtkWidget *show_reduced = NULL;
|
||
|
GtkWidget *crop_option = NULL;
|
||
|
GtkWidget *extra_radio = NULL;
|
||
|
gboolean run;
|
||
|
|
||
|
pages->selector = NULL;
|
||
|
|
||
|
dialog = pika_dialog_new (_("Import from TIFF"), PLUG_IN_ROLE,
|
||
|
NULL, 0,
|
||
|
pika_standard_help_func, help_id,
|
||
|
|
||
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
||
|
_("_Import"), GTK_RESPONSE_OK,
|
||
|
|
||
|
NULL);
|
||
|
|
||
|
pika_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
||
|
GTK_RESPONSE_OK,
|
||
|
GTK_RESPONSE_CANCEL,
|
||
|
-1);
|
||
|
|
||
|
pika_window_set_transient (GTK_WINDOW (dialog));
|
||
|
|
||
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
|
||
|
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
|
||
|
vbox, TRUE, TRUE, 0);
|
||
|
|
||
|
show_reduced = gtk_check_button_new_with_mnemonic (_("_Show reduced images"));
|
||
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_reduced),
|
||
|
pages->show_reduced);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), show_reduced, TRUE, TRUE, 0);
|
||
|
|
||
|
g_signal_connect (show_reduced, "toggled",
|
||
|
G_CALLBACK (tiff_dialog_show_reduced),
|
||
|
pages);
|
||
|
|
||
|
if (pages->n_pages > 1)
|
||
|
{
|
||
|
/* Page Selector */
|
||
|
pages->selector = pika_page_selector_new ();
|
||
|
gtk_widget_set_size_request (pages->selector, 300, 200);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), pages->selector, TRUE, TRUE, 0);
|
||
|
|
||
|
pika_page_selector_set_n_pages (PIKA_PAGE_SELECTOR (pages->selector),
|
||
|
pages->n_filtered_pages);
|
||
|
pika_page_selector_set_target (PIKA_PAGE_SELECTOR (pages->selector),
|
||
|
pages->target);
|
||
|
|
||
|
/* Load a set number of pages, based on whether "Show Reduced Images"
|
||
|
* is checked
|
||
|
*/
|
||
|
tiff_dialog_show_reduced (show_reduced, pages);
|
||
|
|
||
|
g_signal_connect_swapped (pages->selector, "activate",
|
||
|
G_CALLBACK (gtk_window_activate_default),
|
||
|
dialog);
|
||
|
|
||
|
/* Option to shrink the loaded image to its bounding box
|
||
|
or keep as much empty space as possible.
|
||
|
Note that there seems to be no way to keep the empty
|
||
|
space on the right and bottom. */
|
||
|
crop_option = gtk_check_button_new_with_mnemonic (_("_Keep empty space around imported layers"));
|
||
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (crop_option),
|
||
|
pages->keep_empty_space);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), crop_option, TRUE, TRUE, 0);
|
||
|
}
|
||
|
|
||
|
if (extra_message)
|
||
|
{
|
||
|
GtkWidget *warning;
|
||
|
|
||
|
warning = g_object_new (PIKA_TYPE_HINT_BOX,
|
||
|
"icon-name", PIKA_ICON_DIALOG_WARNING,
|
||
|
"hint", extra_message,
|
||
|
NULL);
|
||
|
gtk_box_pack_start (GTK_BOX (vbox), warning, TRUE, TRUE, 0);
|
||
|
gtk_widget_show (warning);
|
||
|
|
||
|
extra_radio = pika_int_radio_group_new (TRUE, _("Process extra channel as:"),
|
||
|
(GCallback) pika_radio_button_update,
|
||
|
default_extra, NULL, PIKA_TIFF_LOAD_UNASSALPHA,
|
||
|
_("_Non-premultiplied alpha"), PIKA_TIFF_LOAD_UNASSALPHA, NULL,
|
||
|
_("Pre_multiplied alpha"), PIKA_TIFF_LOAD_ASSOCALPHA, NULL,
|
||
|
_("Channe_l"), PIKA_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 = (pika_dialog_run (PIKA_DIALOG (dialog)) == GTK_RESPONSE_OK);
|
||
|
|
||
|
if (run)
|
||
|
{
|
||
|
if (pages->n_pages > 1)
|
||
|
{
|
||
|
pages->target =
|
||
|
pika_page_selector_get_target (PIKA_PAGE_SELECTOR (pages->selector));
|
||
|
|
||
|
pages->pages =
|
||
|
pika_page_selector_get_selected_pages (PIKA_PAGE_SELECTOR (pages->selector),
|
||
|
&pages->n_pages);
|
||
|
|
||
|
pages->keep_empty_space =
|
||
|
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crop_option));
|
||
|
|
||
|
/* select all if none selected */
|
||
|
if (pages->n_pages == 0)
|
||
|
{
|
||
|
pika_page_selector_select_all (PIKA_PAGE_SELECTOR (pages->selector));
|
||
|
|
||
|
pages->pages =
|
||
|
pika_page_selector_get_selected_pages (PIKA_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 */
|
||
|
pika_page_selector_set_n_pages (PIKA_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;
|
||
|
|
||
|
pika_page_selector_set_n_pages (PIKA_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)
|
||
|
pika_page_selector_set_page_label (PIKA_PAGE_SELECTOR (pages->selector),
|
||
|
j, name);
|
||
|
j++;
|
||
|
}
|
||
|
|
||
|
TIFFReadDirectory (pages->tif);
|
||
|
}
|
||
|
}
|