1997-11-25 06:05:25 +08:00
|
|
|
/* tiff loading and saving for the GIMP
|
|
|
|
* -Peter Mattis
|
1998-05-18 08:54:11 +08:00
|
|
|
* The TIFF loading code has been completely revamped by Nick Lamb
|
|
|
|
* njl195@zepler.org.uk -- 18 May 1998
|
1999-09-18 06:28:25 +08:00
|
|
|
* And it now gains support for tiles (and doubtless a zillion bugs)
|
|
|
|
* njl195@zepler.org.uk -- 12 June 1999
|
1997-11-25 06:05:25 +08:00
|
|
|
* The code for this filter is based on "tifftopnm" and "pnmtotiff",
|
|
|
|
* 2 programs that are a part of the netpbm package.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
** 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 <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <tiffio.h>
|
|
|
|
#include "libgimp/gimp.h"
|
1999-10-04 02:54:54 +08:00
|
|
|
#include "libgimp/gimpui.h"
|
1999-03-17 04:14:07 +08:00
|
|
|
#ifdef GIMP_HAVE_RESOLUTION_INFO
|
|
|
|
#include "libgimp/gimpunit.h"
|
|
|
|
#endif
|
1998-10-12 06:50:03 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gint compression;
|
|
|
|
gint fillorder;
|
|
|
|
} TiffSaveVals;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
gint run;
|
|
|
|
} TiffSaveInterface;
|
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
typedef struct {
|
|
|
|
gint32 ID;
|
|
|
|
GDrawable *drawable;
|
|
|
|
GPixelRgn pixel_rgn;
|
|
|
|
guchar *pixels;
|
|
|
|
guchar *pixel;
|
|
|
|
} channel_data;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Declare some local functions.
|
|
|
|
*/
|
|
|
|
static void query (void);
|
|
|
|
static void run (char *name,
|
|
|
|
int nparams,
|
|
|
|
GParam *param,
|
|
|
|
int *nreturn_vals,
|
|
|
|
GParam **return_vals);
|
|
|
|
static gint32 load_image (char *filename);
|
1999-09-18 06:28:25 +08:00
|
|
|
|
|
|
|
static void load_rgba (TIFF *tif, channel_data *channel);
|
|
|
|
static void load_lines (TIFF *tif, channel_data *channel,
|
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int alpha, int extra);
|
|
|
|
static void load_tiles (TIFF *tif, channel_data *channel,
|
1998-11-09 10:05:24 +08:00
|
|
|
unsigned short bps, unsigned short photomet,
|
1999-09-18 06:28:25 +08:00
|
|
|
int alpha, int extra);
|
|
|
|
|
|
|
|
static void read_separate (guchar *source, channel_data *channel,
|
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int startcol, int startrow, int rows, int cols,
|
|
|
|
int alpha, int extra, int sample);
|
|
|
|
static void read_16bit (guchar *source, channel_data *channel,
|
|
|
|
unsigned short photomet,
|
|
|
|
int startcol, int startrow, int rows, int cols,
|
|
|
|
int alpha, int extra);
|
|
|
|
static void read_8bit (guchar *source, channel_data *channel,
|
|
|
|
unsigned short photomet,
|
|
|
|
int startcol, int startrow, int rows, int cols,
|
|
|
|
int alpha, int extra);
|
|
|
|
static void read_default (guchar *source, channel_data *channel,
|
1998-11-09 10:05:24 +08:00
|
|
|
unsigned short bps, unsigned short photomet,
|
1999-09-18 06:28:25 +08:00
|
|
|
int startcol, int startrow, int rows, int cols,
|
|
|
|
int alpha, int extra);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static gint save_image (char *filename,
|
1998-05-18 08:54:11 +08:00
|
|
|
gint32 image,
|
1999-10-04 02:54:54 +08:00
|
|
|
gint32 drawable,
|
|
|
|
gint32 orig_image);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static void init_gtk ();
|
1997-11-25 06:05:25 +08:00
|
|
|
static gint save_dialog ();
|
|
|
|
|
|
|
|
static void save_close_callback (GtkWidget *widget,
|
|
|
|
gpointer data);
|
|
|
|
static void save_ok_callback (GtkWidget *widget,
|
|
|
|
gpointer data);
|
|
|
|
static void save_toggle_update (GtkWidget *widget,
|
|
|
|
gpointer data);
|
1998-10-12 06:50:03 +08:00
|
|
|
static void comment_entry_callback (GtkWidget *widget,
|
|
|
|
gpointer data);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
#define DEFAULT_COMMENT "Created with The GIMP"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
GPlugInInfo PLUG_IN_INFO =
|
|
|
|
{
|
|
|
|
NULL, /* init_proc */
|
|
|
|
NULL, /* quit_proc */
|
|
|
|
query, /* query_proc */
|
|
|
|
run, /* run_proc */
|
|
|
|
};
|
|
|
|
|
|
|
|
static TiffSaveVals tsvals =
|
|
|
|
{
|
|
|
|
COMPRESSION_LZW, /* compression */
|
|
|
|
};
|
|
|
|
|
|
|
|
static TiffSaveInterface tsint =
|
|
|
|
{
|
|
|
|
FALSE /* run */
|
|
|
|
};
|
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
static char *image_comment= NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
MAIN ()
|
|
|
|
|
|
|
|
static void
|
|
|
|
query ()
|
|
|
|
{
|
|
|
|
static GParamDef load_args[] =
|
|
|
|
{
|
|
|
|
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
|
|
|
{ PARAM_STRING, "filename", "The name of the file to load" },
|
|
|
|
{ PARAM_STRING, "raw_filename", "The name of the file to load" },
|
|
|
|
};
|
|
|
|
static GParamDef load_return_vals[] =
|
|
|
|
{
|
|
|
|
{ PARAM_IMAGE, "image", "Output image" },
|
|
|
|
};
|
|
|
|
static int nload_args = sizeof (load_args) / sizeof (load_args[0]);
|
|
|
|
static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]);
|
|
|
|
|
|
|
|
static GParamDef save_args[] =
|
|
|
|
{
|
|
|
|
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
|
|
|
{ PARAM_IMAGE, "image", "Input image" },
|
|
|
|
{ PARAM_DRAWABLE, "drawable", "Drawable to save" },
|
|
|
|
{ PARAM_STRING, "filename", "The name of the file to save the image in" },
|
|
|
|
{ PARAM_STRING, "raw_filename", "The name of the file to save the image in" },
|
1999-09-18 06:28:25 +08:00
|
|
|
{ PARAM_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2)" },
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
|
|
|
|
|
|
|
gimp_install_procedure ("file_tiff_load",
|
|
|
|
"loads files of the tiff file format",
|
|
|
|
"FIXME: write help for tiff_load",
|
1998-05-18 08:54:11 +08:00
|
|
|
"Spencer Kimball, Peter Mattis & Nick Lamb",
|
1998-05-31 14:49:20 +08:00
|
|
|
"Nick Lamb <njl195@zepler.org.uk>",
|
1999-09-18 06:28:25 +08:00
|
|
|
"1995-1996,1998-1999",
|
1997-11-25 06:05:25 +08:00
|
|
|
"<Load>/Tiff",
|
|
|
|
NULL,
|
|
|
|
PROC_PLUG_IN,
|
|
|
|
nload_args, nload_return_vals,
|
|
|
|
load_args, load_return_vals);
|
|
|
|
|
|
|
|
gimp_install_procedure ("file_tiff_save",
|
|
|
|
"saves files in the tiff file format",
|
|
|
|
"FIXME: write help for tiff_save",
|
|
|
|
"Spencer Kimball & Peter Mattis",
|
|
|
|
"Spencer Kimball & Peter Mattis",
|
|
|
|
"1995-1996",
|
|
|
|
"<Save>/Tiff",
|
1998-05-18 08:54:11 +08:00
|
|
|
"RGB*, GRAY*, INDEXED",
|
1997-11-25 06:05:25 +08:00
|
|
|
PROC_PLUG_IN,
|
|
|
|
nsave_args, 0,
|
|
|
|
save_args, NULL);
|
|
|
|
|
|
|
|
gimp_register_magic_load_handler ("file_tiff_load", "tif,tiff", "",
|
|
|
|
"0,string,II*\\0,0,string,MM\\0*");
|
|
|
|
gimp_register_save_handler ("file_tiff_save", "tif,tiff", "");
|
|
|
|
}
|
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
run (char *name,
|
|
|
|
int nparams,
|
|
|
|
GParam *param,
|
|
|
|
int *nreturn_vals,
|
|
|
|
GParam **return_vals)
|
|
|
|
{
|
|
|
|
static GParam values[2];
|
|
|
|
GRunModeType run_mode;
|
|
|
|
GStatusType status = STATUS_SUCCESS;
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
|
|
|
Parasite *parasite;
|
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-05-18 08:54:11 +08:00
|
|
|
gint32 image;
|
1999-10-04 02:54:54 +08:00
|
|
|
gint32 drawable;
|
|
|
|
gint32 orig_image;
|
1999-10-04 07:48:33 +08:00
|
|
|
GimpExportReturnType export = EXPORT_CANCEL;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
|
|
|
|
*nreturn_vals = 1;
|
|
|
|
*return_vals = values;
|
|
|
|
values[0].type = PARAM_STATUS;
|
|
|
|
values[0].data.d_status = STATUS_CALLING_ERROR;
|
|
|
|
|
|
|
|
if (strcmp (name, "file_tiff_load") == 0)
|
|
|
|
{
|
1998-05-18 08:54:11 +08:00
|
|
|
image = load_image (param[1].data.d_string);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
if (image != -1)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
*nreturn_vals = 2;
|
|
|
|
values[0].data.d_status = STATUS_SUCCESS;
|
|
|
|
values[1].type = PARAM_IMAGE;
|
1998-05-18 08:54:11 +08:00
|
|
|
values[1].data.d_image = image;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp (name, "file_tiff_save") == 0)
|
|
|
|
{
|
1999-10-04 02:54:54 +08:00
|
|
|
image = orig_image = param[1].data.d_int32;
|
1999-10-25 08:59:18 +08:00
|
|
|
drawable = param[2].data.d_int32;
|
1998-11-09 10:05:24 +08:00
|
|
|
|
|
|
|
/* Do this right this time, if POSSIBLE query for parasites, otherwise
|
|
|
|
or if there isn't one, choose the DEFAULT_COMMENT */
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
/* eventually export the image */
|
|
|
|
switch (run_mode)
|
|
|
|
{
|
|
|
|
case RUN_INTERACTIVE:
|
|
|
|
case RUN_WITH_LAST_VALS:
|
|
|
|
init_gtk ();
|
|
|
|
export = gimp_export_image (&image, &drawable, "TIFF",
|
1999-10-04 07:48:33 +08:00
|
|
|
(CAN_HANDLE_RGB | CAN_HANDLE_GRAY | CAN_HANDLE_INDEXED |
|
|
|
|
CAN_HANDLE_ALPHA));
|
|
|
|
if (export == EXPORT_CANCEL)
|
|
|
|
{
|
|
|
|
*nreturn_vals = 1;
|
|
|
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1998-10-12 06:50:03 +08:00
|
|
|
|
1999-10-17 08:07:55 +08:00
|
|
|
parasite = gimp_image_parasite_find (orig_image, "gimp-comment");
|
1998-11-13 12:00:54 +08:00
|
|
|
if (parasite)
|
1999-10-04 02:54:54 +08:00
|
|
|
image_comment = g_strdup (parasite->data);
|
1998-11-09 10:05:24 +08:00
|
|
|
parasite_free(parasite);
|
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
if (!image_comment) image_comment = g_strdup (DEFAULT_COMMENT);
|
1998-10-12 06:50:03 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (run_mode)
|
|
|
|
{
|
|
|
|
case RUN_INTERACTIVE:
|
1998-10-08 16:15:21 +08:00
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Possibly retrieve data */
|
|
|
|
gimp_get_data ("file_tiff_save", &tsvals);
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1999-10-17 08:07:55 +08:00
|
|
|
parasite = gimp_image_parasite_find (orig_image, "tiff-save-options");
|
1998-11-13 12:00:54 +08:00
|
|
|
if (parasite)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
|
|
|
tsvals.compression = ((TiffSaveVals *)parasite->data)->compression;
|
|
|
|
}
|
1998-10-14 10:54:02 +08:00
|
|
|
parasite_free(parasite);
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* First acquire information with a dialog */
|
|
|
|
if (! save_dialog ())
|
|
|
|
return;
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
case RUN_NONINTERACTIVE:
|
|
|
|
/* Make sure all the arguments are there! */
|
1999-09-21 02:00:06 +08:00
|
|
|
if (nparams != 6)
|
1997-11-25 06:05:25 +08:00
|
|
|
status = STATUS_CALLING_ERROR;
|
|
|
|
if (status == STATUS_SUCCESS)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
default: status = STATUS_CALLING_ERROR; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
case RUN_WITH_LAST_VALS:
|
|
|
|
/* Possibly retrieve data */
|
1998-10-08 16:15:21 +08:00
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
gimp_get_data ("file_tiff_save", &tsvals);
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1999-10-17 08:07:55 +08:00
|
|
|
parasite = gimp_image_parasite_find (orig_image, "tiff-save-options");
|
1998-11-13 12:00:54 +08:00
|
|
|
if (parasite)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
|
|
|
tsvals.compression = ((TiffSaveVals *)parasite->data)->compression;
|
|
|
|
}
|
|
|
|
parasite_free (parasite);
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-10-08 16:15:21 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*nreturn_vals = 1;
|
1999-10-04 02:54:54 +08:00
|
|
|
if (save_image (param[3].data.d_string, image, drawable, orig_image))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/* Store mvals data */
|
|
|
|
gimp_set_data ("file_tiff_save", &tsvals, sizeof (TiffSaveVals));
|
|
|
|
|
|
|
|
values[0].data.d_status = STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
1999-10-04 02:54:54 +08:00
|
|
|
|
1999-10-04 07:48:33 +08:00
|
|
|
if (export == EXPORT_EXPORT)
|
1999-10-04 02:54:54 +08:00
|
|
|
gimp_image_delete (image);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static gint32
|
|
|
|
load_image (char *filename)
|
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
TIFF *tif;
|
1999-09-18 06:28:25 +08:00
|
|
|
unsigned short bps, spp, photomet;
|
1998-11-09 10:05:24 +08:00
|
|
|
int cols, rows, alpha;
|
1999-09-18 06:28:25 +08:00
|
|
|
int image, image_type = RGB;
|
|
|
|
int layer, layer_type = RGB_IMAGE;
|
1998-05-31 14:49:20 +08:00
|
|
|
unsigned short extra, *extra_types;
|
1999-09-18 06:28:25 +08:00
|
|
|
channel_data *channel= NULL;
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
unsigned short *redmap, *greenmap, *bluemap;
|
1998-05-18 08:54:11 +08:00
|
|
|
guchar colors[3]= {0, 0, 0};
|
1999-09-18 06:28:25 +08:00
|
|
|
guchar cmap[768];
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
int i, j, worst_case = 0;
|
|
|
|
char *name;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-10-08 16:15:21 +08:00
|
|
|
TiffSaveVals save_vals;
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1998-10-14 10:54:02 +08:00
|
|
|
Parasite *parasite;
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-10-08 16:15:21 +08:00
|
|
|
guint16 tmp;
|
1997-11-25 06:05:25 +08:00
|
|
|
tif = TIFFOpen (filename, "r");
|
1998-05-18 08:54:11 +08:00
|
|
|
if (!tif) {
|
1999-10-04 02:54:54 +08:00
|
|
|
g_message ("TIFF Can't open %s\n", filename);
|
1998-05-18 08:54:11 +08:00
|
|
|
gimp_quit ();
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
name = g_malloc (strlen (filename) + 12);
|
1997-11-25 06:05:25 +08:00
|
|
|
sprintf (name, "Loading %s:", filename);
|
|
|
|
gimp_progress_init (name);
|
1998-05-18 08:54:11 +08:00
|
|
|
g_free (name);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
if (bps > 8 && bps != 16) {
|
|
|
|
worst_case = 1; /* Wrong sample width => RGBA */
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
|
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
if (!TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
|
|
|
|
extra = 0;
|
|
|
|
|
|
|
|
if (!TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols)) {
|
1999-10-04 02:54:54 +08:00
|
|
|
g_message ("TIFF Can't get image width\n");
|
1998-05-18 08:54:11 +08:00
|
|
|
gimp_quit ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows)) {
|
1999-10-04 02:54:54 +08:00
|
|
|
g_message ("TIFF Can't get image length\n");
|
1998-05-18 08:54:11 +08:00
|
|
|
gimp_quit ();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet)) {
|
1999-09-18 06:28:25 +08:00
|
|
|
g_message("TIFF Can't get photometric\nAssuming min-is-black\n");
|
1998-10-05 18:05:29 +08:00
|
|
|
/* 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;
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* test if the extrasample represents an associated alpha channel... */
|
1998-05-18 08:54:11 +08:00
|
|
|
if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) {
|
1997-11-25 06:05:25 +08:00
|
|
|
alpha = 1;
|
1998-11-09 10:05:24 +08:00
|
|
|
--extra;
|
1998-05-18 08:54:11 +08:00
|
|
|
} else {
|
1997-11-25 06:05:25 +08:00
|
|
|
alpha = 0;
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
if (photomet == PHOTOMETRIC_RGB && spp > 3 + extra) {
|
1998-05-31 14:49:20 +08:00
|
|
|
alpha= 1;
|
1998-11-09 10:05:24 +08:00
|
|
|
extra= spp - 4;
|
|
|
|
} else if (photomet != PHOTOMETRIC_RGB && spp > 1 + extra) {
|
1998-05-31 14:49:20 +08:00
|
|
|
alpha= 1;
|
1998-11-09 10:05:24 +08:00
|
|
|
extra= spp - 2;
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
switch (photomet) {
|
|
|
|
case PHOTOMETRIC_MINISBLACK:
|
|
|
|
case PHOTOMETRIC_MINISWHITE:
|
1997-11-25 06:05:25 +08:00
|
|
|
image_type = GRAY;
|
|
|
|
layer_type = (alpha) ? GRAYA_IMAGE : GRAY_IMAGE;
|
1998-05-18 08:54:11 +08:00
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
case PHOTOMETRIC_RGB:
|
|
|
|
image_type = RGB;
|
|
|
|
layer_type = (alpha) ? RGBA_IMAGE : RGB_IMAGE;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
case PHOTOMETRIC_PALETTE:
|
|
|
|
image_type = INDEXED;
|
|
|
|
layer_type = (alpha) ? INDEXEDA_IMAGE : INDEXED_IMAGE;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
default:
|
1999-09-18 06:28:25 +08:00
|
|
|
worst_case = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (worst_case) {
|
|
|
|
image_type = RGB;
|
|
|
|
layer_type = RGBA_IMAGE;
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
if ((image = gimp_image_new (cols, rows, image_type)) == -1) {
|
1998-05-31 14:49:20 +08:00
|
|
|
g_message("TIFF Can't create a new image\n");
|
1998-05-18 08:54:11 +08:00
|
|
|
gimp_quit ();
|
|
|
|
}
|
|
|
|
gimp_image_set_filename (image, filename);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-10-08 16:15:21 +08:00
|
|
|
/* attach a parasite containing the compression/fillorder */
|
|
|
|
if (!TIFFGetField (tif, TIFFTAG_COMPRESSION, &tmp))
|
|
|
|
save_vals.compression = COMPRESSION_NONE;
|
|
|
|
else
|
|
|
|
save_vals.compression = tmp;
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1998-10-14 10:54:02 +08:00
|
|
|
parasite = parasite_new("tiff-save-options", 0,
|
|
|
|
sizeof(save_vals), &save_vals);
|
1999-10-17 08:07:55 +08:00
|
|
|
gimp_image_parasite_attach(image, parasite);
|
1998-10-14 10:54:02 +08:00
|
|
|
parasite_free(parasite);
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-10-08 16:15:21 +08:00
|
|
|
|
1998-10-05 18:05:29 +08:00
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
/* Attach a parasite containing the image description. Pretend to
|
1998-10-14 10:54:02 +08:00
|
|
|
* be a gimp comment so other plugins will use this description as
|
1998-10-12 06:50:03 +08:00
|
|
|
* an image comment where appropriate. */
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1998-10-12 06:50:03 +08:00
|
|
|
{
|
|
|
|
char *img_desc;
|
|
|
|
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &img_desc))
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = strlen(img_desc) + 1;
|
|
|
|
len = MIN(len, 241);
|
|
|
|
img_desc[len-1] = '\000';
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
parasite = parasite_new ("gimp-comment", PARASITE_PERSISTENT,
|
|
|
|
len, img_desc);
|
1999-10-17 08:07:55 +08:00
|
|
|
gimp_image_parasite_attach (image, parasite);
|
1999-10-04 02:54:54 +08:00
|
|
|
parasite_free (parasite);
|
1998-10-12 06:50:03 +08:00
|
|
|
}
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-10-12 06:50:03 +08:00
|
|
|
|
1998-10-05 18:05:29 +08:00
|
|
|
/* any resolution info in the file? */
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_RESOLUTION_INFO
|
1998-10-05 18:05:29 +08:00
|
|
|
{
|
1999-03-17 04:14:07 +08:00
|
|
|
float xres = 72.0, yres = 72.0;
|
|
|
|
unsigned short read_unit;
|
|
|
|
GUnit unit = UNIT_PIXEL; /* invalid unit */
|
1998-10-05 18:05:29 +08:00
|
|
|
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres)) {
|
|
|
|
if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres)) {
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT, &read_unit))
|
|
|
|
{
|
|
|
|
switch (read_unit)
|
|
|
|
{
|
|
|
|
case RESUNIT_NONE:
|
|
|
|
/* ImageMagick writes files with this silly resunit */
|
|
|
|
g_message ("TIFF warning: resolution units meaningless, "
|
|
|
|
"forcing 72 dpi\n");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RESUNIT_INCH:
|
|
|
|
unit = UNIT_INCH;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case RESUNIT_CENTIMETER:
|
|
|
|
xres *= 2.54;
|
|
|
|
yres *= 2.54;
|
|
|
|
unit = UNIT_MM; /* as this is our default metric unit */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
g_message ("TIFF file error: unknown resolution unit type %d, "
|
|
|
|
"assuming dpi\n", read_unit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* no res unit tag */
|
|
|
|
/* old AppleScan software produces these */
|
|
|
|
g_message ("TIFF warning: resolution specified without\n"
|
|
|
|
"any units tag, assuming dpi\n");
|
1998-10-05 18:05:29 +08:00
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* xres but no yres */
|
|
|
|
g_message("TIFF warning: no y resolution info, assuming same as x\n");
|
|
|
|
yres = xres;
|
1998-10-05 18:05:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* sanity check, since division by zero later could be embarrassing */
|
1999-10-04 02:54:54 +08:00
|
|
|
if (xres < 1e-5 || yres < 1e-5)
|
|
|
|
{
|
|
|
|
g_message ("TIFF: image resolution is zero: forcing 72 dpi\n");
|
1998-11-15 07:28:47 +08:00
|
|
|
xres = 72.0;
|
|
|
|
yres = 72.0;
|
1998-10-05 18:05:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* now set the new image's resolution info */
|
1998-11-15 07:28:47 +08:00
|
|
|
gimp_image_set_resolution (image, xres, yres);
|
1999-03-17 04:14:07 +08:00
|
|
|
if (unit != UNIT_PIXEL)
|
1999-05-23 01:56:35 +08:00
|
|
|
gimp_image_set_unit (image, unit);
|
1998-10-05 18:05:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_RESOLUTION_INFO */
|
1998-10-05 18:05:29 +08:00
|
|
|
|
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
/* Install colormap for INDEXED images only */
|
1999-10-04 02:54:54 +08:00
|
|
|
if (image_type == INDEXED)
|
|
|
|
{
|
|
|
|
if (!TIFFGetField (tif, TIFFTAG_COLORMAP, &redmap, &greenmap, &bluemap))
|
|
|
|
{
|
|
|
|
g_message ("TIFF Can't get colormaps\n");
|
|
|
|
gimp_quit ();
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
for (i = 0, j = 0; i < (1 << bps); i++)
|
|
|
|
{
|
|
|
|
cmap[j++] = redmap[i] >> 8;
|
|
|
|
cmap[j++] = greenmap[i] >> 8;
|
|
|
|
cmap[j++] = bluemap[i] >> 8;
|
|
|
|
}
|
|
|
|
gimp_image_set_cmap (image, cmap, (1 << bps));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
/* Allocate channel_data for all channels, even the background layer */
|
|
|
|
channel = g_new (channel_data, extra + 1);
|
1998-05-18 08:54:11 +08:00
|
|
|
layer = gimp_layer_new (image, "Background", cols, rows, layer_type,
|
1997-11-25 06:05:25 +08:00
|
|
|
100, NORMAL_MODE);
|
1999-09-18 06:28:25 +08:00
|
|
|
channel[0].ID= layer;
|
1998-05-18 08:54:11 +08:00
|
|
|
gimp_image_add_layer (image, layer, 0);
|
1999-09-18 06:28:25 +08:00
|
|
|
channel[0].drawable= gimp_drawable_get(layer);
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
if (extra > 0 && !worst_case) {
|
1998-11-09 10:05:24 +08:00
|
|
|
/* Add alpha channels as appropriate */
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 1; i <= extra; ++i) {
|
1998-11-09 10:05:24 +08:00
|
|
|
channel[i].ID= gimp_channel_new(image, "TIFF Channel", cols, rows,
|
|
|
|
100.0, colors);
|
|
|
|
gimp_image_add_channel(image, channel[i].ID, 0);
|
|
|
|
channel[i].drawable= gimp_drawable_get (channel[i].ID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
if (worst_case) {
|
|
|
|
g_message("TIFF Fell back to RGBA, image may be inverted\n");
|
|
|
|
load_rgba (tif, channel);
|
|
|
|
} else if (TIFFIsTiled(tif)) {
|
|
|
|
load_tiles (tif, channel, bps, photomet, alpha, extra);
|
|
|
|
} else { /* Load scanlines in tile_height chunks */
|
|
|
|
load_lines (tif, channel, bps, photomet, alpha, extra);
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i= 0; i < extra; ++i) {
|
|
|
|
gimp_drawable_flush (channel[i].drawable);
|
|
|
|
gimp_drawable_detach (channel[i].drawable);
|
|
|
|
}
|
|
|
|
|
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-09-18 06:28:25 +08:00
|
|
|
load_rgba (TIFF *tif, channel_data *channel)
|
1998-11-09 10:05:24 +08:00
|
|
|
{
|
1999-09-18 06:28:25 +08:00
|
|
|
uint32 imageWidth, imageLength;
|
1999-09-18 09:22:29 +08:00
|
|
|
gulong *buffer;
|
1999-09-18 06:28:25 +08:00
|
|
|
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageLength);
|
|
|
|
gimp_pixel_rgn_init (&(channel[0].pixel_rgn), channel[0].drawable,
|
|
|
|
0, 0, imageWidth, imageLength, TRUE, FALSE);
|
1999-09-18 09:22:29 +08:00
|
|
|
buffer = g_new(gulong, imageWidth * imageLength);
|
1999-09-18 06:28:25 +08:00
|
|
|
channel[0].pixels = (guchar*) buffer;
|
|
|
|
if (buffer == NULL) {
|
|
|
|
g_message("TIFF Unable to allocate temporary buffer\n");
|
|
|
|
}
|
|
|
|
if (!TIFFReadRGBAImage(tif, imageWidth, imageLength, buffer, 0))
|
|
|
|
g_message("TIFF Unsupported layout, no RGBA loader\n");
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
gimp_pixel_rgn_set_rect(&(channel[0].pixel_rgn), channel[0].pixels,
|
|
|
|
0, 0, imageWidth, imageLength);
|
|
|
|
g_message("TIFF Fell back to RGBA, image may be inverted\n");
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
static void
|
|
|
|
load_tiles (TIFF *tif, channel_data *channel,
|
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int alpha, int extra)
|
|
|
|
{
|
|
|
|
uint16 planar= PLANARCONFIG_CONTIG;
|
|
|
|
uint32 imageWidth, imageLength;
|
|
|
|
uint32 tileWidth, tileLength;
|
|
|
|
uint32 x, y, rows, cols;
|
|
|
|
guchar *buffer;
|
|
|
|
double progress= 0.0, one_row;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar);
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageLength);
|
|
|
|
TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth);
|
|
|
|
TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileLength);
|
|
|
|
one_row = (double) tileLength / (double) imageLength;
|
|
|
|
buffer = g_malloc(TIFFTileSize(tif));
|
|
|
|
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
channel[i].pixels= g_new(guchar, tileWidth * tileLength *
|
|
|
|
channel[i].drawable->bpp);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
for (y = 0; y < imageLength; y += tileLength) {
|
|
|
|
for (x = 0; x < imageWidth; x += tileWidth) {
|
|
|
|
gimp_progress_update (progress + one_row *
|
|
|
|
( (double) x / (double) imageWidth));
|
|
|
|
TIFFReadTile(tif, buffer, x, y, 0, 0);
|
|
|
|
cols= MIN(imageWidth - x, tileWidth);
|
|
|
|
rows= MIN(imageLength - y, tileLength);
|
|
|
|
if (bps == 16) {
|
|
|
|
read_16bit(buffer, channel, photomet, y, x, rows, cols, alpha, extra);
|
|
|
|
} else if (bps == 8) {
|
|
|
|
read_8bit(buffer, channel, photomet, y, x, rows, cols, alpha, extra);
|
|
|
|
} else {
|
|
|
|
read_default(buffer, channel, bps, photomet,
|
|
|
|
y, x, rows, cols, alpha, extra);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
progress+= one_row;
|
|
|
|
}
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
g_free(channel[i].pixels);
|
|
|
|
}
|
|
|
|
g_free(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
load_lines (TIFF *tif, channel_data *channel,
|
1999-10-04 02:54:54 +08:00
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int alpha, int extra)
|
1999-09-18 06:28:25 +08:00
|
|
|
{
|
|
|
|
uint16 planar= PLANARCONFIG_CONTIG;
|
|
|
|
uint32 imageLength, lineSize, cols, rows;
|
|
|
|
guchar *buffer;
|
|
|
|
int i, y, tile_height = gimp_tile_height ();
|
|
|
|
|
|
|
|
TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar);
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageLength);
|
|
|
|
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &cols);
|
|
|
|
lineSize= TIFFScanlineSize(tif);
|
|
|
|
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
channel[i].pixels= g_new(guchar, tile_height * cols
|
|
|
|
* channel[i].drawable->bpp);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
buffer = g_malloc(lineSize * tile_height);
|
|
|
|
if (planar == PLANARCONFIG_CONTIG) {
|
|
|
|
for (y = 0; y < imageLength; y+= tile_height ) {
|
|
|
|
gimp_progress_update ( (double) y / (double) imageLength);
|
|
|
|
rows = MIN(tile_height, imageLength - y);
|
|
|
|
for (i = 0; i < rows; ++i)
|
|
|
|
TIFFReadScanline(tif, buffer + i * lineSize, y + i, 0);
|
|
|
|
if (bps == 16) {
|
|
|
|
read_16bit(buffer, channel, photomet, y, 0, rows, cols, alpha, extra);
|
|
|
|
} else if (bps == 8) {
|
|
|
|
read_8bit(buffer, channel, photomet, y, 0, rows, cols, alpha, extra);
|
|
|
|
} else {
|
|
|
|
read_default(buffer, channel, bps, photomet,
|
|
|
|
y, 0, rows, cols, alpha, extra);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
} else { /* PLANARCONFIG_SEPARATE -- Just say "No" */
|
|
|
|
uint16 s, samples;
|
|
|
|
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples);
|
|
|
|
for (s = 0; s < samples; ++s) {
|
|
|
|
for (y = 0; y < imageLength; y+= tile_height ) {
|
|
|
|
gimp_progress_update ( (double) y / (double) imageLength);
|
|
|
|
rows = MIN(tile_height, imageLength - y);
|
|
|
|
for (i = 0; i < rows; ++i)
|
|
|
|
TIFFReadScanline(tif, buffer + i * lineSize, y + i, s);
|
|
|
|
read_separate (buffer, channel, bps, photomet,
|
|
|
|
y, 0, rows, cols, alpha, extra, s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
g_free(channel[i].pixels);
|
|
|
|
}
|
|
|
|
g_free(buffer);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
static void
|
1999-10-04 02:54:54 +08:00
|
|
|
read_16bit (guchar *source, channel_data *channel, unsigned short photomet,
|
|
|
|
int startrow, int startcol, int rows, int cols,
|
|
|
|
int alpha, int extra)
|
1999-09-18 06:28:25 +08:00
|
|
|
{
|
|
|
|
guchar *dest;
|
|
|
|
int gray_val, red_val, green_val, blue_val, alpha_val;
|
|
|
|
int col, row, i;
|
|
|
|
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
|
|
|
|
startcol, startrow, cols, rows, TRUE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < rows; ++row) {
|
|
|
|
dest= channel[0].pixels + row * cols * channel[0].drawable->bpp;
|
|
|
|
|
|
|
|
for (i= 1; i <= extra; ++i) {
|
|
|
|
channel[i].pixel= channel[i].pixels + row * cols;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
for (col = 0; col < cols; col++) {
|
|
|
|
switch (photomet) {
|
|
|
|
case PHOTOMETRIC_MINISBLACK:
|
|
|
|
if (alpha) {
|
1999-09-18 06:28:25 +08:00
|
|
|
gray_val= *source; source+= 2;
|
|
|
|
alpha_val= *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
if (alpha_val)
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = gray_val * 255 / alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
else
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
case PHOTOMETRIC_MINISWHITE:
|
|
|
|
if (alpha) {
|
1999-09-18 06:28:25 +08:00
|
|
|
gray_val= *source; source+= 2;
|
|
|
|
alpha_val= *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
if (alpha_val)
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = ((255 - gray_val) * 255) / alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
else
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = ~(*source); source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_PALETTE:
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++= *source; source+= 2;
|
|
|
|
if (alpha) *dest++= *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_RGB:
|
|
|
|
if (alpha) {
|
1999-09-18 06:28:25 +08:00
|
|
|
red_val= *source; source+= 2;
|
|
|
|
green_val= *source; source+= 2;
|
|
|
|
blue_val= *source; source+= 2;
|
|
|
|
alpha_val= *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
if (alpha_val) {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = (red_val * 255) / alpha_val;
|
|
|
|
*dest++ = (green_val * 255) / alpha_val;
|
|
|
|
*dest++ = (blue_val * 255) / alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = *source; source+= 2;
|
|
|
|
*dest++ = *source; source+= 2;
|
|
|
|
*dest++ = *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* This case was handled earlier */
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 1; i <= extra; ++i) {
|
|
|
|
*channel[i].pixel++ = *source; source+= 2;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
}
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
|
|
|
|
startcol, startrow, cols, rows);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1999-10-04 02:54:54 +08:00
|
|
|
read_8bit (guchar *source, channel_data *channel, unsigned short photomet,
|
|
|
|
int startrow, int startcol, int rows, int cols,
|
|
|
|
int alpha, int extra)
|
1999-09-18 06:28:25 +08:00
|
|
|
{
|
|
|
|
guchar *dest;
|
|
|
|
int gray_val, red_val, green_val, blue_val, alpha_val;
|
|
|
|
int col, row, i;
|
|
|
|
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
|
|
|
|
startcol, startrow, cols, rows, TRUE, FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < rows; ++row) {
|
|
|
|
dest= channel[0].pixels + row * cols * channel[0].drawable->bpp;
|
|
|
|
|
|
|
|
for (i= 1; i <= extra; ++i) {
|
|
|
|
channel[i].pixel= channel[i].pixels + row * cols;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (col = 0; col < cols; col++) {
|
|
|
|
switch (photomet) {
|
|
|
|
case PHOTOMETRIC_MINISBLACK:
|
|
|
|
if (alpha) {
|
|
|
|
gray_val= *source++;
|
|
|
|
alpha_val= *source++;
|
|
|
|
if (alpha_val)
|
|
|
|
*dest++ = gray_val * 255 / alpha_val;
|
|
|
|
else
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
|
|
|
} else {
|
|
|
|
*dest++ = *source++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_MINISWHITE:
|
|
|
|
if (alpha) {
|
|
|
|
gray_val= *source++;
|
|
|
|
alpha_val= *source++;
|
|
|
|
if (alpha_val)
|
|
|
|
*dest++ = ((255 - gray_val) * 255) / alpha_val;
|
|
|
|
else
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
|
|
|
} else {
|
|
|
|
*dest++ = ~(*source++);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_PALETTE:
|
|
|
|
*dest++= *source++;
|
|
|
|
if (alpha) *dest++= *source++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_RGB:
|
|
|
|
if (alpha) {
|
|
|
|
red_val= *source++;
|
|
|
|
green_val= *source++;
|
|
|
|
blue_val= *source++;
|
|
|
|
alpha_val= *source++;
|
|
|
|
if (alpha_val) {
|
|
|
|
*dest++ = (red_val * 255) / alpha_val;
|
|
|
|
*dest++ = (green_val * 255) / alpha_val;
|
|
|
|
*dest++ = (blue_val * 255) / alpha_val;
|
|
|
|
} else {
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
|
|
|
}
|
|
|
|
*dest++ = alpha_val;
|
|
|
|
} else {
|
|
|
|
*dest++ = *source++;
|
|
|
|
*dest++ = *source++;
|
|
|
|
*dest++ = *source++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* This case was handled earlier */
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
|
|
|
for (i= 1; i <= extra; ++i) {
|
|
|
|
*channel[i].pixel++ = *source++;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
}
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
|
|
|
|
startcol, startrow, cols, rows);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
/* Step through all <= 8-bit samples in an image */
|
|
|
|
|
|
|
|
#define NEXTSAMPLE(var) \
|
1997-11-25 06:05:25 +08:00
|
|
|
{ \
|
1998-05-18 08:54:11 +08:00
|
|
|
if (bitsleft == 0) \
|
1997-11-25 06:05:25 +08:00
|
|
|
{ \
|
1999-09-18 06:28:25 +08:00
|
|
|
source++; \
|
1997-11-25 06:05:25 +08:00
|
|
|
bitsleft = 8; \
|
|
|
|
} \
|
|
|
|
bitsleft -= bps; \
|
1999-09-18 06:28:25 +08:00
|
|
|
var = ( *source >> bitsleft ) & maxval; \
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
static void
|
1999-10-04 02:54:54 +08:00
|
|
|
read_default (guchar *source, channel_data *channel,
|
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int startrow, int startcol, int rows, int cols,
|
|
|
|
int alpha, int extra)
|
1998-11-09 10:05:24 +08:00
|
|
|
{
|
1999-09-18 06:28:25 +08:00
|
|
|
guchar *dest;
|
1998-11-09 10:05:24 +08:00
|
|
|
int gray_val, red_val, green_val, blue_val, alpha_val;
|
1999-09-18 06:28:25 +08:00
|
|
|
int col, row, i;
|
|
|
|
int bitsleft = 8, maxval = (1 << bps) - 1;
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_init (&(channel[i].pixel_rgn), channel[i].drawable,
|
|
|
|
startcol, startrow, cols, rows, TRUE, FALSE);
|
|
|
|
}
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
for (row = 0; row < rows; ++row) {
|
|
|
|
dest= channel[0].pixels + row * cols * channel[0].drawable->bpp;
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 1; i <= extra; ++i) {
|
|
|
|
channel[i].pixel= channel[i].pixels + row * cols;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1998-05-18 08:54:11 +08:00
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
for (col = 0; col < cols; col++) {
|
1998-05-18 08:54:11 +08:00
|
|
|
switch (photomet) {
|
|
|
|
case PHOTOMETRIC_MINISBLACK:
|
1998-11-09 10:05:24 +08:00
|
|
|
NEXTSAMPLE(gray_val);
|
|
|
|
if (alpha) {
|
|
|
|
NEXTSAMPLE(alpha_val);
|
|
|
|
if (alpha_val)
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = (gray_val * 65025) / (alpha_val * maxval);
|
1998-11-09 10:05:24 +08:00
|
|
|
else
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = (gray_val * 255) / maxval;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
case PHOTOMETRIC_MINISWHITE:
|
1998-11-09 10:05:24 +08:00
|
|
|
NEXTSAMPLE(gray_val);
|
|
|
|
if (alpha) {
|
|
|
|
NEXTSAMPLE(alpha_val);
|
|
|
|
if (alpha_val)
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval);
|
1998-11-09 10:05:24 +08:00
|
|
|
else
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = ((maxval - gray_val) * 255) / maxval;
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_PALETTE:
|
1999-09-18 06:28:25 +08:00
|
|
|
NEXTSAMPLE(*dest++);
|
1998-11-09 10:05:24 +08:00
|
|
|
if (alpha) {
|
1999-09-18 06:28:25 +08:00
|
|
|
NEXTSAMPLE(*dest++);
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PHOTOMETRIC_RGB:
|
1998-11-09 10:05:24 +08:00
|
|
|
NEXTSAMPLE(red_val)
|
|
|
|
NEXTSAMPLE(green_val)
|
|
|
|
NEXTSAMPLE(blue_val)
|
|
|
|
if (alpha) {
|
|
|
|
NEXTSAMPLE(alpha_val)
|
|
|
|
if (alpha_val) {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = (red_val * 255) / alpha_val;
|
|
|
|
*dest++ = (green_val * 255) / alpha_val;
|
|
|
|
*dest++ = (blue_val * 255) / alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
|
|
|
*dest++ = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = alpha_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
} else {
|
1999-09-18 06:28:25 +08:00
|
|
|
*dest++ = red_val;
|
|
|
|
*dest++ = green_val;
|
|
|
|
*dest++ = blue_val;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1998-05-18 08:54:11 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
/* This case was handled earlier */
|
|
|
|
g_assert_not_reached();
|
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
for (i= 1; i <= extra; ++i) {
|
1998-11-09 10:05:24 +08:00
|
|
|
NEXTSAMPLE(alpha_val);
|
|
|
|
*channel[i].pixel++ = alpha_val;
|
|
|
|
}
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
bitsleft= 0;
|
|
|
|
}
|
|
|
|
for (i= 0; i <= extra; ++i) {
|
|
|
|
gimp_pixel_rgn_set_rect(&(channel[i].pixel_rgn), channel[i].pixels,
|
|
|
|
startcol, startrow, cols, rows);
|
1998-05-18 08:54:11 +08:00
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
static void
|
1999-09-18 06:28:25 +08:00
|
|
|
read_separate (guchar *source, channel_data *channel,
|
|
|
|
unsigned short bps, unsigned short photomet,
|
|
|
|
int startrow, int startcol, int rows, int cols,
|
|
|
|
int alpha, int extra, int sample)
|
1998-11-09 10:05:24 +08:00
|
|
|
{
|
1999-09-18 06:28:25 +08:00
|
|
|
guchar *dest;
|
|
|
|
int col, row, c;
|
|
|
|
int bitsleft = 8, maxval = (1 << bps) - 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
if (bps > 8) {
|
|
|
|
g_message("TIFF Unsupported layout\n");
|
1998-11-09 10:05:24 +08:00
|
|
|
gimp_quit();
|
|
|
|
}
|
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
if (sample < channel[0].drawable->bpp) {
|
|
|
|
c = 0;
|
|
|
|
} else {
|
|
|
|
c = (sample - channel[0].drawable->bpp) + 4;
|
|
|
|
photomet = PHOTOMETRIC_MINISBLACK;
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
gimp_pixel_rgn_init (&(channel[c].pixel_rgn), channel[c].drawable,
|
|
|
|
startcol, startrow, cols, rows, TRUE, FALSE);
|
1998-11-09 10:05:24 +08:00
|
|
|
|
1999-09-18 06:28:25 +08:00
|
|
|
gimp_pixel_rgn_get_rect(&(channel[c].pixel_rgn), channel[c].pixels,
|
|
|
|
startcol, startrow, cols, rows);
|
|
|
|
for (row = 0; row < rows; ++row) {
|
|
|
|
dest = channel[c].pixels + row * cols * channel[c].drawable->bpp;
|
|
|
|
if (c == 0) {
|
|
|
|
for (col = 0; col < cols; ++col) {
|
|
|
|
NEXTSAMPLE(dest[col * channel[0].drawable->bpp + sample]);
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
} else {
|
|
|
|
for (col = 0; col < cols; ++col)
|
|
|
|
NEXTSAMPLE(dest[col]);
|
1998-11-09 10:05:24 +08:00
|
|
|
}
|
|
|
|
}
|
1999-09-18 06:28:25 +08:00
|
|
|
gimp_pixel_rgn_set_rect(&(channel[c].pixel_rgn), channel[c].pixels,
|
|
|
|
startcol, startrow, cols, rows);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** pnmtotiff.c - converts a portable anymap to a Tagged Image File
|
|
|
|
**
|
|
|
|
** Derived by Jef Poskanzer from ras2tif.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.
|
|
|
|
*/
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static gint save_image (char *filename,
|
|
|
|
gint32 image,
|
|
|
|
gint32 layer,
|
|
|
|
gint32 orig_image) /* the export function might have created a duplicate */
|
|
|
|
{
|
1997-11-25 06:05:25 +08:00
|
|
|
TIFF *tif;
|
|
|
|
unsigned short red[256];
|
|
|
|
unsigned short grn[256];
|
|
|
|
unsigned short blu[256];
|
|
|
|
int cols, col, rows, row, i;
|
|
|
|
long g3options;
|
|
|
|
long rowsperstrip;
|
|
|
|
unsigned short compression;
|
|
|
|
unsigned short extra_samples[1];
|
|
|
|
int alpha;
|
|
|
|
short predictor;
|
|
|
|
short photometric;
|
|
|
|
short samplesperpixel;
|
|
|
|
short bitspersample;
|
|
|
|
int bytesperrow;
|
|
|
|
guchar *t, *src, *data;
|
|
|
|
guchar *cmap;
|
|
|
|
int colors;
|
|
|
|
int success;
|
|
|
|
GDrawable *drawable;
|
|
|
|
GDrawableType drawable_type;
|
|
|
|
GPixelRgn pixel_rgn;
|
|
|
|
int tile_height;
|
|
|
|
int y, yend;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
compression = tsvals.compression;
|
|
|
|
|
|
|
|
g3options = 0;
|
|
|
|
predictor = 0;
|
|
|
|
rowsperstrip = 0;
|
|
|
|
|
|
|
|
tif = TIFFOpen (filename, "w");
|
1999-10-04 02:54:54 +08:00
|
|
|
if (!tif)
|
|
|
|
{
|
|
|
|
g_print ("Can't write image to\n%s", filename);
|
|
|
|
return 0;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
name = malloc (strlen (filename) + 11);
|
|
|
|
sprintf (name, "Saving %s:", filename);
|
|
|
|
gimp_progress_init (name);
|
|
|
|
free (name);
|
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
drawable = gimp_drawable_get (layer);
|
|
|
|
drawable_type = gimp_drawable_type (layer);
|
1997-11-25 06:05:25 +08:00
|
|
|
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
|
|
|
|
|
|
|
|
cols = drawable->width;
|
|
|
|
rows = drawable->height;
|
|
|
|
|
|
|
|
switch (drawable_type)
|
|
|
|
{
|
|
|
|
case RGB_IMAGE:
|
1999-01-12 10:13:18 +08:00
|
|
|
predictor = 2;
|
1997-11-25 06:05:25 +08:00
|
|
|
samplesperpixel = 3;
|
|
|
|
bitspersample = 8;
|
|
|
|
photometric = PHOTOMETRIC_RGB;
|
|
|
|
bytesperrow = cols * 3;
|
|
|
|
alpha = 0;
|
|
|
|
break;
|
|
|
|
case GRAY_IMAGE:
|
|
|
|
samplesperpixel = 1;
|
|
|
|
bitspersample = 8;
|
|
|
|
photometric = PHOTOMETRIC_MINISBLACK;
|
|
|
|
bytesperrow = cols;
|
|
|
|
alpha = 0;
|
|
|
|
break;
|
|
|
|
case RGBA_IMAGE:
|
1999-01-12 10:13:18 +08:00
|
|
|
predictor = 2;
|
1997-11-25 06:05:25 +08:00
|
|
|
samplesperpixel = 4;
|
|
|
|
bitspersample = 8;
|
|
|
|
photometric = PHOTOMETRIC_RGB;
|
|
|
|
bytesperrow = cols * 4;
|
|
|
|
alpha = 1;
|
|
|
|
break;
|
|
|
|
case GRAYA_IMAGE:
|
|
|
|
samplesperpixel = 2;
|
|
|
|
bitspersample = 8;
|
|
|
|
photometric = PHOTOMETRIC_MINISBLACK;
|
|
|
|
bytesperrow = cols * 2;
|
|
|
|
alpha = 1;
|
|
|
|
break;
|
|
|
|
case INDEXED_IMAGE:
|
|
|
|
samplesperpixel = 1;
|
|
|
|
bitspersample = 8;
|
|
|
|
photometric = PHOTOMETRIC_PALETTE;
|
|
|
|
bytesperrow = cols;
|
|
|
|
alpha = 0;
|
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
cmap = gimp_image_get_cmap (image, &colors);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
for (i = 0; i < colors; i++)
|
|
|
|
{
|
1998-04-27 07:28:04 +08:00
|
|
|
red[i] = *cmap++ * 65535 / 255;
|
|
|
|
grn[i] = *cmap++ * 65535 / 255;
|
|
|
|
blu[i] = *cmap++ * 65535 / 255;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case INDEXEDA_IMAGE:
|
|
|
|
return 0;
|
1998-10-08 16:15:21 +08:00
|
|
|
default:
|
1999-09-18 06:28:25 +08:00
|
|
|
return 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rowsperstrip == 0)
|
|
|
|
rowsperstrip = (8 * 1024) / bytesperrow;
|
|
|
|
if (rowsperstrip == 0)
|
|
|
|
rowsperstrip = 1;
|
|
|
|
|
|
|
|
/* Set TIFF parameters. */
|
|
|
|
TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
|
|
|
|
TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
|
|
|
|
TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
|
|
|
|
TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
|
|
|
|
TIFFSetField (tif, TIFFTAG_COMPRESSION, compression);
|
|
|
|
if ((compression == COMPRESSION_LZW) && (predictor != 0))
|
|
|
|
TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
|
|
|
|
if (alpha)
|
|
|
|
{
|
|
|
|
extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
|
|
|
|
TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
|
|
|
|
}
|
|
|
|
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
|
|
|
|
TIFFSetField (tif, TIFFTAG_DOCUMENTNAME, filename);
|
|
|
|
TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
|
|
|
|
TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
|
|
|
|
/* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
|
|
|
|
TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
|
|
|
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_RESOLUTION_INFO
|
1998-10-05 18:05:29 +08:00
|
|
|
/* resolution fields */
|
|
|
|
{
|
1999-05-23 01:56:35 +08:00
|
|
|
double xresolution;
|
|
|
|
double yresolution;
|
|
|
|
unsigned short save_unit = RESUNIT_INCH;
|
|
|
|
GUnit unit;
|
|
|
|
float factor;
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
gimp_image_get_resolution (orig_image, &xresolution, &yresolution);
|
|
|
|
unit = gimp_image_get_unit (orig_image);
|
1999-05-23 01:56:35 +08:00
|
|
|
factor = gimp_unit_get_factor (unit);
|
|
|
|
|
|
|
|
/* if we have a metric unit, save the resolution as centimeters
|
|
|
|
*/
|
|
|
|
if ((ABS(factor - 0.0254) < 1e-5) || /* m */
|
|
|
|
(ABS(factor - 0.254) < 1e-5) || /* why not ;) */
|
|
|
|
(ABS(factor - 2.54) < 1e-5) || /* cm */
|
|
|
|
(ABS(factor - 25.4) < 1e-5)) /* mm */
|
|
|
|
{
|
|
|
|
save_unit = RESUNIT_CENTIMETER;
|
|
|
|
xresolution /= 2.54;
|
|
|
|
yresolution /= 2.54;
|
|
|
|
}
|
1998-11-15 07:28:47 +08:00
|
|
|
|
1999-05-23 01:56:35 +08:00
|
|
|
if (xresolution > 1e-5 && yresolution > 1e-5)
|
1998-10-05 18:05:29 +08:00
|
|
|
{
|
1999-05-23 01:56:35 +08:00
|
|
|
TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
|
|
|
|
TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
|
|
|
|
TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
|
1998-10-05 18:05:29 +08:00
|
|
|
}
|
|
|
|
}
|
1998-11-13 12:00:54 +08:00
|
|
|
#endif /* GIMP_HAVE_RESOLUTION_INFO */
|
1998-10-05 18:05:29 +08:00
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
/* 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. */
|
1998-11-09 10:05:24 +08:00
|
|
|
#ifdef GIMP_HAVE_PARASITES
|
1998-10-12 06:50:03 +08:00
|
|
|
if (image_comment && *image_comment != '\000')
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
|
|
|
Parasite *parasite;
|
|
|
|
|
|
|
|
TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, image_comment);
|
|
|
|
parasite = parasite_new ("gimp-comment", 1,
|
|
|
|
strlen (image_comment) + 1, image_comment);
|
1999-10-17 08:07:55 +08:00
|
|
|
gimp_image_parasite_attach (orig_image, parasite);
|
1999-10-04 02:54:54 +08:00
|
|
|
parasite_free (parasite);
|
|
|
|
}
|
1998-11-09 10:05:24 +08:00
|
|
|
#endif /* GIMP_HAVE_PARASITES */
|
1998-10-12 06:50:03 +08:00
|
|
|
|
1998-05-18 08:54:11 +08:00
|
|
|
if (drawable_type == INDEXED_IMAGE)
|
1997-11-25 06:05:25 +08:00
|
|
|
TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu);
|
|
|
|
|
|
|
|
/* array to rearrange data */
|
|
|
|
tile_height = gimp_tile_height ();
|
|
|
|
src = g_new (guchar, bytesperrow * tile_height);
|
|
|
|
data = g_new (guchar, bytesperrow);
|
|
|
|
|
|
|
|
/* Now write the TIFF data. */
|
|
|
|
for (y = 0; y < rows; y = yend)
|
|
|
|
{
|
|
|
|
yend = y + tile_height;
|
|
|
|
yend = MIN (yend, rows);
|
|
|
|
|
|
|
|
gimp_pixel_rgn_get_rect (&pixel_rgn, src, 0, y, cols, yend - y);
|
|
|
|
|
|
|
|
for (row = y; row < yend; row++)
|
|
|
|
{
|
|
|
|
t = src + bytesperrow * (row - y);
|
|
|
|
|
|
|
|
switch (drawable_type)
|
|
|
|
{
|
|
|
|
case INDEXED_IMAGE:
|
|
|
|
success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
|
|
|
|
break;
|
|
|
|
case GRAY_IMAGE:
|
|
|
|
success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
|
|
|
|
break;
|
|
|
|
case GRAYA_IMAGE:
|
|
|
|
for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
|
|
|
|
{
|
|
|
|
/* pre-multiply gray by alpha */
|
|
|
|
data[col + 0] = (t[col + 0] * t[col + 1]) / 255;
|
|
|
|
data[col + 1] = t[col + 1]; /* alpha channel */
|
|
|
|
}
|
|
|
|
success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
|
|
|
|
break;
|
|
|
|
case RGB_IMAGE:
|
|
|
|
success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
|
|
|
|
break;
|
|
|
|
case RGBA_IMAGE:
|
|
|
|
for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
|
|
|
|
{
|
|
|
|
/* pre-multiply rgb by alpha */
|
|
|
|
data[col+0] = t[col + 0] * t[col + 3] / 255;
|
|
|
|
data[col+1] = t[col + 1] * t[col + 3] / 255;
|
|
|
|
data[col+2] = t[col + 2] * t[col + 3] / 255;
|
|
|
|
data[col+3] = t[col + 3]; /* alpha channel */
|
|
|
|
}
|
|
|
|
success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
|
|
|
|
break;
|
|
|
|
default:
|
1998-10-12 06:50:03 +08:00
|
|
|
success = FALSE;
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1998-05-31 14:49:20 +08:00
|
|
|
if (!success) {
|
|
|
|
g_message("TIFF Failed a scanline write on row %d", row);
|
1997-11-25 06:05:25 +08:00
|
|
|
return 0;
|
1998-05-31 14:49:20 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
gimp_progress_update ((double) row / (double) rows);
|
|
|
|
}
|
|
|
|
|
|
|
|
TIFFFlushData (tif);
|
|
|
|
TIFFClose (tif);
|
|
|
|
|
|
|
|
gimp_drawable_detach (drawable);
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static void
|
|
|
|
init_gtk ()
|
|
|
|
{
|
|
|
|
gchar **argv;
|
|
|
|
gint argc;
|
|
|
|
|
|
|
|
argc = 1;
|
|
|
|
argv = g_new (gchar *, 1);
|
|
|
|
argv[0] = g_strdup ("tiff");
|
|
|
|
|
|
|
|
gtk_init (&argc, &argv);
|
|
|
|
gtk_rc_parse (gimp_gtkrc ());
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static gint
|
|
|
|
save_dialog ()
|
|
|
|
{
|
|
|
|
GtkWidget *dlg;
|
|
|
|
GtkWidget *button;
|
|
|
|
GtkWidget *toggle;
|
|
|
|
GtkWidget *frame;
|
|
|
|
GtkWidget *toggle_vbox;
|
1998-10-12 06:50:03 +08:00
|
|
|
GtkWidget *hbox;
|
|
|
|
GtkWidget *label;
|
|
|
|
GtkWidget *entry;
|
1997-11-25 06:05:25 +08:00
|
|
|
GSList *group;
|
|
|
|
gint use_none = (tsvals.compression == COMPRESSION_NONE);
|
|
|
|
gint use_lzw = (tsvals.compression == COMPRESSION_LZW);
|
|
|
|
gint use_packbits = (tsvals.compression == COMPRESSION_PACKBITS);
|
|
|
|
|
|
|
|
dlg = gtk_dialog_new ();
|
|
|
|
gtk_window_set_title (GTK_WINDOW (dlg), "Save as Tiff");
|
|
|
|
gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
|
|
|
|
(GtkSignalFunc) save_close_callback,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
/* Action area */
|
|
|
|
button = gtk_button_new_with_label ("OK");
|
|
|
|
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
|
|
|
(GtkSignalFunc) save_ok_callback,
|
|
|
|
dlg);
|
|
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_grab_default (button);
|
|
|
|
gtk_widget_show (button);
|
|
|
|
|
|
|
|
button = gtk_button_new_with_label ("Cancel");
|
|
|
|
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
|
|
|
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
|
|
|
(GtkSignalFunc) gtk_widget_destroy,
|
|
|
|
GTK_OBJECT (dlg));
|
|
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_show (button);
|
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
/* hbox for compression and fillorder settings */
|
|
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* compression */
|
|
|
|
frame = gtk_frame_new ("Compression");
|
|
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
1998-10-12 06:50:03 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, FALSE, 0);
|
1997-11-25 06:05:25 +08:00
|
|
|
toggle_vbox = gtk_vbox_new (FALSE, 5);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (toggle_vbox), 5);
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), toggle_vbox);
|
|
|
|
|
|
|
|
group = NULL;
|
|
|
|
toggle = gtk_radio_button_new_with_label (group, "None");
|
|
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
|
|
|
|
gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
|
|
|
|
(GtkSignalFunc) save_toggle_update,
|
|
|
|
&use_none);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_none);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
|
|
|
|
|
|
|
toggle = gtk_radio_button_new_with_label (group, "LZW");
|
|
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
|
|
|
|
gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
|
|
|
|
(GtkSignalFunc) save_toggle_update,
|
|
|
|
&use_lzw);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_lzw);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
|
|
|
|
|
|
|
toggle = gtk_radio_button_new_with_label (group, "Pack Bits");
|
|
|
|
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
|
|
|
|
gtk_box_pack_start (GTK_BOX (toggle_vbox), toggle, FALSE, FALSE, 0);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
|
|
|
|
(GtkSignalFunc) save_toggle_update,
|
|
|
|
&use_packbits);
|
1999-01-16 01:35:04 +08:00
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), use_packbits);
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (toggle);
|
|
|
|
|
|
|
|
gtk_widget_show (toggle_vbox);
|
|
|
|
gtk_widget_show (frame);
|
|
|
|
|
1998-10-12 06:50:03 +08:00
|
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), hbox, FALSE, TRUE, 0);
|
|
|
|
gtk_widget_show (hbox);
|
|
|
|
|
|
|
|
|
|
|
|
/* comment entry */
|
|
|
|
frame = gtk_frame_new(NULL);
|
|
|
|
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
|
|
|
|
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
|
|
|
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, TRUE, 0);
|
|
|
|
|
|
|
|
hbox = gtk_hbox_new (FALSE, 5);
|
|
|
|
label = gtk_label_new ("Comment: ");
|
|
|
|
gtk_widget_show (label);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
|
|
|
|
entry = gtk_entry_new ();
|
|
|
|
gtk_widget_show (entry);
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (entry), image_comment);
|
|
|
|
gtk_signal_connect (GTK_OBJECT (entry), "changed",
|
|
|
|
(GtkSignalFunc) comment_entry_callback,
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), hbox);
|
|
|
|
gtk_widget_show (hbox);
|
|
|
|
gtk_widget_show (frame);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
gtk_widget_show (dlg);
|
|
|
|
|
|
|
|
gtk_main ();
|
|
|
|
gdk_flush ();
|
|
|
|
|
|
|
|
if (use_none)
|
|
|
|
tsvals.compression = COMPRESSION_NONE;
|
|
|
|
else if (use_lzw)
|
|
|
|
tsvals.compression = COMPRESSION_LZW;
|
|
|
|
else if (use_packbits)
|
|
|
|
tsvals.compression = COMPRESSION_PACKBITS;
|
|
|
|
|
|
|
|
return tsint.run;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Save interface functions */
|
|
|
|
|
|
|
|
static void
|
|
|
|
save_close_callback (GtkWidget *widget,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
gtk_main_quit ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
save_ok_callback (GtkWidget *widget,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
tsint.run = TRUE;
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (data));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
save_toggle_update (GtkWidget *widget,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
int *toggle_val;
|
|
|
|
|
|
|
|
toggle_val = (int *) data;
|
|
|
|
|
|
|
|
if (GTK_TOGGLE_BUTTON (widget)->active)
|
|
|
|
*toggle_val = TRUE;
|
|
|
|
else
|
|
|
|
*toggle_val = FALSE;
|
|
|
|
}
|
1998-10-12 06:50:03 +08:00
|
|
|
|
|
|
|
static void
|
|
|
|
comment_entry_callback (GtkWidget *widget,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
char *text;
|
|
|
|
|
|
|
|
text = gtk_entry_get_text (GTK_ENTRY (widget));
|
|
|
|
len = strlen(text);
|
|
|
|
|
|
|
|
/* Temporary kludge for overlength strings - just return */
|
|
|
|
if (len > 240)
|
|
|
|
{
|
|
|
|
g_message ("TIFF save: Your comment string is too long.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
g_free (image_comment);
|
|
|
|
image_comment = g_strdup (text);
|
1998-10-12 06:50:03 +08:00
|
|
|
|
|
|
|
/* g_print ("COMMENT: %s\n", image_comment); */
|
|
|
|
}
|