Apply a (modified) patch from Pablo d'Angelo to enable saving of a

2004-01-16  Dave Neary  <bolsh@gimp.org>

        * plug-ins/common/tiff.c: Apply a (modified) patch from Pablo
        d'Angelo to enable saving of a non-premultiplied alpha channel
        and provide an UI to use it (a checkbox and extra PDB
        procedure). Fixes bug #131030.
This commit is contained in:
Dave Neary 2004-01-16 21:28:31 +00:00 committed by David Neary
parent 4a234e4421
commit c40b7bb0de
2 changed files with 282 additions and 106 deletions

View File

@ -1,3 +1,10 @@
2004-01-16 Dave Neary <bolsh@gimp.org>
* plug-ins/common/tiff.c: Apply a (modified) patch from Pablo
d'Angelo to enable saving of a non-premultiplied alpha channel
and provide an UI to use it (a checkbox and extra PDB
procedure). Fixes bug #131030.
2004-01-16 Sven Neumann <sven@gimp.org> 2004-01-16 Sven Neumann <sven@gimp.org>
* app/tools/gimpcroptool.c (crop_recalc): do a proper fix for bug * app/tools/gimpcroptool.c (crop_recalc): do a proper fix for bug

View File

@ -15,6 +15,8 @@
* peter@kirchgessner.net -- 29 Oct 2002 * peter@kirchgessner.net -- 29 Oct 2002
* Progress bar only when run interactive * Progress bar only when run interactive
* Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004 * 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
*/ */
/* /*
@ -42,6 +44,8 @@
#include <tiffio.h> #include <tiffio.h>
#include <gtk/gtk.h>
#include <libgimp/gimp.h> #include <libgimp/gimp.h>
#include <libgimp/gimpui.h> #include <libgimp/gimpui.h>
@ -52,6 +56,7 @@ typedef struct
{ {
gint compression; gint compression;
gint fillorder; gint fillorder;
gboolean save_transp_pixels;
} TiffSaveVals; } TiffSaveVals;
typedef struct typedef struct
@ -154,8 +159,10 @@ GimpPlugInInfo PLUG_IN_INFO =
static TiffSaveVals tsvals = static TiffSaveVals tsvals =
{ {
COMPRESSION_NONE, /* compression */ COMPRESSION_NONE, /* compression */
FALSE, /* alpha handling */
}; };
static gchar *image_comment = NULL; static gchar *image_comment = NULL;
static GimpRunMode run_mode = GIMP_RUN_INTERACTIVE; static GimpRunMode run_mode = GIMP_RUN_INTERACTIVE;
@ -176,14 +183,23 @@ query (void)
{ GIMP_PDB_IMAGE, "image", "Output image" } { GIMP_PDB_IMAGE, "image", "Output image" }
}; };
#define COMMON_SAVE_ARGS \
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },\
{ 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)" }
static GimpParamDef save_args_old[] =
{
COMMON_SAVE_ARGS
};
static GimpParamDef save_args[] = static GimpParamDef save_args[] =
{ {
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" }, COMMON_SAVE_ARGS,
{ GIMP_PDB_IMAGE, "image", "Input image" }, { GIMP_PDB_INT32, "save_transp_pixels", "Keep the color data masked by an alpha channel intact" }
{ 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)" }
}; };
gimp_install_procedure ("file_tiff_load", gimp_install_procedure ("file_tiff_load",
@ -200,6 +216,20 @@ query (void)
load_args, load_return_vals); load_args, load_return_vals);
gimp_install_procedure ("file_tiff_save", gimp_install_procedure ("file_tiff_save",
"saves files in the tiff file format",
"Saves 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",
"<Save>/Tiff",
"RGB*, GRAY*, INDEXED",
GIMP_PLUGIN,
G_N_ELEMENTS (save_args_old), 0,
save_args_old, NULL);
gimp_install_procedure ("file_tiff_save2",
"saves files in the tiff file format", "saves files in the tiff file format",
"Saves files in the Tagged Image File Format. " "Saves files in the Tagged Image File Format. "
"The value for the saved comment is taken " "The value for the saved comment is taken "
@ -262,8 +292,10 @@ run (const gchar *name,
status = GIMP_PDB_EXECUTION_ERROR; status = GIMP_PDB_EXECUTION_ERROR;
} }
} }
else if (strcmp (name, "file_tiff_save") == 0) else if (strcmp (name, "file_tiff_save") == 0
|| strcmp (name, "file_tiff_save2") == 0 )
{ {
/* Plug-in is either file_tiff_save or file_tiff_save2 */
image = orig_image = param[1].data.d_int32; image = orig_image = param[1].data.d_int32;
drawable = param[2].data.d_int32; drawable = param[2].data.d_int32;
@ -307,6 +339,8 @@ run (const gchar *name,
{ {
tsvals.compression = tsvals.compression =
((TiffSaveVals *) parasite->data)->compression; ((TiffSaveVals *) parasite->data)->compression;
tsvals.save_transp_pixels =
((TiffSaveVals *) parasite->data)->save_transp_pixels;
} }
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
@ -317,7 +351,7 @@ run (const gchar *name,
case GIMP_RUN_NONINTERACTIVE: case GIMP_RUN_NONINTERACTIVE:
/* Make sure all the arguments are there! */ /* Make sure all the arguments are there! */
if (nparams != 6) if (nparams != 6 || nparams != 7)
{ {
status = GIMP_PDB_CALLING_ERROR; status = GIMP_PDB_CALLING_ERROR;
} }
@ -332,6 +366,11 @@ run (const gchar *name,
case 4: tsvals.compression = COMPRESSION_JPEG; break; case 4: tsvals.compression = COMPRESSION_JPEG; break;
default: status = GIMP_PDB_CALLING_ERROR; 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 = FALSE;
} }
break; break;
@ -344,6 +383,8 @@ run (const gchar *name,
{ {
tsvals.compression = tsvals.compression =
((TiffSaveVals *) parasite->data)->compression; ((TiffSaveVals *) parasite->data)->compression;
tsvals.save_transp_pixels =
((TiffSaveVals *) parasite->data)->save_transp_pixels;
} }
gimp_parasite_free (parasite); gimp_parasite_free (parasite);
break; break;
@ -494,6 +535,23 @@ load_image (const gchar *filename)
if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA)) if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA))
{ {
alpha = TRUE; alpha = TRUE;
tsvals.save_transp_pixels = FALSE;
--extra;
}
else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA))
{
alpha = TRUE;
tsvals.save_transp_pixels = TRUE;
--extra;
}
else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
{
/* assuming unassociated alpha if unspecified */
g_message ("alpha channel type not defined for file %s. "
"Assuming alpha is not premultiplied",
filename);
alpha = TRUE;
tsvals.save_transp_pixels = TRUE;
--extra; --extra;
} }
else else
@ -680,6 +738,7 @@ load_image (const gchar *filename)
layer_offset_y_pixel = ROUND(layer_offset_y * yres); layer_offset_y_pixel = ROUND(layer_offset_y * yres);
} }
/* Install colormap for INDEXED images only */ /* Install colormap for INDEXED images only */
if (image_type == GIMP_INDEXED) if (image_type == GIMP_INDEXED)
{ {
@ -1051,16 +1110,24 @@ read_16bit (guchar *source,
case PHOTOMETRIC_MINISBLACK: case PHOTOMETRIC_MINISBLACK:
if (alpha) if (alpha)
{ {
gray_val = *source; source += 2; if (tsvals.save_transp_pixels)
alpha_val = *source; source += 2; {
gray_val = MIN (gray_val, alpha_val); *dest++ = *source; source += 2;
*dest++ = *source; source += 2;
if (alpha_val) }
*dest++ = gray_val * 255 / alpha_val;
else else
*dest++ = 0; {
gray_val = *source; source += 2;
alpha_val = *source; source += 2;
gray_val = MIN (gray_val, alpha_val);
*dest++ = alpha_val; if (alpha_val)
*dest++ = gray_val * 255 / alpha_val;
else
*dest++ = 0;
*dest++ = alpha_val;
}
} }
else else
{ {
@ -1071,16 +1138,24 @@ read_16bit (guchar *source,
case PHOTOMETRIC_MINISWHITE: case PHOTOMETRIC_MINISWHITE:
if (alpha) if (alpha)
{ {
gray_val = *source; source += 2; if (tsvals.save_transp_pixels)
alpha_val = *source; source += 2; {
gray_val = MIN (gray_val, alpha_val); *dest++ = *source; source += 2;
*dest++ = *source; source += 2;
if (alpha_val) }
*dest++ = ((alpha_val - gray_val) * 255) / alpha_val; else
else {
*dest++ = 0; gray_val = *source; source += 2;
alpha_val = *source; source += 2;
*dest++ = alpha_val; gray_val = MIN (gray_val, alpha_val);
if (alpha_val)
*dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
else
*dest++ = 0;
*dest++ = alpha_val;
}
} }
else else
{ {
@ -1096,26 +1171,36 @@ read_16bit (guchar *source,
case PHOTOMETRIC_RGB: case PHOTOMETRIC_RGB:
if (alpha) if (alpha)
{ {
red_val = *source; source += 2; if (tsvals.save_transp_pixels)
green_val = *source; source += 2;
blue_val = *source; source += 2;
alpha_val = *source; source += 2;
red_val = MIN (red_val, alpha_val);
green_val = MIN (green_val, alpha_val);
blue_val = MIN (blue_val, alpha_val);
if (alpha_val)
{ {
*dest++ = (red_val * 255) / alpha_val; *dest++ = *source; source += 2;
*dest++ = (green_val * 255) / alpha_val; *dest++ = *source; source += 2;
*dest++ = (blue_val * 255) / alpha_val; *dest++ = *source; source += 2;
*dest++ = *source; source += 2;
} }
else else
{ {
*dest++ = 0; red_val = *source; source += 2;
*dest++ = 0; green_val = *source; source += 2;
*dest++ = 0; blue_val = *source; source += 2;
alpha_val = *source; source += 2;
red_val = MIN (red_val, alpha_val);
green_val = MIN (green_val, alpha_val);
blue_val = MIN (blue_val, alpha_val);
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;
} }
*dest++ = alpha_val;
} }
else else
{ {
@ -1193,14 +1278,22 @@ read_8bit (guchar *source,
case PHOTOMETRIC_MINISBLACK: case PHOTOMETRIC_MINISBLACK:
if (alpha) if (alpha)
{ {
gray_val= *source++; if (tsvals.save_transp_pixels)
alpha_val= *source++; {
gray_val= MIN(gray_val, alpha_val); *dest++ = *source; source++;
if (alpha_val) *dest++ = *source; source++;
*dest++ = gray_val * 255 / alpha_val; }
else else
*dest++ = 0; {
*dest++ = alpha_val; gray_val= *source++;
alpha_val= *source++;
gray_val= MIN(gray_val, alpha_val);
if (alpha_val)
*dest++ = gray_val * 255 / alpha_val;
else
*dest++ = 0;
*dest++ = alpha_val;
}
} }
else else
{ {
@ -1211,15 +1304,23 @@ read_8bit (guchar *source,
case PHOTOMETRIC_MINISWHITE: case PHOTOMETRIC_MINISWHITE:
if (alpha) if (alpha)
{ {
gray_val = *source++; if (tsvals.save_transp_pixels)
alpha_val = *source++; {
gray_val = MIN (gray_val, alpha_val); *dest++ = *source; source++;
*dest++ = *source; source++;
if (alpha_val) }
*dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
else else
*dest++ = 0; {
*dest++ = alpha_val; gray_val = *source++;
alpha_val = *source++;
gray_val = MIN (gray_val, alpha_val);
if (alpha_val)
*dest++ = ((alpha_val - gray_val) * 255) / alpha_val;
else
*dest++ = 0;
*dest++ = alpha_val;
}
} }
else else
{ {
@ -1236,27 +1337,37 @@ read_8bit (guchar *source,
case PHOTOMETRIC_RGB: case PHOTOMETRIC_RGB:
if (alpha) if (alpha)
{ {
red_val = *source++; if (tsvals.save_transp_pixels)
green_val = *source++;
blue_val = *source++;
alpha_val = *source++;
red_val = MIN (red_val, alpha_val);
blue_val = MIN (blue_val, alpha_val);
green_val = MIN (green_val, alpha_val);
if (alpha_val)
{ {
*dest++ = (red_val * 255) / alpha_val; *dest++ = *source; source++;
*dest++ = (green_val * 255) / alpha_val; *dest++ = *source; source++;
*dest++ = (blue_val * 255) / alpha_val; *dest++ = *source; source++;
*dest++ = *source; source++;
} }
else else
{ {
*dest++ = 0; red_val = *source++;
*dest++ = 0; green_val = *source++;
*dest++ = 0; blue_val = *source++;
alpha_val = *source++;
red_val = MIN (red_val, alpha_val);
blue_val = MIN (blue_val, alpha_val);
green_val = MIN (green_val, alpha_val);
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;
} }
*dest++ = alpha_val;
} }
else else
{ {
@ -1349,14 +1460,22 @@ read_default (guchar *source,
if (alpha) if (alpha)
{ {
NEXTSAMPLE (alpha_val); NEXTSAMPLE (alpha_val);
gray_val= MIN (gray_val, alpha_val); if (tsvals.save_transp_pixels)
{
if (alpha_val) *dest++ = (gray_val * 255) / maxval;
*dest++ = (gray_val * 65025) / (alpha_val * maxval); *dest++ = alpha_val;
}
else else
*dest++ = 0; {
gray_val= MIN (gray_val, alpha_val);
*dest++ = alpha_val; if (alpha_val)
*dest++ = (gray_val * 65025) / (alpha_val * maxval);
else
*dest++ = 0;
*dest++ = alpha_val;
}
} }
else else
{ {
@ -1369,14 +1488,23 @@ read_default (guchar *source,
if (alpha) if (alpha)
{ {
NEXTSAMPLE (alpha_val); NEXTSAMPLE (alpha_val);
gray_val= MIN (gray_val, alpha_val); if (tsvals.save_transp_pixels)
{
if (alpha_val) *dest++ = ((maxval - gray_val) * 255) / maxval;
*dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval); *dest++ = alpha_val;
}
else else
*dest++ = 0; {
gray_val= MIN (gray_val, alpha_val);
if (alpha_val)
*dest++ = ((maxval - gray_val) * 65025) / (alpha_val * maxval);
else
*dest++ = 0;
*dest++ = alpha_val;
}
*dest++ = alpha_val;
} }
else else
{ {
@ -1397,24 +1525,34 @@ read_default (guchar *source,
if (alpha) if (alpha)
{ {
NEXTSAMPLE (alpha_val); NEXTSAMPLE (alpha_val);
red_val = MIN (red_val, alpha_val); if (tsvals.save_transp_pixels)
blue_val = MIN (blue_val, alpha_val);
green_val = MIN (green_val, alpha_val);
if (alpha_val)
{ {
*dest++ = (red_val * 255) / alpha_val; *dest++ = red_val;
*dest++ = (green_val * 255) / alpha_val; *dest++ = green_val;
*dest++ = (blue_val * 255) / alpha_val; *dest++ = blue_val;
*dest++ = alpha_val;
} }
else else
{ {
*dest++ = 0; red_val = MIN (red_val, alpha_val);
*dest++ = 0; blue_val = MIN (blue_val, alpha_val);
*dest++ = 0; green_val = MIN (green_val, alpha_val);
}
*dest++ = alpha_val; 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 else
{ {
@ -1671,7 +1809,7 @@ save_image (const gchar *filename,
} }
if (alpha) if (alpha)
{ {
extra_samples [0] = EXTRASAMPLE_ASSOCALPHA; extra_samples [0] = EXTRASAMPLE_UNASSALPHA;
TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples); TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
} }
TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric); TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
@ -1798,9 +1936,17 @@ save_image (const gchar *filename,
case GIMP_GRAYA_IMAGE: case GIMP_GRAYA_IMAGE:
for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel) for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
{ {
/* pre-multiply gray by alpha */ if (tsvals.save_transp_pixels)
data[col + 0] = (t[col + 0] * t[col + 1]) / 255; {
data[col + 1] = t[col + 1]; /* alpha channel */ data[col + 0] = t[col + 0];
}
else
{
/* 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); success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
break; break;
@ -1810,11 +1956,21 @@ save_image (const gchar *filename,
case GIMP_RGBA_IMAGE: case GIMP_RGBA_IMAGE:
for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel) for (col = 0; col < cols*samplesperpixel; col+=samplesperpixel)
{ {
/* pre-multiply rgb by alpha */ if (tsvals.save_transp_pixels)
data[col+0] = t[col + 0] * t[col + 3] / 255; {
data[col+1] = t[col + 1] * t[col + 3] / 255; data[col+0] = t[col + 0];
data[col+2] = t[col + 2] * t[col + 3] / 255; data[col+1] = t[col + 1];
data[col+3] = t[col + 3]; /* alpha channel */ data[col+2] = t[col + 2];
}
else
{
/* 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); success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
break; break;
@ -1851,6 +2007,7 @@ save_dialog (void)
GtkWidget *hbox; GtkWidget *hbox;
GtkWidget *label; GtkWidget *label;
GtkWidget *entry; GtkWidget *entry;
GtkWidget *toggle;
gboolean run; gboolean run;
dlg = gimp_dialog_new (_("Save as TIFF"), "tiff", dlg = gimp_dialog_new (_("Save as TIFF"), "tiff",
@ -1882,6 +2039,18 @@ save_dialog (void)
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame); gtk_widget_show (frame);
/* Keep colors behind alpha mask */
toggle = gtk_check_button_new_with_mnemonic
( _("Save _color values from transparent pixels"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
tsvals.save_transp_pixels);
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
gtk_widget_show (toggle);
g_signal_connect (toggle, "toggled",
G_CALLBACK (gimp_toggle_button_update),
&tsvals.save_transp_pixels);
/* comment entry */ /* comment entry */
hbox = gtk_hbox_new (FALSE, 4); hbox = gtk_hbox_new (FALSE, 4);
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);