allow saving of images with alpha. If the image has alpha, offer the

2000-06-05  Michael Natterer  <mitch@gimp.org>

	* plug-ins/common/xbm.c: allow saving of images with alpha.
	If the image has alpha, offer the choice of ignoring it or
	alternatively saving it as a separate XBM file which is the
	mask of the X cursor we are saving.
This commit is contained in:
Michael Natterer
2000-06-05 11:38:35 +00:00
committed by Michael Natterer
parent c3131277d5
commit ef2d6c51f4
2 changed files with 275 additions and 202 deletions

View File

@ -1,3 +1,10 @@
2000-06-05 Michael Natterer <mitch@gimp.org>
* plug-ins/common/xbm.c: allow saving of images with alpha.
If the image has alpha, offer the choice of ignoring it or
alternatively saving it as a separate XBM file which is the
mask of the X cursor we are saving.
Sun Jun 4 20:17:25 2000 CET Austin Donnelly <austin@gimp.org>
* app/paths_dialog.c: run the sel2path plugin with a proper

View File

@ -31,7 +31,6 @@
* - Parsing is very tolerant, and the algorithms are quite hairy, so
* load_image should be carefully tested to make sure there are no XBM's
* that fail.
* - Allow the user to specify a hotspot, and preserve it when loading.
*/
/* Set this for debugging. */
@ -55,6 +54,7 @@
/* Wear your GIMP with pride! */
#define DEFAULT_USE_COMMENT TRUE
#define MAX_COMMENT 72
#define MAX_MASK_EXT 32
/* C identifier prefix. */
#define DEFAULT_PREFIX "bitmap"
@ -65,12 +65,14 @@
typedef struct _XBMSaveVals
{
gchar comment[MAX_COMMENT + 1];
gint x10_format;
gint use_hot;
gint x_hot;
gint y_hot;
gchar prefix[MAX_PREFIX + 1];
gchar comment[MAX_COMMENT + 1];
gint x10_format;
gint use_hot;
gint x_hot;
gint y_hot;
gchar prefix[MAX_PREFIX + 1];
gboolean write_mask;
gchar mask_ext[MAX_MASK_EXT + 1];
} XBMSaveVals;
static XBMSaveVals xsvals =
@ -81,6 +83,8 @@ static XBMSaveVals xsvals =
0, /* x_hot */
0, /* y_hot */
DEFAULT_PREFIX, /* prefix */
FALSE, /* write_mask */
"_mask"
};
typedef struct _XBMSaveInterface
@ -97,25 +101,30 @@ static XBMSaveInterface xsint =
/* Declare some local functions.
*/
static void query (void);
static void run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals);
static void run (gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
static gint32 load_image (gchar *filename);
static gint save_image (gchar *filename,
gint32 image_ID,
gint32 drawable_ID);
static gint save_dialog (gint32 drawable_ID);
static void save_ok_callback (GtkWidget *widget,
gpointer data);
static void comment_entry_callback (GtkWidget *widget,
gpointer data);
static void prefix_entry_callback (GtkWidget *widget,
gpointer data);
static gint32 load_image (gchar *filename);
static gint save_image (gchar *filename,
gchar *prefix,
gchar *comment,
gboolean save_mask,
gint32 image_ID,
gint32 drawable_ID);
static gint save_dialog (gint32 drawable_ID);
static void save_ok_callback (GtkWidget *widget,
gpointer data);
static void comment_entry_callback (GtkWidget *widget,
gpointer data);
static void prefix_entry_callback (GtkWidget *widget,
gpointer data);
static void mask_ext_entry_callback (GtkWidget *widget,
gpointer data);
GPlugInInfo PLUG_IN_INFO =
GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
@ -132,7 +141,7 @@ static int verbose = VERBOSE;
static void
query (void)
{
static GParamDef load_args[] =
static GimpParamDef load_args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_STRING, "filename", "The name of the file to load" },
@ -140,25 +149,27 @@ query (void)
};
static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
static GParamDef load_return_vals[] =
static GimpParamDef load_return_vals[] =
{
{ PARAM_IMAGE, "image", "Output image" }
};
static gint nload_return_vals = (sizeof (load_return_vals) /
sizeof (load_return_vals[0]));
static GParamDef save_args[] =
static GimpParamDef 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" },
{ PARAM_STRING, "raw_filename", "The name entered" },
{ PARAM_STRING, "comment", "Image description (maximum 72 bytes)" },
{ PARAM_INT32, "x10", "Save in X10 format" },
{ PARAM_INT32, "x_hot", "X coordinate of hotspot" },
{ PARAM_INT32, "y_hot", "Y coordinate of hotspot" },
{ PARAM_STRING, "prefix", "Identifier prefix [determined from filename]"}
{ 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" },
{ PARAM_STRING, "raw_filename", "The name entered" },
{ PARAM_STRING, "comment", "Image description (maximum 72 bytes)" },
{ PARAM_INT32, "x10", "Save in X10 format" },
{ PARAM_INT32, "x_hot", "X coordinate of hotspot" },
{ PARAM_INT32, "y_hot", "Y coordinate of hotspot" },
{ PARAM_STRING, "prefix", "Identifier prefix [determined from filename]"},
{ PARAM_INT32, "write_mask", "(0 = ignore, 1 = save as extra file)" },
{ PARAM_STRING, "mask_extension", "Extension of the mask file" }
} ;
static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
@ -194,18 +205,13 @@ query (void)
"");
}
static void
static gchar *
init_prefix (gchar *filename)
{
gchar *p, *prefix;
gint len;
/* Mangle the filename to get the prefix. */
prefix = strrchr (filename, '/');
if (prefix)
prefix ++;
else
prefix = filename;
prefix = g_basename (filename);
/* Strip any extension. */
p = strrchr (prefix, '.');
@ -216,21 +222,24 @@ init_prefix (gchar *filename)
memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
strncpy (xsvals.prefix, prefix, len);
return xsvals.prefix;
}
static void
run (gchar *name,
gint nparams,
GParam *param,
gint *nreturn_vals,
GParam **return_vals)
run (gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GParam values[2];
GRunModeType run_mode;
GStatusType status = STATUS_SUCCESS;
gint32 image_ID;
gint32 drawable_ID;
GimpParasite *parasite;
static GimpParam values[2];
GimpRunModeType run_mode;
GimpPDBStatusType status = STATUS_SUCCESS;
gint32 image_ID;
gint32 drawable_ID;
GimpParasite *parasite;
gchar *mask_filename = NULL;
GimpExportReturnType export = EXPORT_CANCEL;
INIT_I18N_UI();
@ -267,7 +276,7 @@ run (gchar *name,
{
image_ID = param[1].data.d_int32;
drawable_ID = param[2].data.d_int32;
/* eventually export the image */
switch (run_mode)
{
@ -275,7 +284,8 @@ run (gchar *name,
case RUN_WITH_LAST_VALS:
gimp_ui_init ("xbm", FALSE);
export = gimp_export_image (&image_ID, &drawable_ID, "XBM",
CAN_HANDLE_INDEXED);
CAN_HANDLE_INDEXED |
CAN_HANDLE_ALPHA);
if (export == EXPORT_CANCEL)
{
values[0].data.d_status = STATUS_CANCEL;
@ -294,8 +304,65 @@ run (gchar *name,
gimp_get_data ("file_xbm_save", &xsvals);
/* Always override the prefix with the filename. */
init_prefix (param[3].data.d_string);
mask_filename = g_strdup (init_prefix (param[3].data.d_string));
break;
case RUN_NONINTERACTIVE:
/* Make sure all the required arguments are there! */
if (nparams < 5)
{
status = STATUS_CALLING_ERROR;
}
else
{
gint i = 5;
if (nparams > i)
{
memset (xsvals.comment, 0, sizeof (xsvals.comment));
strncpy (xsvals.comment, param[i].data.d_string,
MAX_COMMENT);
}
i ++;
if (nparams > i)
xsvals.x10_format = (param[i].data.d_int32) ? TRUE : FALSE;
i += 2;
if (nparams > i)
{
/* They've asked for a hotspot. */
xsvals.use_hot = TRUE;
xsvals.x_hot = param[i - 1].data.d_int32;
xsvals.y_hot = param[i].data.d_int32;
}
mask_filename = g_strdup (init_prefix (param[3].data.d_string));
i ++;
if (nparams > i)
{
memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
strncpy (xsvals.prefix, param[i].data.d_string,
MAX_PREFIX);
}
i += 2;
if (nparams > i)
{
xsvals.write_mask = param[i - 1].data.d_int32;
memset (xsvals.mask_ext, 0, sizeof (xsvals.mask_ext));
strncpy (xsvals.mask_ext, param[i].data.d_string,
MAX_MASK_EXT);
}
i ++;
/* Too many arguments. */
if (nparams > i)
status = STATUS_CALLING_ERROR;
}
break;
default:
break;
}
@ -336,68 +403,59 @@ run (gchar *name,
gimp_parasite_free (parasite);
}
switch (run_mode)
if (run_mode == RUN_INTERACTIVE)
{
case RUN_INTERACTIVE:
/* Acquire information with a dialog */
if (! save_dialog (drawable_ID))
status = STATUS_CANCEL;
break;
case RUN_NONINTERACTIVE:
/* Make sure all the required arguments are there! */
if (nparams < 5)
{
status = STATUS_CALLING_ERROR;
}
else
{
gint i = 5;
if (nparams > i)
{
memset (xsvals.comment, 0, sizeof (xsvals.comment));
strncpy (xsvals.comment, param[i].data.d_string,
MAX_COMMENT);
}
i ++;
if (nparams > i)
xsvals.x10_format = (param[i].data.d_int32) ? TRUE : FALSE;
i += 2;
if (nparams > i)
{
/* They've asked for a hotspot. */
xsvals.use_hot = TRUE;
xsvals.x_hot = param[i - 1].data.d_int32;
xsvals.y_hot = param[i].data.d_int32;
}
i ++;
if (nparams > i)
{
memset (xsvals.prefix, 0, sizeof (xsvals.prefix));
strncpy (xsvals.prefix, param[i].data.d_string,
MAX_PREFIX);
}
else
init_prefix (param[3].data.d_string);
i ++;
/* Too many arguments. */
if (nparams > i)
status = STATUS_CALLING_ERROR;
}
break;
default:
break;
}
if (status == STATUS_SUCCESS)
{
if (save_image (param[3].data.d_string, image_ID, drawable_ID))
gchar *temp;
gchar *mask_prefix;
gchar *dirname;
temp = mask_filename;
if ((dirname = g_dirname (param[3].data.d_string)) != NULL)
{
mask_filename = g_strdup_printf ("%s/%s%s.xbm",
dirname, temp, xsvals.mask_ext);
g_free (dirname);
}
else
{
mask_filename = g_strdup_printf ("%s%s.xbm",
temp, xsvals.mask_ext);
}
g_free (temp);
/* Change any non-alphanumeric prefix characters to underscores. */
temp = xsvals.prefix;
while (*temp)
{
if (!isalnum (*temp))
*temp = '_';
temp ++;
}
mask_prefix = g_strdup_printf ("%s%s", xsvals.prefix, xsvals.mask_ext);
if (save_image (param[3].data.d_string,
xsvals.prefix,
xsvals.comment,
FALSE,
image_ID, drawable_ID) &&
xsvals.write_mask &&
save_image (mask_filename,
mask_prefix,
xsvals.comment,
TRUE,
image_ID, drawable_ID))
{
/* Store xsvals data */
gimp_set_data ("file_xbm_save", &xsvals, sizeof (xsvals));
@ -406,6 +464,9 @@ run (gchar *name,
{
status = STATUS_EXECUTION_ERROR;
}
g_free (mask_prefix);
g_free (mask_filename);
}
if (export == EXPORT_EXPORT)
@ -872,91 +933,53 @@ load_image (gchar *filename)
return image_ID;
}
static int gtk_initialized = FALSE;
static void
not_bw_dialog (void)
static gboolean
save_image (gchar *filename,
gchar *prefix,
gchar *comment,
gboolean save_mask,
gint32 image_ID,
gint32 drawable_ID)
{
GtkWidget *dlg, *label, *frame, *vbox;
if (!gtk_initialized)
{
fprintf (stderr, "XBM: can only save two color indexed images\n");
return;
}
dlg = gimp_dialog_new (_("XBM Warning"), "xbm",
gimp_standard_help_func, "filters/xbm.html",
GTK_WIN_POS_MOUSE,
FALSE, TRUE, FALSE,
_("Cancel"), gtk_main_quit,
NULL, NULL, NULL, TRUE, TRUE,
NULL);
/* the warning message */
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
gtk_container_border_width (GTK_CONTAINER (frame), 6);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame,
TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, 5);
gtk_container_border_width (GTK_CONTAINER (vbox), 4);
gtk_container_add (GTK_CONTAINER (frame), vbox);
label = gtk_label_new (_("The image which you are trying to save as\n"
"an XBM contains more than two colors.\n\n"
"Please convert it to a black and white\n"
"(1-bit) indexed image and try again."));
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
gtk_widget_show (vbox);
gtk_widget_show (frame);
gtk_widget_show (dlg);
gtk_main ();
gdk_flush ();
}
static gint
save_image (gchar *filename,
gint32 image_ID,
gint32 drawable_ID)
{
GDrawable *drawable;
GPixelRgn pixel_rgn;
GimpDrawable *drawable;
GimpPixelRgn pixel_rgn;
FILE *fp;
int width, height, colors, dark;
int intbits, lineints, need_comma, nints, rowoffset, tileheight;
int c, i, j, k, thisbit;
gint width, height, colors, dark;
gint intbits, lineints, need_comma, nints, rowoffset, tileheight;
gint c, i, j, k, thisbit;
guchar *data, *cmap, *name_buf;
char *prefix, *p, *intfmt;
gboolean has_alpha;
gint bpp;
guchar *data, *cmap, *name_buf, *intfmt;
drawable = gimp_drawable_get (drawable_ID);
width = drawable->width;
width = drawable->width;
height = drawable->height;
cmap = gimp_image_get_cmap (image_ID, &colors);
#if 0
printf ("XBM: run `gdb xbm' and `attach %d'\n", getpid ());
kill (getpid (), 19);
#endif
if (gimp_drawable_type (drawable_ID) != INDEXED_IMAGE || colors > 2)
if (!gimp_drawable_is_indexed (drawable_ID) || colors > 2)
{
/* The image is not black-and-white. */
not_bw_dialog ();
g_message (_("The image which you are trying to save as\n"
"an XBM contains more than two colors.\n\n"
"Please convert it to a black and white\n"
"(1-bit) indexed image and try again."));
return FALSE;
}
has_alpha = gimp_drawable_has_alpha (drawable_ID);
if (!has_alpha && save_mask)
{
g_message (_("You cannot save a cursor mask for an image\n"
"which has no alpha channel."));
return FALSE;
}
bpp = gimp_drawable_bpp (drawable_ID);
name_buf = g_strdup_printf (_("Saving %s:"), filename);
gimp_progress_init (name_buf);
g_free (name_buf);
@ -965,10 +988,10 @@ save_image (gchar *filename,
dark = 0;
if (colors > 1)
{
int first, second;
gint first, second;
/* Maybe the second color is darker than the first. */
first = (cmap[0] * cmap[0]) + (cmap[1] * cmap[1]) + (cmap[2] * cmap[2]);
first = (cmap[0] * cmap[0]) + (cmap[1] * cmap[1]) + (cmap[2] * cmap[2]);
second = (cmap[3] * cmap[3]) + (cmap[4] * cmap[4]) + (cmap[5] * cmap[5]);
if (second < first)
@ -984,21 +1007,11 @@ save_image (gchar *filename,
}
/* Maybe write the image comment. */
if (*xsvals.comment)
fprintf (fp, "/* %s */\n", xsvals.comment);
/* Change any non-alphanumeric prefix characters to underscores. */
prefix = xsvals.prefix;
p = prefix;
while (*p)
{
if (!isalnum (*p))
*p = '_';
p ++;
}
if (*comment)
fprintf (fp, "/* %s */\n", comment);
/* Write out the image height and width. */
fprintf (fp, "#define %s_width %d\n", prefix, width);
fprintf (fp, "#define %s_width %d\n", prefix, width);
fprintf (fp, "#define %s_height %d\n", prefix, height);
/* Write out the hotspot, if any. */
@ -1029,7 +1042,7 @@ save_image (gchar *filename,
/* Allocate a new set of pixels. */
tileheight = gimp_tile_height ();
data = (guchar *) g_malloc(width * tileheight);
data = (guchar *) g_malloc (width * tileheight * bpp);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height,
FALSE, FALSE);
@ -1052,11 +1065,11 @@ save_image (gchar *filename,
for (j = 0; j < tileheight; j ++)
{
/* Write out a row at a time. */
rowoffset = j * width;
rowoffset = j * width * bpp;
c = 0;
thisbit = 0;
for (k = 0; k < width; k ++)
for (k = 0; k < width * bpp; k += bpp)
{
if (k != 0 && thisbit == intbits)
{
@ -1079,7 +1092,17 @@ save_image (gchar *filename,
}
/* Pack INTBITS pixels into an integer. */
c |= ((data[rowoffset + k] == dark) ? 1 : 0) << (thisbit ++);
if (save_mask)
{
c |= ((data[rowoffset + k + 1] < 128) ? 1 : 0) << (thisbit ++);
}
else
{
if (has_alpha && (data[rowoffset + k + 1] < 128))
c |= 0 << (thisbit ++);
else
c |= ((data[rowoffset + k] == dark) ? 1 : 0) << (thisbit ++);
}
}
if (thisbit != 0)
@ -1221,6 +1244,40 @@ save_dialog (gint32 drawable_ID)
_("Y:"), 1.0, 0.5,
spinbutton, 1, TRUE);
/* mask file */
frame = gtk_frame_new (_("Mask File"));
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
table = gtk_table_new (2, 2, FALSE);
gtk_container_set_border_width (GTK_CONTAINER (table), 2);
gtk_table_set_col_spacings (GTK_TABLE (table), 4);
gtk_table_set_row_spacings (GTK_TABLE (table), 2);
gtk_container_add (GTK_CONTAINER (frame), table);
gtk_widget_show (table);
toggle = gtk_check_button_new_with_label (_("Write Extra Mask File"));
gtk_table_attach_defaults (GTK_TABLE (table), toggle, 0, 2, 0, 1);
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
GTK_SIGNAL_FUNC (gimp_toggle_button_update),
&xsvals.write_mask);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), xsvals.write_mask);
gtk_widget_show (toggle);
entry = gtk_entry_new_with_max_length (MAX_MASK_EXT);
gtk_entry_set_text (GTK_ENTRY (entry), xsvals.mask_ext);
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
_("Mask File Extension:"), 1.0, 0.5,
entry, 1, TRUE);
gtk_signal_connect (GTK_OBJECT (entry), "changed",
GTK_SIGNAL_FUNC (mask_ext_entry_callback),
NULL);
gtk_object_set_data (GTK_OBJECT (toggle), "set_sensitive", entry);
gtk_widget_set_sensitive (entry, xsvals.write_mask);
gtk_widget_set_sensitive (frame, gimp_drawable_has_alpha (drawable_ID));
/* Done. */
gtk_widget_show (vbox);
gtk_widget_show (dlg);
@ -1251,6 +1308,15 @@ prefix_entry_callback (GtkWidget *widget,
gtk_entry_get_text (GTK_ENTRY (widget)), MAX_PREFIX);
}
static void
mask_ext_entry_callback (GtkWidget *widget,
gpointer data)
{
memset (xsvals.prefix, 0, sizeof (xsvals.mask_ext));
strncpy (xsvals.mask_ext,
gtk_entry_get_text (GTK_ENTRY (widget)), MAX_MASK_EXT);
}
static void
save_ok_callback (GtkWidget *widget,
gpointer data)