Commit 6a19448d authored by Sven Neumann's avatar Sven Neumann

Undestructive File Export(TM)


--Sven

CVS ----------------------------------------------------------------------
parent 3ed87f4a
1999-10-03 Sven Neumann <sven@gimp.org>
* app/channel_ops.c: copy resolution and unit information when duplicating
an image
* libgimp/gimpimage.c
* libgimp/gimp.h: some wrappers for PDB functions
* libgimp/gimpexport.[ch]: new files
This fixes a longstanding bug and the MostFAQ: When saving a file, the
file-save plug-ins can use gimp_export_image() to get the file converted
to a format they can handle. The conversion is performed on a duplicate
of the image; the original image stays as is.
* libgimp/Makefile.am
* libgimp/gimpui.h
* po/POTFILES.in: integrate the new files
* plug-ins/bmp/Makefile.am
* plug-ins/bmp/bmp.c
* plug-ins/bmp/bmpwrite.c
* plug-ins/common/Makefile.am
* plug-ins/common/gif.c
* plug-ins/common/jpeg.c
* plug-ins/common/png.c
* plug-ins/common/ps.c
* plug-ins/common/tiff.c
* plug-ins/print/Makefile.am
* plug-ins/print/print.c: Use the new export functionality. The other save
plug-ins will follow...
1999-10-03 Michael Natterer <mitch@gimp.org>
* app/fileops.c
......
......@@ -565,6 +565,11 @@ duplicate (GimpImage *gimage)
new_gimage = gimage_new (gimage->width, gimage->height, gimage->base_type);
gimage_disable_undo (new_gimage);
/* Copy resolution and unit information */
new_gimage->xresolution = gimage->xresolution;
new_gimage->yresolution = gimage->yresolution;
new_gimage->unit = gimage->unit;
/* Copy-on-write the projection tilemanager so we don't have
* to reproject the new gimage - since if we do the duplicate
* operation correctly, the projection for the new gimage is
......
......@@ -565,6 +565,11 @@ duplicate (GimpImage *gimage)
new_gimage = gimage_new (gimage->width, gimage->height, gimage->base_type);
gimage_disable_undo (new_gimage);
/* Copy resolution and unit information */
new_gimage->xresolution = gimage->xresolution;
new_gimage->yresolution = gimage->yresolution;
new_gimage->unit = gimage->unit;
/* Copy-on-write the projection tilemanager so we don't have
* to reproject the new gimage - since if we do the duplicate
* operation correctly, the projection for the new gimage is
......
......@@ -565,6 +565,11 @@ duplicate (GimpImage *gimage)
new_gimage = gimage_new (gimage->width, gimage->height, gimage->base_type);
gimage_disable_undo (new_gimage);
/* Copy resolution and unit information */
new_gimage->xresolution = gimage->xresolution;
new_gimage->yresolution = gimage->yresolution;
new_gimage->unit = gimage->unit;
/* Copy-on-write the projection tilemanager so we don't have
* to reproject the new gimage - since if we do the duplicate
* operation correctly, the projection for the new gimage is
......
......@@ -565,6 +565,11 @@ duplicate (GimpImage *gimage)
new_gimage = gimage_new (gimage->width, gimage->height, gimage->base_type);
gimage_disable_undo (new_gimage);
/* Copy resolution and unit information */
new_gimage->xresolution = gimage->xresolution;
new_gimage->yresolution = gimage->yresolution;
new_gimage->unit = gimage->unit;
/* Copy-on-write the projection tilemanager so we don't have
* to reproject the new gimage - since if we do the duplicate
* operation correctly, the projection for the new gimage is
......
......@@ -565,6 +565,11 @@ duplicate (GimpImage *gimage)
new_gimage = gimage_new (gimage->width, gimage->height, gimage->base_type);
gimage_disable_undo (new_gimage);
/* Copy resolution and unit information */
new_gimage->xresolution = gimage->xresolution;
new_gimage->yresolution = gimage->yresolution;
new_gimage->unit = gimage->unit;
/* Copy-on-write the projection tilemanager so we don't have
* to reproject the new gimage - since if we do the duplicate
* operation correctly, the projection for the new gimage is
......
......@@ -98,6 +98,7 @@ libgimpui_la_SOURCES = \
gimpbrushmenu.c \
gimpgradientmenu.c \
gimppatternmenu.c \
gimpexport.c \
gimpchainbutton.c \
gimpfileselection.c \
gimppatheditor.c \
......@@ -111,6 +112,7 @@ gimpinclude_HEADERS = \
gimpchainbutton.h \
gimpenums.h \
gimpenv.h \
gimpexport.h \
gimpfeatures.h \
gimpfileselection.h \
gimplimits.h \
......
......@@ -423,6 +423,7 @@ void gimp_request_wakeups (void);
gint32 gimp_image_new (guint width,
guint height,
GImageType type);
gint32 gimp_image_duplicate (gint32 image_ID);
void gimp_image_delete (gint32 image_ID);
guint gimp_image_width (gint32 image_ID);
guint gimp_image_height (gint32 image_ID);
......@@ -505,8 +506,10 @@ Parasite *gimp_image_find_parasite (gint32 image_ID,
void gimp_image_attach_parasite (gint32 image_ID,
const Parasite *p);
void gimp_image_attach_new_parasite (gint32 image_ID,
const char *name, int flags,
int size, const void *data);
const char *name,
int flags,
int size,
const void *data);
void gimp_image_detach_parasite (gint32 image_ID,
const char *name);
void gimp_image_set_resolution (gint32 image_ID,
......@@ -518,15 +521,24 @@ void gimp_image_get_resolution (gint32 image_ID,
void gimp_image_set_unit (gint32 image_ID,
GUnit unit);
GUnit gimp_image_get_unit (gint32 image_ID);
gint32 gimp_image_get_layer_by_tattoo (gint32 image_ID,
gint32 tattoo);
gint32 gimp_image_get_channel_by_tattoo (gint32 image_ID,
gint32 tattoo);
guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
gint *width,
gint *height,
gint *bytes);
gint32 gimp_image_get_layer_by_tattoo (gint32 image_ID,
gint32 tattoo);
gint32 gimp_image_get_channel_by_tattoo (gint32 image_ID,
gint32 tattoo);
guchar * gimp_image_get_thumbnail_data (gint32 image_ID,
gint *width,
gint *height,
gint *bytes);
void gimp_image_convert_rgb (gint32 image_ID);
void gimp_image_convert_grayscale (gint32 image_ID);
void gimp_image_convert_indexed (gint32 image_ID,
gint dither_type,
gint palette_type,
gint num_colors,
gint alpha_dither,
gint remove_unused,
gchar *palette);
/****************************************
* Guides *
......
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gtk/gtk.h>
#include "gimp.h"
#include "gimpenums.h"
#include "gimpexport.h"
#include "gimpintl.h"
typedef void (* ExportFunc) (gint32 imageID, gint32 *drawable_ID);
/* the export action structure */
typedef struct
{
ExportFunc default_action;
ExportFunc alt_action;
gchar *reason;
gchar *possibilities[2];
gint choice;
} ExportAction;
/* the functions that do the actual export */
static void
export_merge (gint32 image_ID,
gint32 *drawable_ID)
{
*drawable_ID = gimp_image_merge_visible_layers (image_ID, GIMP_EXPAND_AS_NECESSARY);
}
static void
export_flatten (gint32 image_ID,
gint32 *drawable_ID)
{
*drawable_ID = gimp_image_flatten (image_ID);
}
static void
export_convert_rgb (gint32 image_ID,
gint32 *drawable_ID)
{
gimp_image_convert_rgb (image_ID);
}
static void
export_convert_grayscale (gint32 image_ID,
gint32 *drawable_ID)
{
gimp_image_convert_grayscale (image_ID);
}
static void
export_convert_indexed (gint32 image_ID,
gint32 *drawable_ID)
{
gint nlayers;
/* check alpha */
g_free (gimp_image_get_layers (image_ID, &nlayers));
if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID))
gimp_image_convert_indexed (image_ID, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 255, FALSE, FALSE, "");
else
gimp_image_convert_indexed (image_ID, GIMP_FS_DITHER, GIMP_MAKE_PALETTE, 256, FALSE, FALSE, "");
}
/* a set of predefined actions */
static ExportAction export_action_merge =
{
export_merge,
NULL,
N_("can't handle layers"),
{ N_("Merge visible layers"), NULL },
0
};
static ExportAction export_action_animate_or_merge =
{
NULL,
export_merge,
N_("can only handle layers as animation frames"),
{ N_("Save as animation"), N_("Merge visible layers") },
0
};
static ExportAction export_action_flatten =
{
&export_flatten,
NULL,
N_("can't handle transparency"),
{ N_("Flatten Image"), NULL },
0
};
static ExportAction export_action_convert_rgb =
{
&export_convert_rgb,
NULL,
N_("can only handle RGB images"),
{ N_("Convert to RGB"), NULL },
0
};
static ExportAction export_action_convert_grayscale =
{
&export_convert_grayscale,
NULL,
N_("can only handle grayscale images"),
{ N_("Convert to grayscale"), NULL },
0
};
static ExportAction export_action_convert_indexed =
{
&export_convert_indexed,
NULL,
N_("can only handle indexed images"),
{ N_("Convert to indexed using default settings\n(Do it manually to tune the result)"), NULL },
0
};
static ExportAction export_action_convert_rgb_or_grayscale =
{
&export_convert_rgb,
&export_convert_grayscale,
N_("can only handle RGB or grayscale images"),
{ N_("Convert to RGB"), N_("Convert to grayscale")},
0
};
static ExportAction export_action_convert_rgb_or_indexed =
{
&export_convert_rgb,
&export_convert_indexed,
N_("can only handle RGB or indexed images"),
{ N_("Convert to RGB"), N_("Convert to indexed using default settings\n(Do it manually to tune the result)")},
0
};
static ExportAction export_action_convert_indexed_or_grayscale =
{
&export_convert_indexed,
&export_convert_grayscale,
N_("can only handle grayscale or indexed images"),
{ N_("Convert to indexed using default settings\n(Do it manually to tune the result)"),
N_("Convert to grayscale") },
0
};
/* dialog functions */
static GtkWidget *dialog = NULL;
static gboolean dialog_return = FALSE;
static void
export_export_callback (GtkWidget *widget,
gpointer data)
{
gtk_widget_destroy (dialog);
dialog_return = TRUE;
}
static void
export_cancel_callback (GtkWidget *widget,
gpointer data)
{
dialog_return = FALSE;
dialog = NULL;
gtk_main_quit ();
}
static void
export_toggle_callback (GtkWidget *widget,
gpointer data)
{
gint *choice = (gint*)data;
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
*choice = 0;
else
*choice = 1;
}
static gint
export_dialog (GList *actions,
gchar *format)
{
GtkWidget *frame;
GtkWidget *button;
GtkWidget *hbox;
GtkWidget *hbbox;
GtkWidget *label;
GList *list;
gchar *text;
ExportAction *action;
dialog_return = FALSE;
g_return_val_if_fail (actions != NULL && format != NULL, FALSE);
/*
* Plug-ins have called gtk_init () before calling gimp_export ().
* Otherwise bad things will happen now!!
*/
/* the dialog */
dialog = gtk_dialog_new ();
gtk_window_set_title (GTK_WINDOW (dialog), _("Export File"));
gtk_window_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
(GtkSignalFunc) export_cancel_callback, NULL);
/* Action area */
gtk_container_set_border_width
(GTK_CONTAINER (GTK_DIALOG (dialog)->action_area), 2);
gtk_box_set_homogeneous (GTK_BOX (GTK_DIALOG (dialog)->action_area), FALSE);
hbbox = gtk_hbutton_box_new ();
gtk_button_box_set_spacing (GTK_BUTTON_BOX (hbbox), 4);
gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->action_area), hbbox,
FALSE, FALSE, 0);
gtk_widget_show (hbbox);
button = gtk_button_new_with_label (_("Export"));
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) export_export_callback, NULL);
gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 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 (dialog));
gtk_box_pack_start (GTK_BOX (hbbox), button, FALSE, FALSE, 0);
gtk_widget_show (button);
/* the headline */
gtk_container_set_border_width
(GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), 6);
label = gtk_label_new (_("Your image should be exported before it can be saved for the following reasons:"));
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 4);
gtk_widget_show (label);
for (list = actions; list; list = list->next)
{
action = (ExportAction*)(list->data);
text = g_strdup_printf ("%s %s", format, gettext (action->reason));
frame = gtk_frame_new (text);
g_free (text);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame, TRUE, TRUE, 4);
hbox = gtk_hbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER (frame), hbox);
if (action->possibilities[0] && action->possibilities[1])
{
GSList *radio_group = NULL;
button = gtk_radio_button_new_with_label (radio_group,
gettext (action->possibilities[0]));
gtk_label_set_justify (GTK_LABEL (GTK_BIN (button)->child), GTK_JUSTIFY_LEFT);
radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 2);
gtk_signal_connect (GTK_OBJECT (button), "toggled",
(GtkSignalFunc) export_toggle_callback, &action->choice);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
gtk_widget_show (button);
button = gtk_radio_button_new_with_label (radio_group,
gettext (action->possibilities[1]));
gtk_label_set_justify (GTK_LABEL (GTK_BIN (button)->child), GTK_JUSTIFY_LEFT);
radio_group = gtk_radio_button_group (GTK_RADIO_BUTTON (button));
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 4);
gtk_widget_show (button);
}
else if (action->possibilities[0])
{
label = gtk_label_new (gettext (action->possibilities[0]));
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
gtk_widget_show (label);
action->choice = 0;
}
else if (action->possibilities[1])
{
label = gtk_label_new (gettext (action->possibilities[1]));
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
gtk_widget_show (label);
action->choice = 1;
}
gtk_widget_show (hbox);
gtk_widget_show (frame);
}
/* the footline */
label = gtk_label_new (_("The export conversion won't modify your image."));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 4);
gtk_widget_show (label);
gtk_widget_show (dialog);
gtk_main ();
return (dialog_return);
}
gboolean
gimp_export_image (gint32 *image_ID_ptr,
gint32 *drawable_ID_ptr,
gchar *format,
gint cap) /* cap like capabilities */
{
GList *actions = NULL;
GList *list;
GimpImageBaseType type;
gint nlayers;
gboolean added_flatten = FALSE;
ExportAction *action;
g_return_val_if_fail (*image_ID_ptr > -1 && *drawable_ID_ptr > -1, FALSE);
/* check alpha */
g_free (gimp_image_get_layers (*image_ID_ptr, &nlayers));
if (nlayers > 1 || gimp_drawable_has_alpha (*drawable_ID_ptr))
{
if ( !(cap & CAN_HANDLE_ALPHA ) )
{
actions = g_list_append (actions, &export_action_flatten);
added_flatten = TRUE;
}
}
/* check multiple layers */
if (!added_flatten && nlayers > 1)
{
if ( !(cap & CAN_HANDLE_LAYERS) && (cap & CAN_HANDLE_LAYERS_AS_ANIMATION))
actions = g_list_append (actions, &export_action_animate_or_merge);
else if (! (cap & CAN_HANDLE_LAYERS))
actions = g_list_append (actions, &export_action_merge);
}
/* check the image type */
type = gimp_image_base_type (*image_ID_ptr);
switch (type)
{
case GIMP_RGB:
if ( !(cap & CAN_HANDLE_RGB) )
{
if ((cap & CAN_HANDLE_INDEXED) && (cap & CAN_HANDLE_GRAY))
actions = g_list_append (actions, &export_action_convert_indexed_or_grayscale);
else if (cap & CAN_HANDLE_INDEXED)
actions = g_list_append (actions, &export_action_convert_indexed);
else if (cap & CAN_HANDLE_GRAY)
actions = g_list_append (actions, &export_action_convert_grayscale);
}
break;
case GIMP_GRAY:
if ( !(cap & CAN_HANDLE_GRAY) )
{
if ((cap & CAN_HANDLE_RGB) && (cap & CAN_HANDLE_INDEXED))
actions = g_list_append (actions, &export_action_convert_rgb_or_indexed);
else if (cap & CAN_HANDLE_RGB)
actions = g_list_append (actions, &export_action_convert_rgb);
else if (cap & CAN_HANDLE_INDEXED)
actions = g_list_append (actions, &export_action_convert_indexed);
}
break;
case GIMP_INDEXED:
if ( !(cap & CAN_HANDLE_INDEXED) )
{
if ((cap & CAN_HANDLE_RGB) && (cap & CAN_HANDLE_GRAY))
actions = g_list_append (actions, &export_action_convert_rgb_or_grayscale);
else if (cap & CAN_HANDLE_RGB)
actions = g_list_append (actions, &export_action_convert_rgb);
else if (cap & CAN_HANDLE_GRAY)
actions = g_list_append (actions, &export_action_convert_grayscale);
}
break;
}
if (actions)
dialog_return = export_dialog (actions, format);
if (dialog_return)
{
*image_ID_ptr = gimp_image_duplicate (*image_ID_ptr);
*drawable_ID_ptr = gimp_image_get_active_layer (*image_ID_ptr);
gimp_image_disable_undo (*image_ID_ptr);
for (list = actions; list; list = list->next)
{
action = (ExportAction*)(list->data);
if (action->choice == 0 && action->default_action)
action->default_action (*image_ID_ptr, drawable_ID_ptr);
else if (action->choice == 1 && action->alt_action)
action->alt_action (*image_ID_ptr, drawable_ID_ptr);
}
return (TRUE);
}
return (FALSE);
}
/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1999 Peter Mattis and Spencer Kimball
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __GIMP_EXPORT_H__
#define __GIMP_EXPORT_H__
#include <glib.h>
#define CAN_HANDLE_RGB 1 << 0
#define CAN_HANDLE_GRAY 1 << 1
#define CAN_HANDLE_INDEXED 1 << 2
#define CAN_HANDLE_ALPHA 1 << 3
#define CAN_HANDLE_LAYERS 1 << 4
#define CAN_HANDLE_LAYERS_AS_ANIMATION 1 << 5
gboolean gimp_export_image (gint32*, /* image_ID */
gint32*, /* drawable_ID */
gchar*, /* format name */
gint); /* plug_in_capabilities */
#endif
......@@ -181,6 +181,27 @@ gimp_image_new (guint width,
return image_ID;
}
gint32
gimp_image_duplicate (gint32 image_ID)
{
GParam *return_vals;
int nreturn_vals;
gint32 new_image_ID;
return_vals = gimp_run_procedure ("gimp_channel_ops_duplicate",
&nreturn_vals,
PARAM_IMAGE, image_ID,
PARAM_END);
new_image_ID = -1;
if (return_vals[0].data.d_status == STATUS_SUCCESS)
new_image_ID = return_vals[1].data.d_image;
gimp_destroy_params (return_vals, nreturn_vals);
return (new_image_ID);
}
void
gimp_image_delete (gint32 image_ID)
{
......@@ -955,7 +976,8 @@ gimp_image_set_filename (gint32 image_ID,
}
Parasite *
gimp_image_find_parasite (gint32 image_ID, const char *name)
gimp_image_find_parasite (gint32 image_ID,
const char *name)
{
GParam *return_vals;
int nreturn_vals;
......@@ -979,7 +1001,8 @@ gimp_image_find_parasite (gint32 image_ID, const char *name)
}
void
gimp_image_attach_parasite (gint32 image_ID, const Parasite *p)
gimp_image_attach_parasite (gint32 image_ID,
const Parasite *p)
{
GParam *return_vals;
int nreturn_vals;
......@@ -994,8 +1017,11 @@ gimp_image_attach_parasite (gint32 image_ID, const Parasite *p)
}
void
gimp_image_attach_new_parasite (gint32 image_ID, const char *name, int flags,
int size, const void *data)
gimp_image_attach_new_parasite (gint32 image_ID,
const char *name,
int flags,
int size,
const void *data)
{
GParam *return_vals;
int nreturn_vals;
......@@ -1012,7 +1038,8 @@ gimp_image_attach_new_parasite (gint32 image_ID, const char *name, int flags,
}
void
gimp_image_detach_parasite (gint32 image_ID, const char *name)
gimp_image_detach_parasite (gint32 image_ID,
const char *name)
{
GParam *return_vals;
int nreturn_vals;
......@@ -1060,9 +1087,9 @@ gimp_image_get_resolution (gint32 image_ID,
}
void
gimp_image_set_resolution (gint32 image_ID,
double xresolution,
double yresolution)
gimp_image_set_resolution (gint32 image_ID,
double xresolution,
double yresolution)
{
GParam *return_vals;
int nreturn_vals;
......@@ -1078,7 +1105,7 @@ gimp_image_set_resolution (gint32 image_ID,
}
GUnit
gimp_image_get_unit (gint32 image_ID)