1999-10-04 02:54:54 +08:00
|
|
|
/* LIBGIMP - The GIMP Library
|
|
|
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
|
|
|
*
|
2000-02-26 11:41:06 +08:00
|
|
|
* gimpexport.c
|
2004-11-30 09:43:54 +08:00
|
|
|
* Copyright (C) 1999-2004 Sven Neumann <sven@gimp.org>
|
2000-02-26 11:41:06 +08:00
|
|
|
*
|
2009-01-18 06:28:01 +08:00
|
|
|
* This library is free software: you can redistribute it and/or
|
1999-11-18 05:13:50 +08:00
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
1999-10-04 02:54:54 +08:00
|
|
|
* License as published by the Free Software Foundation; either
|
2009-01-18 06:28:01 +08:00
|
|
|
* version 3 of the License, or (at your option) any later version.
|
1999-10-04 02:54:54 +08:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
1999-11-18 05:13:50 +08:00
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2009-01-18 06:28:01 +08:00
|
|
|
* License along with this library. If not, see
|
2018-07-12 05:27:07 +08:00
|
|
|
* <https://www.gnu.org/licenses/>.
|
2003-11-06 23:27:05 +08:00
|
|
|
*/
|
2000-06-01 05:16:11 +08:00
|
|
|
|
1999-11-14 10:08:07 +08:00
|
|
|
#include "config.h"
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2006-06-27 06:48:05 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
#include <gtk/gtk.h>
|
|
|
|
|
|
|
|
#include "gimp.h"
|
2000-06-01 05:16:11 +08:00
|
|
|
#include "gimpui.h"
|
2000-01-27 09:54:52 +08:00
|
|
|
|
|
|
|
#include "libgimp-intl.h"
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2000-06-01 05:16:11 +08:00
|
|
|
|
2010-07-07 17:48:10 +08:00
|
|
|
/**
|
|
|
|
* SECTION: gimpexport
|
|
|
|
* @title: gimpexport
|
|
|
|
* @short_description: Export an image before it is saved.
|
|
|
|
*
|
|
|
|
* This function should be called by all save_plugins unless they are
|
|
|
|
* able to save all image formats GIMP knows about. It takes care of
|
|
|
|
* asking the user if she wishes to export the image to a format the
|
|
|
|
* save_plugin can handle. It then performs the necessary conversions
|
|
|
|
* (e.g. Flatten) on a copy of the image so that the image can be
|
|
|
|
* saved without changing the original image.
|
|
|
|
*
|
|
|
|
* The capabilities of the save_plugin are specified by combining
|
|
|
|
* #GimpExportCapabilities using a bitwise OR.
|
|
|
|
*
|
|
|
|
* Make sure you have initialized GTK+ before you call this function
|
|
|
|
* as it will most probably have to open a dialog.
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
2020-04-14 17:46:17 +08:00
|
|
|
typedef void (* ExportFunc) (GimpImage *image,
|
|
|
|
GList **drawables);
|
2000-11-18 08:25:42 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
|
|
|
|
/* the export action structure */
|
2003-11-06 23:27:05 +08:00
|
|
|
typedef struct
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2008-09-11 19:49:13 +08:00
|
|
|
ExportFunc default_action;
|
|
|
|
ExportFunc alt_action;
|
|
|
|
const gchar *reason;
|
|
|
|
const gchar *possibilities[2];
|
|
|
|
gint choice;
|
1999-10-04 02:54:54 +08:00
|
|
|
} ExportAction;
|
|
|
|
|
|
|
|
|
|
|
|
/* the functions that do the actual export */
|
1999-10-24 21:26:30 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_merge (GimpImage *image,
|
|
|
|
GList **drawables)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2000-02-09 01:04:19 +08:00
|
|
|
gint32 nvisible = 0;
|
1999-10-24 21:26:30 +08:00
|
|
|
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (image);
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = g_list_next (iter))
|
2000-02-09 01:04:19 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
if (gimp_item_get_visible (GIMP_ITEM (iter->data)))
|
2006-04-12 18:53:28 +08:00
|
|
|
nvisible++;
|
2000-02-09 01:04:19 +08:00
|
|
|
}
|
|
|
|
|
2001-10-25 01:34:44 +08:00
|
|
|
if (nvisible <= 1)
|
|
|
|
{
|
2020-06-28 00:11:40 +08:00
|
|
|
GimpLayer *transp;
|
|
|
|
GimpImageType layer_type;
|
2019-08-13 22:16:08 +08:00
|
|
|
|
2017-08-20 21:06:26 +08:00
|
|
|
/* if there is only one (or zero) visible layer, add a new
|
|
|
|
* transparent layer that has the same size as the canvas. The
|
|
|
|
* merge that follows will ensure that the offset, opacity and
|
|
|
|
* size are correct
|
|
|
|
*/
|
2021-04-06 06:47:07 +08:00
|
|
|
switch (gimp_image_get_base_type (image))
|
2020-06-28 00:11:40 +08:00
|
|
|
{
|
|
|
|
case GIMP_RGB:
|
|
|
|
layer_type = GIMP_RGBA_IMAGE;
|
|
|
|
break;
|
|
|
|
case GIMP_GRAY:
|
|
|
|
layer_type = GIMP_GRAYA_IMAGE;
|
|
|
|
break;
|
|
|
|
case GIMP_INDEXED:
|
|
|
|
layer_type = GIMP_INDEXEDA_IMAGE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* In case we add a new type in future. */
|
|
|
|
g_return_if_reached ();
|
|
|
|
}
|
2019-08-11 23:12:20 +08:00
|
|
|
transp = gimp_layer_new (image, "-",
|
2021-04-06 06:47:07 +08:00
|
|
|
gimp_image_get_width (image),
|
|
|
|
gimp_image_get_height (image),
|
2020-06-28 00:11:40 +08:00
|
|
|
layer_type,
|
2017-08-20 21:06:26 +08:00
|
|
|
100.0, GIMP_LAYER_MODE_NORMAL);
|
2019-08-13 22:16:08 +08:00
|
|
|
gimp_image_insert_layer (image, transp, NULL, 1);
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_selection_none (image);
|
2019-08-13 22:16:08 +08:00
|
|
|
gimp_drawable_edit_clear (GIMP_DRAWABLE (transp));
|
2001-10-25 01:34:44 +08:00
|
|
|
nvisible++;
|
|
|
|
}
|
2000-02-09 01:04:19 +08:00
|
|
|
|
|
|
|
if (nvisible > 1)
|
|
|
|
{
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *merged;
|
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
merged = gimp_image_merge_visible_layers (image, GIMP_CLIP_TO_IMAGE);
|
2000-02-09 01:04:19 +08:00
|
|
|
|
2020-04-14 17:46:17 +08:00
|
|
|
g_return_if_fail (merged != NULL);
|
|
|
|
|
|
|
|
*drawables = g_list_prepend (NULL, merged);
|
2003-11-06 23:27:05 +08:00
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
2019-08-27 19:26:27 +08:00
|
|
|
|
|
|
|
layers = gimp_image_list_layers (image);
|
2003-05-27 01:30:15 +08:00
|
|
|
|
|
|
|
/* make sure that the merged drawable matches the image size */
|
2021-04-06 20:28:40 +08:00
|
|
|
if (gimp_drawable_get_width (GIMP_DRAWABLE (merged)) !=
|
2021-04-06 06:47:07 +08:00
|
|
|
gimp_image_get_width (image) ||
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_height (GIMP_DRAWABLE (merged)) !=
|
2021-04-06 06:47:07 +08:00
|
|
|
gimp_image_get_height (image))
|
2003-05-27 01:30:15 +08:00
|
|
|
{
|
|
|
|
gint off_x, off_y;
|
|
|
|
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_offsets (GIMP_DRAWABLE (merged), &off_x, &off_y);
|
2003-05-27 01:30:15 +08:00
|
|
|
gimp_layer_resize (merged,
|
2021-04-06 06:47:07 +08:00
|
|
|
gimp_image_get_width (image),
|
|
|
|
gimp_image_get_height (image),
|
2003-05-27 01:30:15 +08:00
|
|
|
off_x, off_y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-06 23:27:05 +08:00
|
|
|
/* remove any remaining (invisible) layers */
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
1999-10-24 21:26:30 +08:00
|
|
|
{
|
2020-06-28 00:11:40 +08:00
|
|
|
if (! g_list_find (*drawables, iter->data))
|
2019-08-14 17:08:42 +08:00
|
|
|
gimp_image_remove_layer (image, iter->data);
|
1999-10-24 21:26:30 +08:00
|
|
|
}
|
2019-08-27 19:26:27 +08:00
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_flatten (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
2019-08-13 22:16:08 +08:00
|
|
|
GimpLayer *flattened;
|
2003-11-06 23:27:05 +08:00
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
flattened = gimp_image_flatten (image);
|
2000-02-09 01:04:19 +08:00
|
|
|
|
2019-08-13 22:16:08 +08:00
|
|
|
if (flattened != NULL)
|
2020-04-14 17:46:17 +08:00
|
|
|
*drawables = g_list_prepend (NULL, flattened);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
static void
|
|
|
|
export_merge_layer_effects_rec (GList *layers)
|
|
|
|
{
|
|
|
|
GList *iter;
|
|
|
|
|
|
|
|
for (iter = layers; iter; iter = g_list_next (iter))
|
|
|
|
if (gimp_item_is_group (iter->data))
|
|
|
|
{
|
|
|
|
GList *children = gimp_item_list_children (iter->data);
|
|
|
|
|
|
|
|
export_merge_layer_effects_rec (children);
|
|
|
|
|
|
|
|
g_list_free (children);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gimp_drawable_merge_filters (GIMP_DRAWABLE (iter->data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
static void
|
|
|
|
export_merge_layer_effects (GimpImage *image,
|
|
|
|
GList **drawables)
|
|
|
|
{
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
GList *layers;
|
2023-06-19 22:54:21 +08:00
|
|
|
|
|
|
|
layers = gimp_image_list_layers (image);
|
app: a few fixups to MR !958 (initial non-destructive editing).
- Do not leak allocated return value of gegl_node_to_xml_full().
- When merging layer effects, use gimp_drawable_filter_commit(), making
sure we use the exact same code path as when applying layer effects
destructively from the start. This also ensures that filters are
properly removed from the filter stack (unlike
gimp_drawable_merge_filter()), which was the reason why the rendering
was wrong (hence getting the buffer without effects first, then
reapplying it after was only a workaround to an actual bug).
- When removing a filter, verify the object still exists before doing
anything with it. If this was the last reference, we don't want to
call functions on this object. In gimp_drawable_filter_commit(), we
set up a weak pointer. In gimp_drawable_filter_remove_filter() itself,
we save the pointer to the filter's drawable before actual removal (as
we don't want to dereference a freed object later on).
- export_merge_layer_effects() should merge filters recursively through
layer groups.
- clean up the XCF code:
* No need to wrap the effect pointers list into 2 zero offset. Only
have one zero offset to indicate the list end.
* Add the layer effect mask in the effect structure (not in the layer
structure), similar as for layer masks.
* Effect name and icon made as main data in the structure, not as
properties.
2024-01-03 00:12:35 +08:00
|
|
|
export_merge_layer_effects_rec (layers);
|
2023-06-19 22:54:21 +08:00
|
|
|
g_list_free (layers);
|
|
|
|
}
|
|
|
|
|
2019-06-28 19:20:44 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_remove_alpha (GimpImage *image,
|
|
|
|
GList **drawables)
|
2019-06-28 19:20:44 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2019-06-28 19:20:44 +08:00
|
|
|
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (image);
|
2019-06-28 19:20:44 +08:00
|
|
|
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2019-06-28 19:20:44 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (iter->data)))
|
|
|
|
gimp_layer_flatten (iter->data);
|
2019-06-28 19:20:44 +08:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
2019-06-28 19:20:44 +08:00
|
|
|
}
|
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_apply_masks (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (image);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
2019-08-14 04:54:37 +08:00
|
|
|
GimpLayerMask *mask;
|
2019-08-13 22:16:08 +08:00
|
|
|
|
2019-08-14 17:08:42 +08:00
|
|
|
mask = gimp_layer_get_mask (iter->data);
|
2019-09-07 15:24:51 +08:00
|
|
|
|
2019-08-14 04:54:37 +08:00
|
|
|
if (mask)
|
2019-09-07 15:24:51 +08:00
|
|
|
{
|
|
|
|
/* we can't apply the mask directly to a layer group, so merge it
|
|
|
|
* first
|
|
|
|
*/
|
|
|
|
if (gimp_item_is_group (iter->data))
|
|
|
|
iter->data = gimp_image_merge_layer_group (image, iter->data);
|
|
|
|
|
|
|
|
gimp_layer_remove_mask (iter->data, GIMP_MASK_APPLY);
|
|
|
|
}
|
2003-11-16 00:51:20 +08:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
2003-11-16 00:51:20 +08:00
|
|
|
}
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_convert_rgb (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_rgb (image);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_convert_grayscale (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_grayscale (image);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_convert_indexed (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
2020-04-14 17:46:17 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
|
|
|
gboolean has_alpha = FALSE;
|
2003-11-06 23:27:05 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
/* check alpha */
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (image);
|
|
|
|
|
2020-04-14 17:46:17 +08:00
|
|
|
for (iter = *drawables; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
if (gimp_drawable_has_alpha (iter->data))
|
|
|
|
{
|
|
|
|
has_alpha = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (layers || has_alpha)
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_indexed (image,
|
2017-02-27 03:55:00 +08:00
|
|
|
GIMP_CONVERT_DITHER_NONE,
|
|
|
|
GIMP_CONVERT_PALETTE_GENERATE,
|
|
|
|
255, FALSE, FALSE, "");
|
1999-10-04 02:54:54 +08:00
|
|
|
else
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_indexed (image,
|
2017-02-27 03:55:00 +08:00
|
|
|
GIMP_CONVERT_DITHER_NONE,
|
|
|
|
GIMP_CONVERT_PALETTE_GENERATE,
|
|
|
|
256, FALSE, FALSE, "");
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
2003-11-16 00:51:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_convert_bitmap (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
2021-04-06 06:47:07 +08:00
|
|
|
if (gimp_image_get_base_type (image) == GIMP_INDEXED)
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_rgb (image);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_convert_indexed (image,
|
2017-02-27 03:55:00 +08:00
|
|
|
GIMP_CONVERT_DITHER_FS,
|
|
|
|
GIMP_CONVERT_PALETTE_GENERATE,
|
|
|
|
2, FALSE, FALSE, "");
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
1999-10-09 08:11:50 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_add_alpha (GimpImage *image,
|
|
|
|
GList **drawables)
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
1999-10-09 08:11:50 +08:00
|
|
|
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (image);
|
|
|
|
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
1999-10-09 08:11:50 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
if (! gimp_drawable_has_alpha (GIMP_DRAWABLE (iter->data)))
|
|
|
|
gimp_layer_add_alpha (GIMP_LAYER (iter->data));
|
1999-10-09 08:11:50 +08:00
|
|
|
}
|
2019-08-27 19:26:27 +08:00
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
1999-10-09 08:11:50 +08:00
|
|
|
}
|
|
|
|
|
2020-06-25 22:50:21 +08:00
|
|
|
static void
|
|
|
|
export_crop_image (GimpImage *image,
|
|
|
|
GList **drawables)
|
|
|
|
{
|
|
|
|
gimp_image_crop (image,
|
2021-04-06 06:47:07 +08:00
|
|
|
gimp_image_get_width (image),
|
|
|
|
gimp_image_get_height (image),
|
2020-06-25 22:50:21 +08:00
|
|
|
0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
export_resize_image (GimpImage *image,
|
|
|
|
GList **drawables)
|
|
|
|
{
|
|
|
|
gimp_image_resize_to_layers (image);
|
|
|
|
}
|
|
|
|
|
2008-11-25 16:20:56 +08:00
|
|
|
static void
|
2020-04-14 17:46:17 +08:00
|
|
|
export_void (GimpImage *image,
|
|
|
|
GList **drawables)
|
2008-11-25 16:20:56 +08:00
|
|
|
{
|
|
|
|
/* do nothing */
|
|
|
|
}
|
|
|
|
|
1999-10-24 21:26:30 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
/* a set of predefined actions */
|
|
|
|
|
|
|
|
static ExportAction export_action_merge =
|
|
|
|
{
|
|
|
|
export_merge,
|
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can't handle layers"),
|
2000-01-11 23:48:00 +08:00
|
|
|
{ N_("Merge Visible Layers"), NULL },
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2001-10-25 01:34:44 +08:00
|
|
|
static ExportAction export_action_merge_single =
|
|
|
|
{
|
|
|
|
export_merge,
|
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can't handle layer offsets, size or opacity"),
|
2001-10-25 01:34:44 +08:00
|
|
|
{ N_("Merge Visible Layers"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static ExportAction export_action_animate_or_merge =
|
|
|
|
{
|
2000-07-09 23:47:11 +08:00
|
|
|
NULL,
|
2016-09-04 23:10:57 +08:00
|
|
|
export_merge,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle layers as animation frames"),
|
2016-09-04 23:10:57 +08:00
|
|
|
{ N_("Save as Animation"), N_("Merge Visible Layers") },
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2000-04-08 04:04:00 +08:00
|
|
|
static ExportAction export_action_animate_or_flatten =
|
|
|
|
{
|
2000-07-09 23:47:11 +08:00
|
|
|
NULL,
|
2016-09-04 23:10:57 +08:00
|
|
|
export_flatten,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle layers as animation frames"),
|
2016-09-04 23:10:57 +08:00
|
|
|
{ N_("Save as Animation"), N_("Flatten Image") },
|
2000-04-08 04:04:00 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2006-03-09 19:12:55 +08:00
|
|
|
static ExportAction export_action_merge_or_flatten =
|
2000-06-26 07:18:56 +08:00
|
|
|
{
|
|
|
|
export_flatten,
|
2006-03-09 19:12:55 +08:00
|
|
|
export_merge,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can't handle layers"),
|
2006-03-09 19:12:55 +08:00
|
|
|
{ N_("Flatten Image"), N_("Merge Visible Layers") },
|
|
|
|
1
|
2000-06-26 07:18:56 +08:00
|
|
|
};
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static ExportAction export_action_flatten =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_flatten,
|
1999-10-04 02:54:54 +08:00
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can't handle transparency"),
|
1999-10-04 02:54:54 +08:00
|
|
|
{ N_("Flatten Image"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
static ExportAction export_action_merge_layer_effects =
|
|
|
|
{
|
|
|
|
export_merge_layer_effects,
|
|
|
|
NULL,
|
|
|
|
N_("%s plug-in can't handle layer effects"),
|
|
|
|
{ N_("Merge Layer Effects"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2019-06-28 19:20:44 +08:00
|
|
|
static ExportAction export_action_remove_alpha =
|
|
|
|
{
|
|
|
|
export_remove_alpha,
|
|
|
|
NULL,
|
|
|
|
N_("%s plug-in can't handle transparent layers"),
|
|
|
|
{ N_("Flatten Image"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
static ExportAction export_action_apply_masks =
|
|
|
|
{
|
|
|
|
export_apply_masks,
|
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can't handle layer masks"),
|
2003-11-16 00:51:20 +08:00
|
|
|
{ N_("Apply Layer Masks"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static ExportAction export_action_convert_rgb =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_rgb,
|
1999-10-04 02:54:54 +08:00
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle RGB images"),
|
1999-10-04 02:54:54 +08:00
|
|
|
{ N_("Convert to RGB"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
static ExportAction export_action_convert_grayscale =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_grayscale,
|
1999-10-04 02:54:54 +08:00
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle grayscale images"),
|
2000-01-11 23:48:00 +08:00
|
|
|
{ N_("Convert to Grayscale"), NULL },
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
static ExportAction export_action_convert_indexed =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_indexed,
|
1999-10-04 02:54:54 +08:00
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle indexed images"),
|
2000-06-28 06:02:27 +08:00
|
|
|
{ N_("Convert to Indexed using default settings\n"
|
2000-01-11 23:48:00 +08:00
|
|
|
"(Do it manually to tune the result)"), NULL },
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
static ExportAction export_action_convert_bitmap =
|
|
|
|
{
|
|
|
|
export_convert_bitmap,
|
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle bitmap (two color) indexed images"),
|
2003-11-16 00:51:20 +08:00
|
|
|
{ N_("Convert to Indexed using bitmap default settings\n"
|
|
|
|
"(Do it manually to tune the result)"), NULL },
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
static ExportAction export_action_convert_rgb_or_grayscale =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_rgb,
|
|
|
|
export_convert_grayscale,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle RGB or grayscale images"),
|
2000-01-11 23:48:00 +08:00
|
|
|
{ N_("Convert to RGB"), N_("Convert to Grayscale")},
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
static ExportAction export_action_convert_rgb_or_indexed =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_rgb,
|
|
|
|
export_convert_indexed,
|
2016-11-06 22:53:27 +08:00
|
|
|
N_("%s plug-in can only handle RGB or indexed images"),
|
2000-06-28 06:02:27 +08:00
|
|
|
{ N_("Convert to RGB"), N_("Convert to Indexed using default settings\n"
|
2006-04-12 18:53:28 +08:00
|
|
|
"(Do it manually to tune the result)")},
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
static ExportAction export_action_convert_indexed_or_grayscale =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_convert_indexed,
|
|
|
|
export_convert_grayscale,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in can only handle grayscale or indexed images"),
|
2000-06-28 06:02:27 +08:00
|
|
|
{ N_("Convert to Indexed using default settings\n"
|
2003-11-06 23:27:05 +08:00
|
|
|
"(Do it manually to tune the result)"),
|
2000-01-11 23:48:00 +08:00
|
|
|
N_("Convert to Grayscale") },
|
1999-10-04 02:54:54 +08:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
1999-10-09 08:11:50 +08:00
|
|
|
static ExportAction export_action_add_alpha =
|
|
|
|
{
|
2000-04-08 04:04:00 +08:00
|
|
|
export_add_alpha,
|
1999-10-09 08:11:50 +08:00
|
|
|
NULL,
|
2007-03-06 05:31:04 +08:00
|
|
|
N_("%s plug-in needs an alpha channel"),
|
2000-01-11 23:48:00 +08:00
|
|
|
{ N_("Add Alpha Channel"), NULL},
|
1999-10-09 08:11:50 +08:00
|
|
|
0
|
|
|
|
};
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2020-06-25 22:50:21 +08:00
|
|
|
static ExportAction export_action_crop_or_resize =
|
|
|
|
{
|
|
|
|
export_crop_image,
|
|
|
|
export_resize_image,
|
|
|
|
N_("%s plug-in needs to crop the layers to the image bounds"),
|
|
|
|
{ N_("Crop Layers"), N_("Resize Image to Layers")},
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
1999-10-24 21:26:30 +08:00
|
|
|
|
2008-11-25 16:20:56 +08:00
|
|
|
static ExportFunc
|
|
|
|
export_action_get_func (const ExportAction *action)
|
|
|
|
{
|
|
|
|
if (action->choice == 0 && action->default_action)
|
2016-09-04 23:10:57 +08:00
|
|
|
{
|
|
|
|
return action->default_action;
|
|
|
|
}
|
2008-11-25 16:20:56 +08:00
|
|
|
|
|
|
|
if (action->choice == 1 && action->alt_action)
|
2016-09-04 23:10:57 +08:00
|
|
|
{
|
|
|
|
return action->alt_action;
|
|
|
|
}
|
2008-11-25 16:20:56 +08:00
|
|
|
|
|
|
|
return export_void;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
export_action_perform (const ExportAction *action,
|
2019-08-11 23:12:20 +08:00
|
|
|
GimpImage *image,
|
2020-04-14 17:46:17 +08:00
|
|
|
GList **drawables)
|
2008-11-25 16:20:56 +08:00
|
|
|
{
|
2020-04-14 17:46:17 +08:00
|
|
|
export_action_get_func (action) (image, drawables);
|
2008-11-25 16:20:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
/* dialog functions */
|
|
|
|
|
2000-02-05 02:55:32 +08:00
|
|
|
/**
|
|
|
|
* gimp_export_image:
|
2019-08-11 23:12:20 +08:00
|
|
|
* @image: Pointer to the image.
|
2020-04-14 17:46:17 +08:00
|
|
|
* @n_drawables: Size of @drawables.
|
2020-10-28 01:40:24 +08:00
|
|
|
* @drawables: (array length=n_drawables): Array of pointers to drawables.
|
2019-08-11 23:12:20 +08:00
|
|
|
* @format_name: The (short) name of the image_format (e.g. JPEG or GIF).
|
2000-02-05 02:55:32 +08:00
|
|
|
* @capabilities: What can the image_format do?
|
|
|
|
*
|
2000-02-07 05:19:10 +08:00
|
|
|
* Takes an image and a drawable to be saved together with a
|
|
|
|
* description of the capabilities of the image_format. If the
|
|
|
|
* type of image doesn't match the capabilities of the format
|
2000-02-05 02:55:32 +08:00
|
|
|
* a dialog is opened that informs the user that the image has
|
|
|
|
* to be exported and offers to do the necessary conversions.
|
|
|
|
*
|
|
|
|
* If the user chooses to export the image, a copy is created.
|
2020-04-14 17:46:17 +08:00
|
|
|
* This copy is then converted, @image and @drawables are changed to
|
2019-08-11 23:12:20 +08:00
|
|
|
* point to the new image and the procedure returns GIMP_EXPORT_EXPORT.
|
|
|
|
* The save_plugin has to take care of deleting the created image using
|
2020-04-14 17:46:17 +08:00
|
|
|
* gimp_image_delete() and the drawables list with g_free() once the
|
|
|
|
* image has been saved.
|
2000-02-05 02:55:32 +08:00
|
|
|
*
|
2019-08-11 23:12:20 +08:00
|
|
|
* If the user chooses to Ignore the export problem, @image and
|
2020-04-14 17:46:17 +08:00
|
|
|
* @drawables are not altered, GIMP_EXPORT_IGNORE is returned and the
|
2019-08-11 23:12:20 +08:00
|
|
|
* save_plugin should try to save the original image. If the user
|
|
|
|
* chooses Cancel, GIMP_EXPORT_CANCEL is returned and the save_plugin
|
|
|
|
* should quit itself with status %GIMP_PDB_CANCEL.
|
2000-02-05 02:55:32 +08:00
|
|
|
*
|
2009-07-21 18:44:13 +08:00
|
|
|
* If @format_name is NULL, no dialogs will be shown and this function
|
|
|
|
* will behave as if the user clicked on the 'Export' button, if a
|
|
|
|
* dialog would have been shown.
|
|
|
|
*
|
2003-11-06 23:27:05 +08:00
|
|
|
* Returns: An enum of #GimpExportReturn describing the user_action.
|
2000-11-18 08:25:42 +08:00
|
|
|
**/
|
2003-11-06 23:27:05 +08:00
|
|
|
GimpExportReturn
|
2020-04-14 17:46:17 +08:00
|
|
|
gimp_export_image (GimpImage **image,
|
|
|
|
gint *n_drawables,
|
|
|
|
GimpDrawable ***drawables,
|
|
|
|
const gchar *format_name,
|
|
|
|
GimpExportCapabilities capabilities)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2003-11-06 23:27:05 +08:00
|
|
|
GSList *actions = NULL;
|
|
|
|
GimpImageBaseType type;
|
2019-08-14 17:08:42 +08:00
|
|
|
GList *layers;
|
|
|
|
GList *iter;
|
2020-04-14 17:46:17 +08:00
|
|
|
GType drawables_type = G_TYPE_NONE;
|
2003-11-16 00:51:20 +08:00
|
|
|
gboolean added_flatten = FALSE;
|
|
|
|
gboolean has_layer_masks = FALSE;
|
2003-11-06 23:27:05 +08:00
|
|
|
gboolean background_has_alpha = TRUE;
|
2006-06-27 14:41:38 +08:00
|
|
|
GimpExportReturn retval = GIMP_EXPORT_CANCEL;
|
2020-04-14 17:46:17 +08:00
|
|
|
gint i;
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2020-04-14 17:46:17 +08:00
|
|
|
g_return_val_if_fail (gimp_image_is_valid (*image) && drawables &&
|
|
|
|
n_drawables && *n_drawables > 0, FALSE);
|
|
|
|
|
|
|
|
for (i = 0; i < *n_drawables; i++)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (gimp_item_is_valid (GIMP_ITEM ((*drawables)[i])), FALSE);
|
|
|
|
|
|
|
|
if (drawables_type == G_TYPE_NONE ||
|
|
|
|
g_type_is_a (drawables_type, G_OBJECT_TYPE ((*drawables)[i])))
|
|
|
|
drawables_type = G_OBJECT_TYPE ((*drawables)[i]);
|
|
|
|
else
|
|
|
|
g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE ((*drawables)[i]), drawables_type), FALSE);
|
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
|
1999-10-09 08:11:50 +08:00
|
|
|
/* do some sanity checks */
|
2000-08-24 22:17:34 +08:00
|
|
|
if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
|
2003-11-08 01:13:45 +08:00
|
|
|
capabilities |= GIMP_EXPORT_CAN_HANDLE_ALPHA;
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2000-08-24 22:17:34 +08:00
|
|
|
if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
|
|
|
|
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
|
1999-10-09 08:11:50 +08:00
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS)
|
|
|
|
capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
|
|
|
|
|
2023-06-19 22:54:21 +08:00
|
|
|
/* Merge down layer effects for non-project file formats */
|
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_EFFECTS))
|
|
|
|
actions = g_slist_prepend (actions, &export_action_merge_layer_effects);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
|
|
|
/* check alpha and layer masks */
|
2019-08-27 19:26:27 +08:00
|
|
|
layers = gimp_image_list_layers (*image);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GimpLayer *layer = GIMP_LAYER (iter->data);
|
2019-08-13 22:16:08 +08:00
|
|
|
|
|
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
|
2006-04-12 18:53:28 +08:00
|
|
|
{
|
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA))
|
|
|
|
{
|
2019-06-28 19:20:44 +08:00
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions, &export_action_flatten);
|
|
|
|
added_flatten = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions, &export_action_remove_alpha);
|
|
|
|
break;
|
|
|
|
}
|
2006-04-12 18:53:28 +08:00
|
|
|
}
|
|
|
|
}
|
2003-11-06 23:27:05 +08:00
|
|
|
else
|
2006-04-12 18:53:28 +08:00
|
|
|
{
|
2003-11-16 00:51:20 +08:00
|
|
|
/* If this is the last layer, it's visible and has no alpha
|
|
|
|
* channel, then the image has a "flat" background
|
|
|
|
*/
|
2019-08-14 17:08:42 +08:00
|
|
|
if (iter->next == NULL && gimp_item_get_visible (GIMP_ITEM (layer)))
|
2006-04-12 18:53:28 +08:00
|
|
|
background_has_alpha = FALSE;
|
|
|
|
|
|
|
|
if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions, &export_action_add_alpha);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
if (! added_flatten)
|
2001-10-25 01:34:44 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
for (iter = layers; iter; iter = iter->next)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
2019-08-15 18:12:25 +08:00
|
|
|
if (gimp_layer_get_mask (iter->data))
|
2003-11-16 00:51:20 +08:00
|
|
|
has_layer_masks = TRUE;
|
|
|
|
}
|
2001-10-25 01:34:44 +08:00
|
|
|
}
|
2003-11-16 00:51:20 +08:00
|
|
|
|
|
|
|
if (! added_flatten)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2019-08-14 17:08:42 +08:00
|
|
|
GimpLayer *layer = GIMP_LAYER (layers->data);
|
|
|
|
GList *children;
|
2013-11-10 08:54:45 +08:00
|
|
|
|
2019-08-27 19:26:27 +08:00
|
|
|
children = gimp_item_list_children (GIMP_ITEM (layer));
|
2013-11-10 08:54:45 +08:00
|
|
|
|
2020-06-25 22:50:21 +08:00
|
|
|
if ((capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS) &&
|
|
|
|
(capabilities & GIMP_EXPORT_NEEDS_CROP))
|
|
|
|
{
|
|
|
|
GeglRectangle image_bounds;
|
|
|
|
gboolean needs_crop = FALSE;
|
|
|
|
|
|
|
|
image_bounds.x = 0;
|
|
|
|
image_bounds.y = 0;
|
2021-04-06 06:47:07 +08:00
|
|
|
image_bounds.width = gimp_image_get_width (*image);
|
|
|
|
image_bounds.height = gimp_image_get_height (*image);
|
2020-06-25 22:50:21 +08:00
|
|
|
|
|
|
|
for (iter = layers; iter; iter = iter->next)
|
|
|
|
{
|
|
|
|
GimpDrawable *drawable = iter->data;
|
|
|
|
GeglRectangle layer_bounds;
|
|
|
|
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_offsets (drawable,
|
2020-06-25 22:50:21 +08:00
|
|
|
&layer_bounds.x, &layer_bounds.y);
|
|
|
|
|
2021-04-06 20:28:40 +08:00
|
|
|
layer_bounds.width = gimp_drawable_get_width (drawable);
|
|
|
|
layer_bounds.height = gimp_drawable_get_height (drawable);
|
2020-06-25 22:50:21 +08:00
|
|
|
|
|
|
|
if (! gegl_rectangle_contains (&image_bounds, &layer_bounds))
|
|
|
|
{
|
|
|
|
needs_crop = TRUE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needs_crop)
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_crop_or_resize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-11-16 00:51:20 +08:00
|
|
|
/* check if layer size != canvas size, opacity != 100%, or offsets != 0 */
|
2019-08-14 17:08:42 +08:00
|
|
|
if (g_list_length (layers) == 1 &&
|
2013-11-10 08:54:45 +08:00
|
|
|
! children &&
|
2020-04-14 17:46:17 +08:00
|
|
|
g_type_is_a (drawables_type, GIMP_TYPE_LAYER) &&
|
2003-11-16 00:51:20 +08:00
|
|
|
! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
|
|
|
|
{
|
2020-04-14 17:46:17 +08:00
|
|
|
GimpDrawable *drawable = (*drawables)[0];
|
|
|
|
gint offset_x;
|
|
|
|
gint offset_y;
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_offsets (drawable, &offset_x, &offset_y);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
2020-04-14 17:46:17 +08:00
|
|
|
if ((gimp_layer_get_opacity (GIMP_LAYER (drawable)) < 100.0) ||
|
2021-04-06 06:47:07 +08:00
|
|
|
(gimp_image_get_width (*image) !=
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_width (drawable)) ||
|
2021-04-06 06:47:07 +08:00
|
|
|
(gimp_image_get_height (*image) !=
|
2021-04-06 20:28:40 +08:00
|
|
|
gimp_drawable_get_height (drawable)) ||
|
2003-11-16 00:51:20 +08:00
|
|
|
offset_x || offset_y)
|
|
|
|
{
|
|
|
|
if (capabilities & GIMP_EXPORT_CAN_HANDLE_ALPHA)
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_merge_single);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_flatten);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* check multiple layers */
|
2019-08-14 17:08:42 +08:00
|
|
|
else if (layers && layers->next != NULL)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
|
|
|
if (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION)
|
|
|
|
{
|
2006-03-09 19:12:55 +08:00
|
|
|
if (background_has_alpha ||
|
|
|
|
capabilities & GIMP_EXPORT_NEEDS_ALPHA)
|
2003-11-16 00:51:20 +08:00
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_animate_or_merge);
|
|
|
|
else
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_animate_or_flatten);
|
|
|
|
}
|
|
|
|
else if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
|
|
|
|
{
|
2006-03-09 19:12:55 +08:00
|
|
|
if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
|
2003-11-16 00:51:20 +08:00
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_merge);
|
|
|
|
else
|
|
|
|
actions = g_slist_prepend (actions,
|
2006-03-09 19:12:55 +08:00
|
|
|
&export_action_merge_or_flatten);
|
2003-11-16 00:51:20 +08:00
|
|
|
}
|
|
|
|
}
|
2013-11-10 08:54:45 +08:00
|
|
|
/* check for a single toplevel layer group */
|
|
|
|
else if (children)
|
|
|
|
{
|
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYERS))
|
|
|
|
{
|
|
|
|
if (capabilities & GIMP_EXPORT_NEEDS_ALPHA)
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_merge);
|
|
|
|
else
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_merge_or_flatten);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (children);
|
2003-11-16 00:51:20 +08:00
|
|
|
|
|
|
|
/* check layer masks */
|
|
|
|
if (has_layer_masks &&
|
|
|
|
! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS))
|
|
|
|
actions = g_slist_prepend (actions, &export_action_apply_masks);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
|
|
|
|
2019-08-15 18:12:25 +08:00
|
|
|
g_list_free (layers);
|
2013-11-10 08:54:45 +08:00
|
|
|
|
2003-11-06 23:27:05 +08:00
|
|
|
/* check the image type */
|
2021-04-06 06:47:07 +08:00
|
|
|
type = gimp_image_get_base_type (*image);
|
1999-10-04 02:54:54 +08:00
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case GIMP_RGB:
|
2003-11-16 00:51:20 +08:00
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB))
|
2006-04-12 18:53:28 +08:00
|
|
|
{
|
|
|
|
if ((capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED) &&
|
2002-09-17 20:40:13 +08:00
|
|
|
(capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
|
2006-04-12 18:53:28 +08:00
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_indexed_or_grayscale);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_indexed);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_grayscale);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
|
|
|
|
actions = g_slist_prepend (actions,
|
2003-11-16 00:51:20 +08:00
|
|
|
&export_action_convert_bitmap);
|
2006-04-12 18:53:28 +08:00
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
break;
|
2003-11-16 00:51:20 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
case GIMP_GRAY:
|
2003-11-16 00:51:20 +08:00
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
|
2006-04-12 18:53:28 +08:00
|
|
|
{
|
|
|
|
if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
|
2002-09-17 20:40:13 +08:00
|
|
|
(capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
|
2006-04-12 18:53:28 +08:00
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_rgb_or_indexed);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_rgb);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_indexed);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
|
|
|
|
actions = g_slist_prepend (actions,
|
2003-11-16 00:51:20 +08:00
|
|
|
&export_action_convert_bitmap);
|
2006-04-12 18:53:28 +08:00
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
break;
|
2003-11-16 00:51:20 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
case GIMP_INDEXED:
|
2003-11-16 00:51:20 +08:00
|
|
|
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_INDEXED))
|
|
|
|
{
|
|
|
|
if ((capabilities & GIMP_EXPORT_CAN_HANDLE_RGB) &&
|
2002-09-17 20:40:13 +08:00
|
|
|
(capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY))
|
2003-11-16 00:51:20 +08:00
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_rgb_or_grayscale);
|
2003-11-16 00:51:20 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_RGB)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_rgb);
|
2003-11-16 00:51:20 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_GRAY)
|
|
|
|
actions = g_slist_prepend (actions,
|
2002-09-17 20:40:13 +08:00
|
|
|
&export_action_convert_grayscale);
|
2006-04-12 18:53:28 +08:00
|
|
|
else if (capabilities & GIMP_EXPORT_CAN_HANDLE_BITMAP)
|
2003-11-16 00:51:20 +08:00
|
|
|
{
|
|
|
|
gint n_colors;
|
|
|
|
|
2023-05-24 05:37:46 +08:00
|
|
|
g_free (gimp_image_get_colormap (*image, NULL, &n_colors));
|
2003-11-16 00:51:20 +08:00
|
|
|
|
|
|
|
if (n_colors > 2)
|
|
|
|
actions = g_slist_prepend (actions,
|
|
|
|
&export_action_convert_bitmap);
|
|
|
|
}
|
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
break;
|
|
|
|
}
|
2003-11-06 23:27:05 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
if (actions)
|
1999-10-22 03:35:35 +08:00
|
|
|
{
|
|
|
|
actions = g_slist_reverse (actions);
|
2006-06-27 14:41:38 +08:00
|
|
|
|
libgimp: clean out the "interactive" intermediate dialog which was hidden…
… since forever anyway!
GIMP used to have a second export dialog, a generically generated one, appearing
either before or after (depending on when gimp_export_image() was called) the
custom export dialog implemented by the plug-in code. This has been hidden deep
in code since forever (since version 2.8.0 in fact, I believe) and only kept
hidden behind an environment variable "GIMP_INTERACTIVE_EXPORT". I don't think
we'll ever revive this, so let's clean up.
In fact, not one, but in worst case even 2 more dialogs were hidden behind this
variable! The first dialog (confirm_save_dialog()) was a confirmation when the
selected drawable was a layer mask or a channel (and not a layer). Most export
code don't even seem to care about the selected drawables anymore anyway (cf.
issue #7370), except with gimp_file_save() non-interactively (issue #8855),
which is a real mess of inconsistency anyway.
The second dialog (export_dialog()) was listing the various actions to do on a
copy of the image to help the plug-in (e.g. merge layers/flatten image, etc.)
and possibly give choices to some of these actions. Though there is definitely
no reason to request this kind of thing anymore, especially for a short-lasting
image copy, the list of action could still be interesting in the future, not as
information of what is going to be done, but as information of the kind of data
loss of the exported format. I could imagine we want to be able to reuse such
information for generating types of data loss per format in the export dialog,
in particular in the context of my long-term export workflow refactoring (from
which resizing before export such as #2531 are part of, but the whole
refactoring project is much wider).
In the whole discussion of #5858, there will be the question on whether we don't
want plug-ins to be directly given a "ready-to-use" image depending on
capabilities they advertized in create_procedure().
2024-04-21 06:16:27 +08:00
|
|
|
retval = GIMP_EXPORT_EXPORT;
|
1999-10-22 03:35:35 +08:00
|
|
|
}
|
1999-10-04 14:36:38 +08:00
|
|
|
else
|
2003-11-06 23:27:05 +08:00
|
|
|
{
|
|
|
|
retval = GIMP_EXPORT_IGNORE;
|
|
|
|
}
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2003-11-06 23:27:05 +08:00
|
|
|
if (retval == GIMP_EXPORT_EXPORT)
|
1999-10-04 02:54:54 +08:00
|
|
|
{
|
2008-11-25 16:20:56 +08:00
|
|
|
GSList *list;
|
2020-04-14 17:46:17 +08:00
|
|
|
GList *drawables_in;
|
|
|
|
GList *drawables_out;
|
|
|
|
gint i;
|
2008-11-25 16:20:56 +08:00
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
*image = gimp_image_duplicate (*image);
|
2020-04-14 17:46:17 +08:00
|
|
|
drawables_in = gimp_image_list_selected_layers (*image);
|
|
|
|
drawables_out = drawables_in;
|
2003-11-06 23:27:05 +08:00
|
|
|
|
2019-08-11 23:12:20 +08:00
|
|
|
gimp_image_undo_disable (*image);
|
2003-11-06 23:27:05 +08:00
|
|
|
|
1999-10-04 02:54:54 +08:00
|
|
|
for (list = actions; list; list = list->next)
|
2006-04-12 18:53:28 +08:00
|
|
|
{
|
2020-04-14 17:46:17 +08:00
|
|
|
export_action_perform (list->data, *image, &drawables_out);
|
|
|
|
|
|
|
|
if (drawables_in != drawables_out)
|
|
|
|
{
|
|
|
|
g_list_free (drawables_in);
|
|
|
|
drawables_in = drawables_out;
|
|
|
|
}
|
2006-04-12 18:53:28 +08:00
|
|
|
}
|
2020-04-14 17:46:17 +08:00
|
|
|
|
|
|
|
*n_drawables = g_list_length (drawables_out);
|
|
|
|
*drawables = g_new (GimpDrawable *, *n_drawables);
|
|
|
|
for (iter = drawables_out, i = 0; iter; iter = iter->next, i++)
|
|
|
|
(*drawables)[i] = iter->data;
|
|
|
|
|
|
|
|
g_list_free (drawables_out);
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
2003-11-06 23:27:05 +08:00
|
|
|
|
1999-10-24 21:26:30 +08:00
|
|
|
g_slist_free (actions);
|
1999-10-04 02:54:54 +08:00
|
|
|
|
2003-11-06 23:27:05 +08:00
|
|
|
return retval;
|
1999-10-04 02:54:54 +08:00
|
|
|
}
|
2009-07-15 18:12:13 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_export_dialog_new:
|
|
|
|
* @format_name: The short name of the image_format (e.g. JPEG or PNG).
|
|
|
|
* @role: The dialog's @role which will be set with
|
|
|
|
* gtk_window_set_role().
|
|
|
|
* @help_id: The GIMP help id.
|
|
|
|
*
|
|
|
|
* Creates a new export dialog. All file plug-ins should use this
|
|
|
|
* dialog to get a consistent look on the export dialogs. Use
|
2020-05-08 21:23:56 +08:00
|
|
|
* gimp_export_dialog_get_content_area() to get a vertical #GtkBox to be
|
2009-07-15 18:12:13 +08:00
|
|
|
* filled with export options. The export dialog is a wrapped
|
|
|
|
* #GimpDialog.
|
|
|
|
*
|
|
|
|
* The dialog response when the user clicks on the Export button is
|
|
|
|
* %GTK_RESPONSE_OK, and when the Cancel button is clicked it is
|
|
|
|
* %GTK_RESPONSE_CANCEL.
|
|
|
|
*
|
2019-08-01 07:52:07 +08:00
|
|
|
* Returns: (transfer full): The new export dialog.
|
2009-07-15 18:12:13 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.8
|
2009-07-15 18:12:13 +08:00
|
|
|
**/
|
|
|
|
GtkWidget *
|
|
|
|
gimp_export_dialog_new (const gchar *format_name,
|
|
|
|
const gchar *role,
|
|
|
|
const gchar *help_id)
|
|
|
|
{
|
2017-02-12 23:10:50 +08:00
|
|
|
GtkWidget *dialog;
|
2019-03-20 05:26:08 +08:00
|
|
|
/* TRANSLATORS: the %s parameter is an image format name (ex: PNG). */
|
|
|
|
gchar *title = g_strdup_printf (_("Export Image as %s"), format_name);
|
2009-07-15 18:12:13 +08:00
|
|
|
|
|
|
|
dialog = gimp_dialog_new (title, role,
|
|
|
|
NULL, 0,
|
|
|
|
gimp_standard_help_func, help_id,
|
|
|
|
|
2017-02-12 23:10:50 +08:00
|
|
|
_("_Cancel"), GTK_RESPONSE_CANCEL,
|
|
|
|
_("_Export"), GTK_RESPONSE_OK,
|
|
|
|
|
|
|
|
NULL);
|
2009-07-15 18:12:13 +08:00
|
|
|
|
2018-05-10 23:04:37 +08:00
|
|
|
gimp_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
|
2009-07-15 18:12:13 +08:00
|
|
|
GTK_RESPONSE_OK,
|
|
|
|
GTK_RESPONSE_CANCEL,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
gimp_window_set_transient (GTK_WINDOW (dialog));
|
|
|
|
|
|
|
|
g_free (title);
|
|
|
|
|
|
|
|
return dialog;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gimp_export_dialog_get_content_area:
|
|
|
|
* @dialog: A dialog created with gimp_export_dialog_new()
|
|
|
|
*
|
2020-05-08 21:23:56 +08:00
|
|
|
* Returns the vertical #GtkBox of the passed export dialog to be filled with
|
2009-07-15 18:12:13 +08:00
|
|
|
* export options.
|
|
|
|
*
|
2020-05-08 21:23:56 +08:00
|
|
|
* Returns: (transfer none): The #GtkBox to fill with export options.
|
2009-07-15 18:12:13 +08:00
|
|
|
*
|
2015-06-01 03:18:09 +08:00
|
|
|
* Since: 2.8
|
2009-07-15 18:12:13 +08:00
|
|
|
**/
|
|
|
|
GtkWidget *
|
|
|
|
gimp_export_dialog_get_content_area (GtkWidget *dialog)
|
|
|
|
{
|
|
|
|
return gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
}
|