/* tiff loading for GIMP * -Peter Mattis * * The TIFF loading code has been completely revamped by Nick Lamb * njl195@zepler.org.uk -- 18 May 1998 * And it now gains support for tiles (and doubtless a zillion bugs) * njl195@zepler.org.uk -- 12 June 1999 * LZW patent fuss continues :( * njl195@zepler.org.uk -- 20 April 2000 * The code for this filter is based on "tifftopnm" and "pnmtotiff", * 2 programs that are a part of the netpbm package. * khk@khk.net -- 13 May 2000 * Added support for ICCPROFILE tiff tag. If this tag is present in a * TIFF file, then a parasite is created and vice versa. * peter@kirchgessner.net -- 29 Oct 2002 * Progress bar only when run interactive * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004 * Honor EXTRASAMPLES tag while loading images with alphachannel * pablo.dangelo@web.de -- 16 Jan 2004 */ /* * tifftopnm.c - converts a Tagged Image File to a portable anymap * * Derived by Jef Poskanzer from tif2ras.c, which is: * * Copyright (c) 1990 by Sun Microsystems, Inc. * * Author: Patrick J. Naughton * naughton@wind.sun.com * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. */ #include "config.h" #include #include #include #include "file-tiff-io.h" #include "file-tiff-load.h" #include "file-tiff-save.h" #include "libgimp/stdplugins-intl.h" #define SAVE_PROC "file-tiff-save" #define SAVE2_PROC "file-tiff-save2" #define SAVE_BIGTIFF_PROC "file-bigtiff-save" #define PLUG_IN_BINARY "file-tiff" static void query (void); static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals); static GimpPDBStatusType tiff_save_rec (GimpRunMode run_mode, gint32 orig_image, gint32 orig_drawable, GFile *file, GimpMetadata *metadata, GimpMetadataSaveFlags metadata_flags, gboolean retried, GError **error); static gboolean image_is_monochrome (gint32 image); const GimpPlugInInfo PLUG_IN_INFO = { NULL, /* init_proc */ NULL, /* quit_proc */ query, /* query_proc */ run, /* run_proc */ }; static TiffSaveVals tsvals = { COMPRESSION_NONE, /* compression */ TRUE, /* alpha handling */ FALSE, /* save transp. pixels */ FALSE, /* save exif */ FALSE, /* save xmp */ FALSE, /* save iptc */ TRUE, /* save geotiff */ TRUE, /* save thumbnail */ TRUE, /* save profile */ TRUE, /* save layers */ TRUE, /* crop layers */ FALSE /* save BigTIFF */ }; static gchar *image_comment = NULL; MAIN () static void query (void) { static const GimpParamDef load_args[] = { { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, { GIMP_PDB_STRING, "filename", "The name of the file to load" }, { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" } }; static const GimpParamDef load_return_vals[] = { { GIMP_PDB_IMAGE, "image", "Output image" } }; #define COMMON_SAVE_ARGS \ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },\ { GIMP_PDB_IMAGE, "image", "Input image" },\ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },\ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },\ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },\ { GIMP_PDB_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3), JPEG (4), CCITT G3 Fax (5), CCITT G4 Fax (6) }" } static const GimpParamDef save_args_old[] = { COMMON_SAVE_ARGS }; static const GimpParamDef save_args[] = { COMMON_SAVE_ARGS, { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" } }; static const GimpParamDef save_bigtiff_args[] = { COMMON_SAVE_ARGS, { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" }, { GIMP_PDB_INT32, "bigtiff", "Export in BigTIFF variant file format" } }; gimp_install_procedure (LOAD_PROC, #ifdef TIFF_VERSION_BIG "Loads files of the TIFF and BigTIFF file formats", "Loads files of the Tag Image File Format (TIFF) and " "its 64-bits offsets variant (BigTIFF)", #else "Loads files of the TIFF file format", "Loads files of the Tag Image File Format (TIFF)", #endif "Spencer Kimball, Peter Mattis & Nick Lamb", "Nick Lamb ", "1995-1996,1998-2003", N_("TIFF image"), NULL, GIMP_PLUGIN, G_N_ELEMENTS (load_args), G_N_ELEMENTS (load_return_vals), load_args, load_return_vals); gimp_register_file_handler_mime (LOAD_PROC, "image/tiff"); gimp_register_file_handler_uri (LOAD_PROC); gimp_register_magic_load_handler (LOAD_PROC, "tif,tiff", "", "0,string,II*\\0,0,string,MM\\0*"); gimp_install_procedure (SAVE_PROC, "Exports files in the TIFF file format", "Exports files in the Tagged Image File Format. " "The value for the saved comment is taken " "from the 'gimp-comment' parasite.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996,2000-2003", N_("TIFF image"), "RGB*, GRAY*, INDEXED*", GIMP_PLUGIN, G_N_ELEMENTS (save_args_old), 0, save_args_old, NULL); gimp_register_file_handler_mime (SAVE_PROC, "image/tiff"); gimp_register_file_handler_uri (SAVE_PROC); gimp_register_save_handler (SAVE_PROC, "tif,tiff", ""); gimp_install_procedure (SAVE2_PROC, "Exports files in the TIFF file format", "Exports files in the Tagged Image File Format. " "The value for the saved comment is taken " "from the 'gimp-comment' parasite.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996,2000-2003", N_("TIFF image"), "RGB*, GRAY*, INDEXED*", GIMP_PLUGIN, G_N_ELEMENTS (save_args), 0, save_args, NULL); #ifdef TIFF_VERSION_BIG gimp_install_procedure (SAVE_BIGTIFF_PROC, "Exports files in the TIFF or BigTIFF file format", "Exports files in the Tagged Image File Format or " "its 64-bit offsets variant (BigTIFF) able to support " "much bigger file sizes. " "The value for the saved comment is taken " "from the 'gimp-comment' parasite.", "Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis", "1995-1996,2000-2003", N_("TIFF image"), "RGB*, GRAY*, INDEXED*", GIMP_PLUGIN, G_N_ELEMENTS (save_bigtiff_args), 0, save_bigtiff_args, NULL); gimp_register_file_handler_mime (SAVE_PROC, "image/tiff"); gimp_register_file_handler_uri (SAVE_PROC); gimp_register_save_handler (SAVE_PROC, "tif,tiff", ""); #endif } static void run (const gchar *name, gint nparams, const GimpParam *param, gint *nreturn_vals, GimpParam **return_vals) { static GimpParam values[2]; GimpRunMode run_mode; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GError *error = NULL; INIT_I18N (); gegl_init (NULL, NULL); run_mode = param[0].data.d_int32; *nreturn_vals = 1; *return_vals = values; values[0].type = GIMP_PDB_STATUS; values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR; if (strcmp (name, LOAD_PROC) == 0) { GFile *file = g_file_new_for_uri (param[1].data.d_string); gint32 image = 0; gboolean resolution_loaded = FALSE; gboolean profile_loaded = FALSE; if (run_mode == GIMP_RUN_INTERACTIVE) gimp_ui_init (PLUG_IN_BINARY, FALSE); status = load_image (file, run_mode, &image, &resolution_loaded, &profile_loaded, &error); if (image > 0) { GimpMetadata *metadata; metadata = gimp_image_metadata_load_prepare (image, "image/tiff", file, NULL); if (metadata) { GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL; if (resolution_loaded) flags &= ~GIMP_METADATA_LOAD_RESOLUTION; if (profile_loaded) flags &= ~GIMP_METADATA_LOAD_COLORSPACE; gimp_image_metadata_load_finish (image, "image/tiff", metadata, flags, run_mode == GIMP_RUN_INTERACTIVE); g_object_unref (metadata); } *nreturn_vals = 2; values[1].type = GIMP_PDB_IMAGE; values[1].data.d_image = image; } g_object_unref (file); } else if ((strcmp (name, SAVE_PROC) == 0) || (strcmp (name, SAVE2_PROC) == 0) || (strcmp (name, SAVE_BIGTIFF_PROC) == 0)) { /* Plug-in is either file_tiff_save or file_tiff_save2 */ GFile *file; GimpMetadata *metadata; GimpMetadataSaveFlags metadata_flags; GimpParasite *parasite; gint32 image = param[1].data.d_int32; gint32 drawable = param[2].data.d_int32; gint32 orig_image = image; switch (run_mode) { case GIMP_RUN_INTERACTIVE: case GIMP_RUN_WITH_LAST_VALS: gimp_ui_init (PLUG_IN_BINARY, FALSE); break; default: break; } /* Override the defaults with preferences. */ metadata = gimp_image_metadata_save_prepare (orig_image, "image/tiff", &metadata_flags); tsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0; tsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0; tsvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0; tsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0; tsvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0; tsvals.save_geotiff = TRUE; tsvals.bigtiff = FALSE; #ifdef TIFF_VERSION_BIG if (nparams >= 8) tsvals.bigtiff = param[7].data.d_int32; #endif parasite = gimp_image_get_parasite (orig_image, "gimp-comment"); if (parasite) { image_comment = g_strndup (gimp_parasite_data (parasite), gimp_parasite_data_size (parasite)); gimp_parasite_free (parasite); } switch (run_mode) { case GIMP_RUN_INTERACTIVE: /* Possibly retrieve data */ gimp_get_data (name, &tsvals); parasite = gimp_image_get_parasite (orig_image, "tiff-save-options"); if (parasite) { const TiffSaveVals *pvals = gimp_parasite_data (parasite); if (pvals->compression == COMPRESSION_DEFLATE) tsvals.compression = COMPRESSION_ADOBE_DEFLATE; else tsvals.compression = pvals->compression; tsvals.save_transp_pixels = pvals->save_transp_pixels; } gimp_parasite_free (parasite); break; case GIMP_RUN_NONINTERACTIVE: /* Make sure all the arguments are there! */ if (nparams == 6 || nparams == 7 || nparams == 8) { switch (param[5].data.d_int32) { case 0: tsvals.compression = COMPRESSION_NONE; break; case 1: tsvals.compression = COMPRESSION_LZW; break; case 2: tsvals.compression = COMPRESSION_PACKBITS; break; case 3: tsvals.compression = COMPRESSION_ADOBE_DEFLATE; break; case 4: tsvals.compression = COMPRESSION_JPEG; break; case 5: tsvals.compression = COMPRESSION_CCITTFAX3; break; case 6: tsvals.compression = COMPRESSION_CCITTFAX4; break; default: status = GIMP_PDB_CALLING_ERROR; break; } if (nparams >= 7) tsvals.save_transp_pixels = param[6].data.d_int32; else tsvals.save_transp_pixels = TRUE; if (nparams == 8) tsvals.bigtiff = param[7].data.d_int32; else tsvals.bigtiff = FALSE; } else { status = GIMP_PDB_CALLING_ERROR; } break; case GIMP_RUN_WITH_LAST_VALS: /* Possibly retrieve data */ gimp_get_data (SAVE_PROC, &tsvals); parasite = gimp_image_get_parasite (orig_image, "tiff-save-options"); if (parasite) { const TiffSaveVals *pvals = gimp_parasite_data (parasite); tsvals.compression = pvals->compression; tsvals.save_transp_pixels = pvals->save_transp_pixels; } gimp_parasite_free (parasite); break; default: break; } file = g_file_new_for_uri (param[3].data.d_string); status = tiff_save_rec (run_mode, orig_image, drawable, file, metadata, metadata_flags, FALSE, &error); if (metadata) g_object_unref (metadata); } else { status = GIMP_PDB_CALLING_ERROR; } if (status != GIMP_PDB_SUCCESS && error) { *nreturn_vals = 2; values[1].type = GIMP_PDB_STRING; values[1].data.d_string = error->message; } values[0].data.d_status = status; } static gboolean image_is_monochrome (gint32 image) { guchar *colors; gint num_colors; gboolean monochrome = FALSE; g_return_val_if_fail (image != -1, FALSE); colors = gimp_image_get_colormap (image, &num_colors); if (colors) { if (num_colors == 2 || num_colors == 1) { const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 }; const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 }; if (memcmp (colors, bw_map, 3 * num_colors) == 0 || memcmp (colors, wb_map, 3 * num_colors) == 0) { monochrome = TRUE; } } g_free (colors); } return monochrome; } static GimpPDBStatusType tiff_save_rec (GimpRunMode run_mode, gint32 orig_image, gint32 orig_drawable, GFile *file, GimpMetadata *metadata, GimpMetadataSaveFlags metadata_flags, gboolean retried, GError **error) { gint32 image = orig_image; gint32 drawable = orig_drawable; GimpPDBStatusType status = GIMP_PDB_SUCCESS; GimpExportReturn export = GIMP_EXPORT_CANCEL; gboolean bigtiff = FALSE; if (run_mode == GIMP_RUN_INTERACTIVE) { if (! save_dialog (&tsvals, image, SAVE_PROC, gimp_drawable_has_alpha (drawable), image_is_monochrome (image), gimp_image_base_type (image) == GIMP_INDEXED, &image_comment, error, retried)) { return GIMP_PDB_CANCEL; } } switch (run_mode) { case GIMP_RUN_INTERACTIVE: case GIMP_RUN_WITH_LAST_VALS: { GimpExportCapabilities capabilities; if (tsvals.compression == COMPRESSION_CCITTFAX3 || tsvals.compression == COMPRESSION_CCITTFAX4) { /* G3/G4 are fax compressions. They only support * monochrome images without alpha support. */ capabilities = GIMP_EXPORT_CAN_HANDLE_INDEXED; } else { capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB | GIMP_EXPORT_CAN_HANDLE_GRAY | GIMP_EXPORT_CAN_HANDLE_INDEXED | GIMP_EXPORT_CAN_HANDLE_ALPHA); } if (tsvals.save_layers) { capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS; if (tsvals.crop_layers) capabilities |= GIMP_EXPORT_NEEDS_CROP; } export = gimp_export_image (&image, &drawable, "TIFF", capabilities); if (export == GIMP_EXPORT_CANCEL) return GIMP_PDB_CANCEL; } break; default: break; } if (status == GIMP_PDB_SUCCESS) { gint saved_bpp; 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)); } else { status = GIMP_PDB_EXECUTION_ERROR; } } if (export == GIMP_EXPORT_EXPORT) { gimp_image_delete (image); } #ifdef TIFF_VERSION_BIG if (status == GIMP_PDB_EXECUTION_ERROR && run_mode == GIMP_RUN_INTERACTIVE && ! retried && ! bigtiff && tiff_got_file_size_error ()) { /* Retrying but just once, when the save failed because we exceeded * TIFF max size, to propose BigTIFF instead. */ tiff_reset_file_size_error (); g_clear_error (error); return tiff_save_rec (run_mode, orig_image, orig_drawable, file, metadata, metadata_flags, TRUE, error); } #endif return status; }