[gimp/gimp-2-10] plug-ins: Add layer support to TIFF writing
- From: Tobias <et src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp/gimp-2-10] plug-ins: Add layer support to TIFF writing
- Date: Tue, 16 Apr 2019 08:18:42 +0000 (UTC)
commit 9335c26b6858d88679fcdeb60f45bb3437d5b950
Author: Tobias Ellinghaus <me houz org>
Date: Tue Apr 16 10:04:16 2019 +0200
plug-ins: Add layer support to TIFF writing
(cherry picked from commit 7584969453ae39a79905f61930ef7a432319fe07)
plug-ins/file-tiff/file-tiff-io.c | 28 ++-
plug-ins/file-tiff/file-tiff-load.c | 113 +++++----
plug-ins/file-tiff/file-tiff-load.h | 1 +
plug-ins/file-tiff/file-tiff-save.c | 466 ++++++++++++++++++++++++++----------
plug-ins/file-tiff/file-tiff-save.h | 18 +-
plug-ins/file-tiff/file-tiff.c | 91 +++----
plug-ins/ui/plug-in-file-tiff.ui | 26 +-
7 files changed, 503 insertions(+), 240 deletions(-)
---
diff --git a/plug-ins/file-tiff/file-tiff-io.c b/plug-ins/file-tiff/file-tiff-io.c
index ebe2d84f4f..62b991b84f 100644
--- a/plug-ins/file-tiff/file-tiff-io.c
+++ b/plug-ins/file-tiff/file-tiff-io.c
@@ -87,7 +87,7 @@ tiff_open (GFile *file,
tiff_io.stream = G_OBJECT (tiff_io.input);
}
- else
+ else if(! strcmp (mode, "w"))
{
tiff_io.output = G_OUTPUT_STREAM (g_file_replace (file,
NULL, FALSE,
@@ -98,6 +98,21 @@ tiff_open (GFile *file,
tiff_io.stream = G_OBJECT (tiff_io.output);
}
+ else if(! strcmp (mode, "a"))
+ {
+ GIOStream *iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL,
+ error));
+ if (! iostream)
+ return NULL;
+
+ tiff_io.input = g_io_stream_get_input_stream (iostream);
+ tiff_io.output = g_io_stream_get_output_stream (iostream);
+ tiff_io.stream = G_OBJECT (iostream);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
#if 0
#warning FIXME !can_seek code is broken
@@ -383,7 +398,7 @@ tiff_io_close (thandle_t handle)
GError *error = NULL;
gboolean closed = FALSE;
- if (io->input)
+ if (io->input && ! io->output)
{
closed = g_input_stream_close (io->input, NULL, &error);
}
@@ -401,7 +416,14 @@ tiff_io_close (thandle_t handle)
}
}
- closed = g_output_stream_close (io->output, NULL, &error);
+ if (io->input)
+ {
+ closed = g_io_stream_close (G_IO_STREAM (io->stream), NULL, &error);
+ }
+ else
+ {
+ closed = g_output_stream_close (io->output, NULL, &error);
+ }
}
if (! closed)
diff --git a/plug-ins/file-tiff/file-tiff-load.c b/plug-ins/file-tiff/file-tiff-load.c
index d71b22224b..123bac68de 100644
--- a/plug-ins/file-tiff/file-tiff-load.c
+++ b/plug-ins/file-tiff/file-tiff-load.c
@@ -98,7 +98,11 @@ static void load_separate (TIFF *tif,
gboolean is_bw,
gint extra);
static void load_paths (TIFF *tif,
- gint image);
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y);
static void fill_bit2byte (void);
static void convert_bit2byte (const guchar *src,
@@ -137,6 +141,7 @@ load_dialog (TIFF *tif,
GtkWidget *dialog;
GtkWidget *vbox;
GtkWidget *selector;
+ GtkWidget *crop_option;
gint i;
gboolean run;
@@ -160,7 +165,6 @@ load_dialog (TIFF *tif,
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);
- gtk_widget_show (vbox);
/* Page Selector */
selector = gimp_page_selector_new ();
@@ -186,10 +190,17 @@ load_dialog (TIFF *tif,
G_CALLBACK (gtk_window_activate_default),
dialog);
- gtk_widget_show (selector);
+ /* 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);
/* Setup done; display the dialog */
- gtk_widget_show (dialog);
+ gtk_widget_show_all (dialog);
/* run the dialog */
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
@@ -203,6 +214,9 @@ load_dialog (TIFF *tif,
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)
{
@@ -900,7 +914,11 @@ load_image (GFile *file,
gimp_image_set_colormap (image, cmap, (1 << bps));
}
- load_paths (tif, image);
+ 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);
@@ -1031,29 +1049,27 @@ load_image (GFile *file,
g_free (channel);
channel = NULL;
-
- /* TODO: in GIMP 2.6, use a dialog to selectively enable the
- * following code, as the save plug-in will then save layer offsets
- * as well.
- */
-
- /* 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)
+ if (pages->target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
{
- gimp_layer_set_offsets (layer,
- layer_offset_x_pixel, layer_offset_y_pixel);
+ /* 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);
@@ -1067,16 +1083,7 @@ load_image (GFile *file,
gimp_progress_update (1.0);
}
- if (pages->target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
- {
- /* 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);
- }
- else
+ if (pages->target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
{
GList *list = images_list;
@@ -1094,6 +1101,22 @@ load_image (GFile *file,
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);
+ }
return image;
}
@@ -1170,18 +1193,17 @@ load_rgba (TIFF *tif,
static void
load_paths (TIFF *tif,
- gint image)
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y)
{
- gint width;
- gint height;
gsize n_bytes;
gchar *bytes;
gint path_index;
gsize pos;
- width = gimp_image_width (image);
- height = gimp_image_height (image);
-
if (! TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &n_bytes, &bytes))
return;
@@ -1305,6 +1327,9 @@ load_paths (TIFF *tif,
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);
@@ -1316,7 +1341,7 @@ load_paths (TIFF *tif,
* first, gimp expects the horizontal
* component first. Sigh.
*/
- points[pointcount * 6 + (j ^ 1)] = f * (j % 2 ? width : height);
+ points[pointcount * 6 + (j ^ 1)] = f * size + offset;
}
pointcount++;
diff --git a/plug-ins/file-tiff/file-tiff-load.h b/plug-ins/file-tiff/file-tiff-load.h
index 372af71f53..956ea43a77 100644
--- a/plug-ins/file-tiff/file-tiff-load.h
+++ b/plug-ins/file-tiff/file-tiff-load.h
@@ -29,6 +29,7 @@ typedef struct
gint n_pages;
gint *pages;
GimpPageSelectorTarget target;
+ gboolean keep_empty_space;
} TiffSelectedPages;
diff --git a/plug-ins/file-tiff/file-tiff-save.c b/plug-ins/file-tiff/file-tiff-save.c
index 79d00a596c..8581725e92 100644
--- a/plug-ins/file-tiff/file-tiff-save.c
+++ b/plug-ins/file-tiff/file-tiff-save.c
@@ -48,6 +48,7 @@
#include <string.h>
#include <tiffio.h>
+#include <gexiv2/gexiv2.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
@@ -62,7 +63,11 @@
static gboolean save_paths (TIFF *tif,
- gint32 image);
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y);
static void comment_entry_callback (GtkWidget *widget,
gchar **comment);
@@ -72,6 +77,10 @@ static void byte2bit (const guchar *byteline,
guchar *bitline,
gboolean invert);
+static void save_thumbnail (TiffSaveVals *tsvals,
+ gint32 image,
+ TIFF *tif);
+
static void
double_to_psd_fixed (gdouble value,
@@ -97,17 +106,18 @@ double_to_psd_fixed (gdouble value,
}
static gboolean
-save_paths (TIFF *tif,
- gint32 image)
+save_paths (TIFF *tif,
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y)
{
gint id = 2000; /* Photoshop paths have IDs >= 2000 */
gint num_vectors, *vectors, v;
gint num_strokes, *strokes, s;
- gdouble width, height;
GString *ps_tag;
- width = gimp_image_width (image);
- height = gimp_image_height (image);
vectors = gimp_image_get_vectors (image, &num_vectors);
if (num_vectors <= 0)
@@ -203,12 +213,12 @@ save_paths (TIFF *tif,
{
pointrecord[1] = closed ? 2 : 5;
- double_to_psd_fixed (points[p+1] / height, pointrecord + 2);
- double_to_psd_fixed (points[p+0] / width, pointrecord + 6);
- double_to_psd_fixed (points[p+3] / height, pointrecord + 10);
- double_to_psd_fixed (points[p+2] / width, pointrecord + 14);
- double_to_psd_fixed (points[p+5] / height, pointrecord + 18);
- double_to_psd_fixed (points[p+4] / width, pointrecord + 22);
+ double_to_psd_fixed ((points[p+1] - offset_y) / height, pointrecord + 2);
+ double_to_psd_fixed ((points[p+0] - offset_x) / width, pointrecord + 6);
+ double_to_psd_fixed ((points[p+3] - offset_y) / height, pointrecord + 10);
+ double_to_psd_fixed ((points[p+2] - offset_x) / width, pointrecord + 14);
+ double_to_psd_fixed ((points[p+5] - offset_y) / height, pointrecord + 18);
+ double_to_psd_fixed ((points[p+4] - offset_x) / width, pointrecord + 22);
g_string_append_len (data, pointrecord, 26);
}
@@ -254,18 +264,19 @@ save_paths (TIFF *tif,
* other special, indirect and consequential damages.
*/
-gboolean
-save_image (GFile *file,
+static gboolean
+save_layer (TIFF *tif,
TiffSaveVals *tsvals,
gint32 image,
gint32 layer,
- gint32 orig_image, /* the export function might have */
- const gchar *image_comment,
+ gint32 page,
+ gint32 num_pages,
+ gint32 orig_image, /* the export function might */
+ /* have created a duplicate */
gint *saved_bpp,
- GError **error) /* created a duplicate */
+ GError **error)
{
gboolean status = FALSE;
- TIFF *tif;
gushort red[256];
gushort grn[256];
gushort blu[256];
@@ -297,11 +308,18 @@ save_image (GFile *file,
gboolean invert = TRUE;
const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 };
const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 };
- gint number_of_sub_IFDs = 1;
- toff_t sub_IFDs_offsets[1] = { 0UL };
+ gchar *layer_name = NULL;
+ const gdouble progress_base = (gdouble) page / (gdouble) num_pages;
+ const gdouble progress_fraction = 1.0 / (gdouble) num_pages;
+ gdouble xresolution;
+ gdouble yresolution;
+ gushort save_unit = RESUNIT_INCH;
+ gint offset_x, offset_y;
compression = tsvals->compression;
+ layer_name = gimp_item_get_name (layer);
+
/* Disabled because this isn't in older releases of libtiff, and it
* wasn't helping much anyway
*/
@@ -314,14 +332,6 @@ save_image (GFile *file,
tile_height = gimp_tile_height ();
rowsperstrip = tile_height;
- gimp_progress_init_printf (_("Exporting '%s'"),
- gimp_file_get_utf8_name (file));
-
-#ifdef TIFFTAG_ICCPROFILE
- if (tsvals->save_profile)
- profile = gimp_image_get_effective_color_profile (orig_image);
-#endif
-
drawable_type = gimp_drawable_type (layer);
buffer = gimp_drawable_get_buffer (layer);
@@ -633,21 +643,14 @@ save_image (GFile *file,
}
}
- tif = tiff_open (file, "w", error);
- if (! tif)
+ /* Set TIFF parameters. */
+ if (num_pages > 1)
{
- if (! error)
- g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
- _("Could not open '%s' for writing: %s"),
- gimp_file_get_utf8_name (file), g_strerror (errno));
- goto out;
+ TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+ TIFFSetField (tif, TIFFTAG_PAGENUMBER, page, num_pages);
}
-
- /* Set TIFF parameters. */
- if (tsvals->save_thumbnail)
- TIFFSetField (tif, TIFFTAG_SUBIFD, number_of_sub_IFDs, sub_IFDs_offsets);
- TIFFSetField (tif, TIFFTAG_SUBFILETYPE, 0);
+ TIFFSetField (tif, TIFFTAG_PAGENAME, layer_name);
TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
@@ -680,105 +683,45 @@ save_image (GFile *file,
}
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
- TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, g_file_get_path (file));
TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
/* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
/* resolution fields */
- {
- gdouble xresolution;
- gdouble yresolution;
- gushort save_unit = RESUNIT_INCH;
-
- gimp_image_get_resolution (orig_image, &xresolution, &yresolution);
-
- if (gimp_unit_is_metric (gimp_image_get_unit (orig_image)))
- {
- save_unit = RESUNIT_CENTIMETER;
- xresolution /= 2.54;
- yresolution /= 2.54;
- }
-
- if (xresolution > 1e-5 && yresolution > 1e-5)
- {
- TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
- TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
- TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
- }
+ gimp_image_get_resolution (orig_image, &xresolution, &yresolution);
-#if 0
- /* TODO: enable in 2.6 */
-
- gint offset_x, offset_y;
-
- gimp_drawable_offsets (layer, &offset_x, &offset_y);
-
- if (offset_x || offset_y)
- {
- TIFFSetField (tif, TIFFTAG_XPOSITION, offset_x / xresolution);
- TIFFSetField (tif, TIFFTAG_YPOSITION, offset_y / yresolution);
- }
-#endif
- }
-
- /* The TIFF spec explicitly says ASCII for the image description. */
- if (image_comment)
+ if (gimp_unit_is_metric (gimp_image_get_unit (orig_image)))
{
- const gchar *c = image_comment;
- gint len;
-
- for (len = strlen (c); len; c++, len--)
- {
- if ((guchar) *c > 127)
- {
- g_message (_("The TIFF format only supports comments in\n"
- "7bit ASCII encoding. No comment is saved."));
- image_comment = NULL;
-
- break;
- }
- }
+ save_unit = RESUNIT_CENTIMETER;
+ xresolution /= 2.54;
+ yresolution /= 2.54;
}
- /* do we have a comment? If so, create a new parasite to hold it,
- * and attach it to the image. The attach function automatically
- * detaches a previous incarnation of the parasite. */
- if (image_comment && *image_comment)
+ if (xresolution > 1e-5 && yresolution > 1e-5)
{
- GimpParasite *parasite;
-
- TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, image_comment);
- parasite = gimp_parasite_new ("gimp-comment",
- GIMP_PARASITE_PERSISTENT,
- strlen (image_comment) + 1, image_comment);
- gimp_image_attach_parasite (orig_image, parasite);
- gimp_parasite_free (parasite);
+ TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
+ TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
+ TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
}
- /* do we have an ICC profile? If so, write it to the TIFF file */
-#ifdef TIFFTAG_ICCPROFILE
- if (profile)
- {
- const guint8 *icc_data;
- gsize icc_length;
-
- icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ gimp_drawable_offsets (layer, &offset_x, &offset_y);
- TIFFSetField (tif, TIFFTAG_ICCPROFILE, icc_length, icc_data);
-
- g_object_unref (profile);
+ if (offset_x || offset_y)
+ {
+ TIFFSetField (tif, TIFFTAG_XPOSITION, offset_x / xresolution);
+ TIFFSetField (tif, TIFFTAG_YPOSITION, offset_y / yresolution);
}
-#endif
-
- /* save path data */
- save_paths (tif, orig_image);
if (! is_bw &&
(drawable_type == GIMP_INDEXED_IMAGE || drawable_type == GIMP_INDEXEDA_IMAGE))
TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu);
+ /* save path data. we need layer information for that,
+ * so we have to do this in here. :-( */
+ if (page == 0)
+ save_paths (tif, orig_image, cols, rows, offset_x, offset_y);
+
/* array to rearrange data */
src = g_new (guchar, bytesperrow * tile_height);
data = g_new (guchar, bytesperrow);
@@ -833,11 +776,32 @@ save_image (GFile *file,
}
if ((row % 32) == 0)
- gimp_progress_update ((gdouble) row / (gdouble) rows);
+ gimp_progress_update (progress_base + progress_fraction
+ * (gdouble) row / (gdouble) rows);
}
TIFFWriteDirectory (tif);
+ gimp_progress_update (progress_base + progress_fraction);
+
+ status = TRUE;
+
+out:
+ if (buffer)
+ g_object_unref (buffer);
+
+ g_free (data);
+ g_free (src);
+ g_free (layer_name);
+
+ return status;
+}
+
+static void
+save_thumbnail (TiffSaveVals *tsvals,
+ gint32 image,
+ TIFF *tif)
+{
/* now switch IFD and write thumbnail
*
* the thumbnail must be saved in a subimage of the image.
@@ -913,21 +877,262 @@ save_image (GFile *file,
g_object_unref (thumb_pixbuf);
}
+}
+
+static void
+save_metadata (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gint saved_bpp)
+{
+ gchar **exif_tags;
+
+ /* See bug 758909: clear TIFFTAG_MIN/MAXSAMPLEVALUE because
+ * exiv2 saves them with wrong type and the original values
+ * could be invalid, see also bug 761823.
+ * we also clear some other tags that were only meaningful
+ * for the original imported image.
+ */
+ static const gchar *exif_tags_to_remove[] = {
+ "Exif.Image.0x0118",
+ "Exif.Image.0x0119",
+ "Exif.Image.0x011d",
+ "Exif.Image.Compression",
+ "Exif.Image.FillOrder",
+ "Exif.Image.NewSubfileType",
+ "Exif.Image.PageNumber",
+ "Exif.Image.PhotometricInterpretation",
+ "Exif.Image.PlanarConfiguration",
+ "Exif.Image.Predictor",
+ "Exif.Image.RowsPerStrip",
+ "Exif.Image.SampleFormat",
+ "Exif.Image.SamplesPerPixel",
+ "Exif.Image.StripByteCounts",
+ "Exif.Image.StripOffsets"
+ };
+ static const guint n_keys = G_N_ELEMENTS(exif_tags_to_remove);
+
+ for (int k = 0; k < n_keys; k++)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
+ exif_tags_to_remove[k]);
+ }
+
+ /* get rid of all the EXIF tags for anything but the first sub image. */
+ exif_tags = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA(metadata));
+ for (char **tag = exif_tags; *tag; tag++)
+ {
+ if (g_str_has_prefix (*tag, "Exif.Image")
+ && (*tag)[strlen ("Exif.Image")] >= '0'
+ && (*tag)[strlen ("Exif.Image")] <= '9')
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ }
+ gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
+
+ if (tsvals->save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (tsvals->save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (tsvals->save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ /* never save metadata thumbnails for TIFF, see bug #729952 */
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (tsvals->save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ gimp_image_metadata_save_finish (image,
+ "image/tiff",
+ metadata, metadata_flags,
+ file, NULL);
+}
+
+gboolean
+save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image, /* the export function */
+ /* might have created */
+ /* a duplicate */
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error)
+{
+ gboolean status = FALSE;
+ TIFF *tif;
+ GimpColorProfile *profile = NULL;
+ gint number_of_sub_IFDs = 1;
+ toff_t sub_IFDs_offsets[1] = { 0UL };
+ gint32 num_layers, *layers, current_layer = 0;
+
+ layers = gimp_image_get_layers (image, &num_layers);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ /* Open file and write some gloabl data */
+ tif = tiff_open (file, "w", error);
+
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file), g_strerror (errno));
+ goto out;
+ }
+
+ TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, g_file_get_path (file));
+
+ /* The TIFF spec explicitly says ASCII for the image description. */
+ if (image_comment)
+ {
+ const gchar *c = image_comment;
+ gint len;
+
+ for (len = strlen (c); len; c++, len--)
+ {
+ if ((guchar) *c > 127)
+ {
+ g_message (_("The TIFF format only supports comments in\n"
+ "7bit ASCII encoding. No comment is saved."));
+ image_comment = NULL;
+
+ break;
+ }
+ }
+ }
+
+ /* do we have a comment? If so, create a new parasite to hold it,
+ * and attach it to the image. The attach function automatically
+ * detaches a previous incarnation of the parasite. */
+ if (image_comment && *image_comment)
+ {
+ GimpParasite *parasite;
+
+ TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, image_comment);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (image_comment) + 1, image_comment);
+ gimp_image_attach_parasite (orig_image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ /* do we have an ICC profile? If so, write it to the TIFF file */
+#ifdef TIFFTAG_ICCPROFILE
+ if (tsvals->save_profile)
+ {
+ profile = gimp_image_get_effective_color_profile (orig_image);
+
+ if (profile)
+ {
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+
+ TIFFSetField (tif, TIFFTAG_ICCPROFILE, icc_length, icc_data);
+
+ g_object_unref (profile);
+ }
+ }
+#endif
+
+ /* we put the whole file's thumbnail into the first IFD (i.e., page) */
+ if (tsvals->save_thumbnail)
+ TIFFSetField (tif, TIFFTAG_SUBIFD, number_of_sub_IFDs, sub_IFDs_offsets);
+
+ /* write last layer as first page. */
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers,
+ orig_image, saved_bpp, error))
+ {
+ goto out;
+ }
+ current_layer++;
+
+ /* write thumbnail */
+ if (tsvals->save_thumbnail)
+ save_thumbnail (tsvals, image, tif);
+
+ /* close file so we can savely let exiv2 work on it to write metadata.
+ * this can be simplified once multi page TIFF is supported by exiv2
+ */
TIFFFlushData (tif);
TIFFClose (tif);
+ tif = NULL;
+ if (metadata)
+ save_metadata (file, tsvals, image, metadata, metadata_flags, *saved_bpp);
- gimp_progress_update (1.0);
+ /* write the remaining layers */
+ if (num_layers > 1)
+ {
+ tif = tiff_open (file, "a", error);
- status = TRUE;
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file),
+ g_strerror (errno));
+ goto out;
+ }
- out:
- if (buffer)
- g_object_unref (buffer);
+ for (; current_layer < num_layers; current_layer++)
+ {
+ gint tmp_saved_bpp;
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers, orig_image,
+ &tmp_saved_bpp, error))
+ {
+ goto out;
+ }
+ if (tmp_saved_bpp != *saved_bpp)
+ {
+ /* this should never happen.
+ * if it does, decide if it's really an error.
+ */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Writing pages with different bit depth "
+ "is strange."));
+ goto out;
+ }
+ gimp_progress_update ((gdouble) (current_layer + 1) / num_layers);
+ }
+ }
- g_free (data);
- g_free (src);
+ /* close the file for good */
+ if (tif)
+ {
+ TIFFFlushData (tif);
+ TIFFClose (tif);
+ }
+
+ gimp_progress_update (1.0);
+ status = TRUE;
+
+out:
return status;
}
@@ -1069,6 +1274,13 @@ save_dialog (TiffSaveVals *tsvals,
gtk_widget_hide (toggle);
#endif
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_layers);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_layers);
+
gtk_widget_show (dialog);
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
diff --git a/plug-ins/file-tiff/file-tiff-save.h b/plug-ins/file-tiff/file-tiff-save.h
index 04c6ddc402..24e4f85ba1 100644
--- a/plug-ins/file-tiff/file-tiff-save.h
+++ b/plug-ins/file-tiff/file-tiff-save.h
@@ -33,17 +33,19 @@ typedef struct
gboolean save_iptc;
gboolean save_thumbnail;
gboolean save_profile;
+ gboolean save_layers;
} TiffSaveVals;
-gboolean save_image (GFile *file,
- TiffSaveVals *tsvals,
- gint32 image,
- gint32 drawable,
- gint32 orig_image,
- const gchar *image_comment,
- gint *saved_bpp,
- GError **error);
+gboolean save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image,
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error);
gboolean save_dialog (TiffSaveVals *tsvals,
const gchar *help_id,
diff --git a/plug-ins/file-tiff/file-tiff.c b/plug-ins/file-tiff/file-tiff.c
index 3c50a00567..dbaa6a153d 100644
--- a/plug-ins/file-tiff/file-tiff.c
+++ b/plug-ins/file-tiff/file-tiff.c
@@ -45,7 +45,6 @@
#include "config.h"
#include <tiffio.h>
-#include <gexiv2/gexiv2.h>
#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>
@@ -90,7 +89,8 @@ static TiffSaveVals tsvals =
FALSE, /* save xmp */
FALSE, /* save iptc */
TRUE, /* save thumbnail */
- TRUE /* save profile */
+ TRUE, /* save profile */
+ TRUE /* save layer */
};
static gchar *image_comment = NULL;
@@ -221,7 +221,12 @@ run (const gchar *name,
pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
- gimp_get_data (LOAD_PROC, &pages.target);
+ 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);
@@ -268,9 +273,13 @@ run (const gchar *name,
gint32 image;
gboolean resolution_loaded = FALSE;
- gimp_set_data (LOAD_PROC,
+ 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));
+
image = load_image (file, tif, &pages,
&resolution_loaded,
&error);
@@ -347,7 +356,8 @@ run (const gchar *name,
GIMP_EXPORT_CAN_HANDLE_RGB |
GIMP_EXPORT_CAN_HANDLE_GRAY |
GIMP_EXPORT_CAN_HANDLE_INDEXED |
- GIMP_EXPORT_CAN_HANDLE_ALPHA);
+ GIMP_EXPORT_CAN_HANDLE_ALPHA |
+ GIMP_EXPORT_CAN_HANDLE_LAYERS);
if (export == GIMP_EXPORT_CANCEL)
{
@@ -462,55 +472,32 @@ run (const gchar *name,
file = g_file_new_for_uri (param[3].data.d_string);
- if (save_image (file, &tsvals,
- image, drawable, orig_image, image_comment,
- &saved_bpp, &error))
+ /* saving with layers is not supporting blend modes, so people might
+ * prefer to save a flat copy. */
+ if (! tsvals.save_layers)
{
- if (metadata)
- {
-
- /* See bug 758909: clear TIFFTAG_MIN/MAXSAMPLEVALUE because
- * exiv2 saves them with wrong type and the original values
- * could be invalid, see also bug 761823
- */
- gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
- "Exif.Image.0x0118");
- gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
- "Exif.Image.0x0119");
- gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
- "Exif.Image.PageNumber");
-
- gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
-
- if (tsvals.save_exif)
- metadata_flags |= GIMP_METADATA_SAVE_EXIF;
- else
- metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
-
- if (tsvals.save_xmp)
- metadata_flags |= GIMP_METADATA_SAVE_XMP;
- else
- metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
-
- if (tsvals.save_iptc)
- metadata_flags |= GIMP_METADATA_SAVE_IPTC;
- else
- metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
-
- /* never save metadata thumbnails for TIFF, see bug #729952 */
- metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
-
- if (tsvals.save_profile)
- metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
- else
- metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
-
- gimp_image_metadata_save_finish (image,
- "image/tiff",
- metadata, metadata_flags,
- file, NULL);
- }
+ gint32 transp;
+
+ /* FIXME: Do we have to update drawable, too? */
+ image = gimp_image_duplicate (image);
+
+ /* borrowed from ./libgimp/gimpexport.c:export_merge()
+ * this makes sure that the exported file size is correct. */
+ transp = gimp_layer_new (image, "-",
+ gimp_image_width (image),
+ gimp_image_height (image),
+ gimp_drawable_type (drawable) | 1,
+ 100.0, GIMP_LAYER_MODE_NORMAL);
+ gimp_image_insert_layer (image, transp, -1, 1);
+ gimp_selection_none (image);
+ gimp_drawable_edit_clear (transp);
+
+ gimp_image_merge_visible_layers (image, GIMP_CLIP_TO_IMAGE);
+ }
+ if (save_image (file, &tsvals, image, orig_image, image_comment,
+ &saved_bpp, metadata, metadata_flags, &error))
+ {
/* Store mvals data */
gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals));
}
diff --git a/plug-ins/ui/plug-in-file-tiff.ui b/plug-ins/ui/plug-in-file-tiff.ui
index c1dec4e4d3..f8c19f2a64 100644
--- a/plug-ins/ui/plug-in-file-tiff.ui
+++ b/plug-ins/ui/plug-in-file-tiff.ui
@@ -151,6 +151,20 @@
<property name="position">1</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="save-iptc">
+ <property name="label" translatable="yes">Save IPTC data</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">True</property>
@@ -163,8 +177,8 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
- <object class="GtkCheckButton" id="save-iptc">
- <property name="label" translatable="yes">Save IPTC data</property>
+ <object class="GtkCheckButton" id="save-thumbnail">
+ <property name="label" translatable="yes">Save thumbnail</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -177,8 +191,8 @@
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="save-thumbnail">
- <property name="label" translatable="yes">Save thumbnail</property>
+ <object class="GtkCheckButton" id="save-color-profile">
+ <property name="label" translatable="yes">Save color profile</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
@@ -191,8 +205,8 @@
</packing>
</child>
<child>
- <object class="GtkCheckButton" id="save-color-profile">
- <property name="label" translatable="yes">Save color profile</property>
+ <object class="GtkCheckButton" id="save-layers">
+ <property name="label" translatable="yes">Save layers</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]