mirror of https://github.com/GNOME/gimp.git
1858 lines
59 KiB
C
1858 lines
59 KiB
C
/* gap_range_ops.c
|
|
* 1997.11.06 hof (Wolfgang Hofer)
|
|
*
|
|
* GAP ... Gimp Animation Plugins
|
|
*
|
|
* This Module contains implementation of range based frame operations.
|
|
* - gap_range_to_multilayer
|
|
* - gap_range_flatten
|
|
* - gap_range_layer_del
|
|
* - gap_range_conv
|
|
* - gap_anim_scale
|
|
* - gap_anim_resize
|
|
* - gap_anim_crop
|
|
*
|
|
*/
|
|
/* The GIMP -- an image manipulation program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
/* revision history
|
|
* 1.1.28a; 2000/11/05 hof: check for GIMP_PDB_SUCCESS (not for FALSE)
|
|
* 1.1.24a 2000/07/01 hof: bugfix: flatten of singlelayer images has to remove alpha channel
|
|
* 1.1.17b 2000/02/26 hof: bugfixes
|
|
* 1.1.14a 2000/01/06 hof: gap_range_to_multilayer: use framerate (from video info file) in framenames
|
|
* bugfix: gap_range_to_multilayer: first save current frame
|
|
* 1.1.10a 1999/10/22 hof: bugfix: have to use the changed PDB-Interface
|
|
* for gimp_convert_indexed
|
|
* (with extended dither options and extra dialog window)
|
|
* 1.1.9a 1999/09/21 hof: bugfix GIMP_RUN_NONINTERACTIVE did not work in
|
|
* plug_in_gap_range_convert
|
|
* plug_in_gap_range_layer_del
|
|
* plug_in_gap_range_flatten
|
|
* 1.1.8 1999/08/31 hof: frames convert: save subsequent frames
|
|
* with rumode GIMP_RUN_WITH_LAST_VALS
|
|
* 0.97.00; 1998/10/19 hof: gap_range_to_multilayer: extended layer selection
|
|
* 0.96.03; 1998/08/31 hof: gap_range_to_multilayer: all params available
|
|
* in non-interactive runmode
|
|
* 0.96.02; 1998/08/05 hof: - p_frames_to_multilayer added framerate support
|
|
* 0.96.00; 1998/07/01 hof: - added scale, resize and crop
|
|
* (affects full range == all anim frames)
|
|
* - now using gap_arr_dialog.h
|
|
* 0.94.01; 1998/04/28 hof: added flatten_mode to plugin: gap_range_to_multilayer
|
|
* 0.92.00 1998.01.10 hof: bugfix in p_frames_to_multilayer
|
|
* layers need alpha (to be raise/lower able)
|
|
* 0.90.00 first development release
|
|
*/
|
|
#include "config.h"
|
|
|
|
/* SYTEM (UNIX) includes */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
/* GIMP includes */
|
|
#include "gtk/gtk.h"
|
|
#include "config.h"
|
|
#include "libgimp/stdplugins-intl.h"
|
|
#include "libgimp/gimp.h"
|
|
|
|
/* GAP includes */
|
|
#include "gap_layer_copy.h"
|
|
#include "gap_lib.h"
|
|
#include "gap_pdb_calls.h"
|
|
#include "gap_match.h"
|
|
#include "gap_arr_dialog.h"
|
|
#include "gap_resi_dialog.h"
|
|
#include "gap_mod_layer.h"
|
|
#include "gap_range_ops.h"
|
|
|
|
extern int gap_debug; /* ==0 ... dont print debug infos */
|
|
|
|
|
|
/* ============================================================================
|
|
* p_anim_sizechange_dialog
|
|
* dialog window with 2 (or 4) entry fields
|
|
* where the user can select the new Anim Frame (Image)-Size
|
|
* (if cnt == 4 additional Inputfields for offests are available)
|
|
* return -1 in case of cancel or any error
|
|
* (include check for change of current frame)
|
|
* return positve (0 or layerstack position) if everythig OK
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_anim_sizechange_dialog(t_anim_info *ainfo_ptr, t_gap_asiz asiz_mode,
|
|
long *size_x, long *size_y,
|
|
long *offs_x, long *offs_y)
|
|
{
|
|
static t_arr_arg argv[4];
|
|
gint cnt;
|
|
gchar *title;
|
|
gchar *hline;
|
|
gint l_width;
|
|
gint l_height;
|
|
gint l_rc;
|
|
|
|
/* get info about the image (size is common to all frames) */
|
|
l_width = gimp_image_width(ainfo_ptr->image_id);
|
|
l_height = gimp_image_height(ainfo_ptr->image_id);
|
|
|
|
p_init_arr_arg(&argv[0], WGT_INT_PAIR);
|
|
argv[0].label_txt = _("New Width:");
|
|
argv[0].constraint = FALSE;
|
|
argv[0].int_min = 1;
|
|
argv[0].int_max = 1024;
|
|
argv[0].umin = 1;
|
|
argv[0].umax = 10000;
|
|
argv[0].int_ret = l_width;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_INT_PAIR);
|
|
argv[1].label_txt = _("New Height:");
|
|
argv[1].constraint = FALSE;
|
|
argv[1].int_min = 1;
|
|
argv[1].int_max = 1024;
|
|
argv[1].umin = 1;
|
|
argv[1].umax = 10000;
|
|
argv[1].int_ret = l_height;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_INT_PAIR);
|
|
argv[2].label_txt = _("Offset X:");
|
|
argv[2].constraint = FALSE;
|
|
argv[2].int_min = 0;
|
|
argv[2].int_max = l_width;
|
|
argv[2].umin = 0;
|
|
argv[2].umax = 10000;
|
|
argv[2].int_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[3], WGT_INT_PAIR);
|
|
argv[3].label_txt = _("Offset Y:");
|
|
argv[3].constraint = FALSE;
|
|
argv[3].int_min = 0;
|
|
argv[3].int_max = l_height;
|
|
argv[3].umin = 0;
|
|
argv[3].umax = 10000;
|
|
argv[3].int_ret = 0;
|
|
|
|
switch(asiz_mode)
|
|
{
|
|
case ASIZ_CROP:
|
|
title = _("Crop AnimFrames (all)");
|
|
hline = g_strdup_printf (_("Crop (original %dx%d)"), l_width, l_height);
|
|
argv[0].int_max = l_width;
|
|
argv[0].constraint = TRUE;
|
|
argv[1].int_max = l_height;
|
|
argv[1].constraint = TRUE;
|
|
argv[2].constraint = TRUE;
|
|
argv[3].constraint = TRUE;
|
|
cnt = 4;
|
|
break;
|
|
case ASIZ_RESIZE:
|
|
title = _("Resize AnimFrames (all)");
|
|
hline = g_strdup_printf (_("Resize (original %dx%d)"), l_width, l_height);
|
|
argv[2].int_min = -l_width;
|
|
argv[3].int_min = -l_height;
|
|
cnt = 4;
|
|
break;
|
|
default:
|
|
title = _("Scale AnimFrames (all)");
|
|
hline = g_strdup_printf (_("Scale (original %dx%d)"), l_width, l_height);
|
|
cnt = 2;
|
|
break;
|
|
}
|
|
|
|
if(0 != p_chk_framerange(ainfo_ptr)) return -1;
|
|
|
|
/* array dialog can handle all asiz_mode type (and is already prepared for)
|
|
* BUT: RESIZE and SCALE should use the same dialogs as used in gimp
|
|
* on single Images.
|
|
* Therfore I made a procedure p_resi_dialog
|
|
*/
|
|
if(asiz_mode == ASIZ_CROP)
|
|
{
|
|
l_rc = p_array_dialog(title, hline, cnt, argv);
|
|
g_free (hline);
|
|
|
|
*size_x = (long)(argv[0].int_ret);
|
|
*size_y = (long)(argv[1].int_ret);
|
|
*offs_x = (long)(argv[2].int_ret);
|
|
*offs_y = (long)(argv[3].int_ret);
|
|
|
|
/* Clip size down to image borders */
|
|
if((*size_x + *offs_x) > l_width)
|
|
{
|
|
*size_x = l_width - *offs_x;
|
|
}
|
|
if((*size_y + *offs_y) > l_height)
|
|
{
|
|
*size_y = l_height - *offs_y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l_rc = p_resi_dialog(ainfo_ptr->image_id, asiz_mode, title,
|
|
size_x, size_y, offs_x, offs_y);
|
|
}
|
|
|
|
|
|
if(l_rc == TRUE)
|
|
{
|
|
if(0 != p_chk_framechange(ainfo_ptr))
|
|
{
|
|
return -1;
|
|
}
|
|
return 0; /* OK */
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
} /* end p_anim_sizechange_dialog */
|
|
|
|
|
|
/* ============================================================================
|
|
* p_range_dialog
|
|
* dialog window with 2 (or 3) entry fields
|
|
* where the user can select a frame range (FROM TO)
|
|
* (if cnt == 3 additional Layerstackposition)
|
|
* return -1 in case of cancel or any error
|
|
* (include check for change of current frame)
|
|
* return positve (0 or layerstack position) if everythig OK
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_range_dialog(t_anim_info *ainfo_ptr,
|
|
long *range_from, long *range_to,
|
|
char *title, char *hline, gint cnt)
|
|
{
|
|
static t_arr_arg argv[3];
|
|
|
|
|
|
if(cnt != 3) cnt = 2;
|
|
|
|
p_init_arr_arg(&argv[0], WGT_INT_PAIR);
|
|
argv[0].label_txt = _("From:");
|
|
argv[0].constraint = TRUE;
|
|
argv[0].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[0].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[0].int_ret = (gint)ainfo_ptr->curr_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_INT_PAIR);
|
|
argv[1].label_txt = _("To:");
|
|
argv[1].constraint = TRUE;
|
|
argv[1].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[1].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[1].int_ret = (gint)ainfo_ptr->last_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_INT_PAIR);
|
|
argv[2].label_txt = _("Layerstack:");
|
|
argv[2].constraint = FALSE;
|
|
argv[2].int_min = 0;
|
|
argv[2].int_max = 99;
|
|
argv[2].umin = 0;
|
|
argv[2].umax = 999999;
|
|
argv[2].int_ret = 0;
|
|
|
|
if(0 != p_chk_framerange(ainfo_ptr)) return -1;
|
|
if(TRUE == p_array_dialog(title, hline, cnt, argv))
|
|
{ *range_from = (long)(argv[0].int_ret);
|
|
*range_to = (long)(argv[1].int_ret);
|
|
|
|
if(0 != p_chk_framechange(ainfo_ptr))
|
|
{
|
|
return -1;
|
|
}
|
|
return (int)(argv[2].int_ret);
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
} /* end p_range_dialog */
|
|
|
|
/* ============================================================================
|
|
* p_convert_indexed_dialog
|
|
*
|
|
* extra dialog with dither options (when converting to indexed image type)
|
|
* return 0 .. OK
|
|
* -1 .. in case of Error or cancel
|
|
* ============================================================================
|
|
*/
|
|
static long
|
|
p_convert_indexed_dialog(gint32 *dest_colors, gint32 *dest_dither,
|
|
gint32 *palette_type, gint32 *alpha_dither, gint32 *remove_unused,
|
|
char *palette, gint len_palette)
|
|
{
|
|
#define ARGC_INDEXED 6
|
|
static t_arr_arg argv[ARGC_INDEXED];
|
|
static char *radio_paltype[4] = { N_("Generate Optimal Palette")
|
|
, N_("WEB Palette")
|
|
, N_("Use Custom Palette")
|
|
, N_("Use Black/White (1-Bit) Palette")
|
|
};
|
|
static char *radio_dither[4] = { N_("Floyd-Steinberg Color Dithering (Normal)")
|
|
, N_("Floyd-Steinberg Color Dithering (Reduced Color Bleeding)")
|
|
, N_("Positioned Color Dithering")
|
|
, N_("No Color Dithering")
|
|
};
|
|
static int gettextize_loop = 0;
|
|
|
|
for (;gettextize_loop < 4; gettextize_loop++)
|
|
radio_paltype[gettextize_loop] = gettext(radio_paltype[gettextize_loop]);
|
|
|
|
for (;gettextize_loop < 4; gettextize_loop++)
|
|
radio_dither[gettextize_loop] = gettext(radio_dither[gettextize_loop]);
|
|
|
|
p_init_arr_arg(&argv[0], WGT_RADIO);
|
|
argv[0].label_txt = _("Palette Type");
|
|
argv[0].help_txt = NULL;
|
|
argv[0].radio_argc = 4;
|
|
argv[0].radio_argv = radio_paltype;
|
|
argv[0].radio_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_TEXT);
|
|
argv[1].label_txt = _("Custom Palette");
|
|
argv[1].help_txt = _("Name of a cutom palette\n(is ignored if Palette Type is not custom)");
|
|
argv[1].text_buf_len = len_palette;
|
|
argv[1].text_buf_ret = palette;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_TOGGLE);
|
|
argv[2].label_txt = _("Remove Unused");
|
|
argv[2].help_txt = _("Remove unused or double colors\n(is ignored if Palette Type is not custom)");
|
|
argv[2].int_ret = 1;
|
|
|
|
p_init_arr_arg(&argv[3], WGT_INT_PAIR);
|
|
argv[3].constraint = TRUE;
|
|
argv[3].label_txt = _("Number of Colors");
|
|
argv[3].help_txt = _("Number of resulting Colors \n(ignored if Palette Type is not Generate optimal palette)");
|
|
argv[3].int_min = 2;
|
|
argv[3].int_max = 256;
|
|
argv[3].int_ret = 255;
|
|
|
|
|
|
p_init_arr_arg(&argv[4], WGT_RADIO);
|
|
argv[4].label_txt = _("Dither Options");
|
|
argv[4].help_txt = NULL;
|
|
argv[4].radio_argc = 4;
|
|
argv[4].radio_argv = radio_dither;
|
|
argv[4].radio_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[5], WGT_TOGGLE);
|
|
argv[5].label_txt = _("Enable transparency");
|
|
argv[5].help_txt = _("Enable dithering of transparency");
|
|
argv[5].int_ret = 0;
|
|
|
|
if(TRUE == p_array_dialog( _("Convert Frames to Indexed"),
|
|
_("Palette and Dither Settings"),
|
|
ARGC_INDEXED, argv))
|
|
{
|
|
switch(argv[0].radio_ret)
|
|
{
|
|
case 3:
|
|
*palette_type = GIMP_MONO_PALETTE;
|
|
break;
|
|
case 2:
|
|
*palette_type = GIMP_CUSTOM_PALETTE;
|
|
break;
|
|
case 1:
|
|
*palette_type = GIMP_WEB_PALETTE;
|
|
break;
|
|
default:
|
|
*palette_type = GIMP_MAKE_PALETTE;
|
|
break;
|
|
}
|
|
*remove_unused = (gint32)(argv[2].int_ret);;
|
|
*dest_colors = (gint32)(argv[3].int_ret);
|
|
switch(argv[4].radio_ret)
|
|
{
|
|
case 3:
|
|
*dest_dither = GIMP_NO_DITHER;
|
|
break;
|
|
case 2:
|
|
*dest_dither = GIMP_FIXED_DITHER;
|
|
break;
|
|
case 1:
|
|
*dest_dither = GIMP_FSLOWBLEED_DITHER;
|
|
break;
|
|
default:
|
|
*dest_dither = GIMP_FS_DITHER;
|
|
break;
|
|
}
|
|
|
|
*alpha_dither = (gint32)(argv[5].int_ret);
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
/* ============================================================================
|
|
* p_convert_dialog
|
|
*
|
|
* return 0 .. OK
|
|
* -1 .. in case of Error or cancel
|
|
* ============================================================================
|
|
*/
|
|
static long
|
|
p_convert_dialog(t_anim_info *ainfo_ptr,
|
|
long *range_from, long *range_to, long *flatten,
|
|
GimpImageBaseType *dest_type, gint32 *dest_colors, gint32 *dest_dither,
|
|
char *basename, gint len_base,
|
|
char *extension, gint len_ext,
|
|
gint32 *palette_type, gint32 *alpha_dither, gint32 *remove_unused,
|
|
char *palette, gint len_palette)
|
|
{
|
|
static t_arr_arg argv[7];
|
|
static char *radio_args[4] = {
|
|
N_("Keep Type"),
|
|
N_("Convert to RGB"),
|
|
N_("Convert to Gray"),
|
|
N_("Convert to Indexed")
|
|
};
|
|
static int gettextize_loop = 0;
|
|
|
|
for (;gettextize_loop < 4; gettextize_loop++)
|
|
radio_args[gettextize_loop] = gettext(radio_args[gettextize_loop]);
|
|
|
|
p_init_arr_arg(&argv[0], WGT_INT_PAIR);
|
|
argv[0].constraint = TRUE;
|
|
argv[0].label_txt = _("From Frame:");
|
|
argv[0].help_txt = _("first handled frame");
|
|
argv[0].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[0].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[0].int_ret = (gint)ainfo_ptr->curr_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_INT_PAIR);
|
|
argv[1].constraint = TRUE;
|
|
argv[1].label_txt = _("To Frame:");
|
|
argv[1].help_txt = _("last handled frame");
|
|
argv[1].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[1].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[1].int_ret = (gint)ainfo_ptr->last_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_LABEL);
|
|
argv[2].label_txt = _("\nSelect destination fileformat by extension\noptionally convert imagetype\n");
|
|
|
|
p_init_arr_arg(&argv[3], WGT_FILESEL);
|
|
argv[3].label_txt = _("Basename:");
|
|
argv[3].help_txt = _("basename of the resulting frames \n(0001.ext is added)");
|
|
argv[3].text_buf_len = len_base;
|
|
argv[3].text_buf_ret = basename;
|
|
|
|
p_init_arr_arg(&argv[4], WGT_TEXT);
|
|
argv[4].label_txt = _("Extension:");
|
|
argv[4].help_txt = _("extension of resulting frames \n(is also used to define Fileformat)");
|
|
argv[4].text_buf_len = len_ext;
|
|
argv[4].text_buf_ret = extension;
|
|
|
|
|
|
p_init_arr_arg(&argv[5], WGT_OPTIONMENU);
|
|
argv[5].label_txt = _("Imagetype:");
|
|
argv[5].help_txt = _("Convert to, or keep imagetype \n(most fileformats can't handle all types)");
|
|
argv[5].radio_argc = 4;
|
|
argv[5].radio_argv = radio_args;
|
|
argv[5].radio_ret = 0;
|
|
|
|
p_init_arr_arg(&argv[6], WGT_TOGGLE);
|
|
argv[6].label_txt = _("Flatten:");
|
|
argv[6].help_txt = _("Flatten all resulting frames \n(most fileformats need flattened frames)");
|
|
argv[6].int_ret = 1;
|
|
|
|
if(0 != p_chk_framerange(ainfo_ptr)) return -1;
|
|
|
|
if(TRUE == p_array_dialog( _("Convert Frames to other Formats"),
|
|
_("Convert Settings"),
|
|
7, argv))
|
|
{
|
|
*range_from = (long)(argv[0].int_ret);
|
|
*range_to = (long)(argv[1].int_ret);
|
|
switch(argv[5].radio_ret)
|
|
{
|
|
case 1:
|
|
*dest_type = GIMP_RGB;
|
|
break;
|
|
case 2:
|
|
*dest_type = GIMP_GRAY;
|
|
break;
|
|
case 3:
|
|
*dest_type = GIMP_INDEXED;
|
|
break;
|
|
default:
|
|
*dest_type = 9444; /* huh ?? */
|
|
break;
|
|
}
|
|
*flatten = (long)(argv[6].int_ret);
|
|
|
|
*dest_colors = 255;
|
|
*dest_dither = 0;
|
|
*palette_type = 2; /* WEB palette */
|
|
*alpha_dither = 0;
|
|
*remove_unused = 0;
|
|
|
|
if(*dest_type == GIMP_INDEXED)
|
|
{
|
|
/* Open a 2.nd dialog for the Dither Options */
|
|
if(0 != p_convert_indexed_dialog(dest_colors,
|
|
dest_dither,
|
|
palette_type,
|
|
alpha_dither,
|
|
remove_unused,
|
|
palette,
|
|
len_palette
|
|
))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if(0 != p_chk_framechange(ainfo_ptr))
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
} /* end p_convert_dialog */
|
|
|
|
/* ============================================================================
|
|
* p_range_to_multilayer_dialog
|
|
* dialog window with 4 entry fields
|
|
* where the user can select a frame range (FROM TO)
|
|
* return -1 in case of cancel or any error
|
|
* (include check for change of current frame)
|
|
* return positve (0 or layerstack position) if everythig OK
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_range_to_multilayer_dialog(t_anim_info *ainfo_ptr,
|
|
long *range_from, long *range_to,
|
|
long *flatten_mode, long *bg_visible,
|
|
long *framrate, char *frame_basename, gint len_frame_basename,
|
|
char *title, char *hline,
|
|
|
|
gint32 *sel_mode, gint32 *sel_case,
|
|
gint32 *sel_invert, char *sel_pattern)
|
|
{
|
|
static t_arr_arg argv[10];
|
|
|
|
static char *radio_args[4] = { N_("Expand as necessary"),
|
|
N_("Clipped to image"),
|
|
N_("Clipped to bottom layer"),
|
|
N_("Flattened image") };
|
|
static char *radio_help[4] = { N_("Resulting Layer Size is made of the outline-rectangle \nof all visible layers (may differ from frame to frame)"),
|
|
N_("Resulting Layer Size is the frame size"),
|
|
N_("Resulting Layer Size is the size of the bottom layer\n(may differ from frame to frame)"),
|
|
N_("Resulting Layer Size is the frame size \ntransparent parts are filled with BG color") };
|
|
/* Layer select modes */
|
|
static char *sel_args[7] = { N_("Pattern is equal to LayerName"),
|
|
N_("Pattern is Start of LayerName"),
|
|
N_("Pattern is End of Layername"),
|
|
N_("Pattern is a Part of LayerName"),
|
|
N_("Pattern is LayerstackNumber List"),
|
|
N_("Pattern is REVERSE-stack List"),
|
|
N_("All Visible (ignore Pattern)")
|
|
};
|
|
static char *sel_help[7] = { N_("select all Layers where Layername is equal to Pattern"),
|
|
N_("select all Layers where Layername starts with Pattern"),
|
|
N_("select all Layers where Layername ends up with Pattern"),
|
|
N_("select all Layers where Layername contains Pattern"),
|
|
N_("select Layerstack positions.\n0, 4-5, 8\nwhere 0 == Top-layer"),
|
|
N_("select Layerstack positions.\n0, 4-5, 8\nwhere 0 == BG-layer"),
|
|
N_("select all visible Layers")
|
|
};
|
|
static int gettextize_radio = 0, gettextize_sel = 0;
|
|
for (;gettextize_radio < 4; gettextize_radio++) {
|
|
radio_args[gettextize_radio] = gettext(radio_args[gettextize_radio]);
|
|
radio_help[gettextize_radio] = gettext(radio_help[gettextize_radio]);
|
|
}
|
|
for (;gettextize_sel < 4; gettextize_sel++) {
|
|
sel_args[gettextize_sel] = gettext(sel_args[gettextize_sel]);
|
|
sel_help[gettextize_sel] = gettext(sel_help[gettextize_sel]);
|
|
}
|
|
|
|
p_init_arr_arg(&argv[0], WGT_INT_PAIR);
|
|
argv[0].constraint = TRUE;
|
|
argv[0].label_txt = _("From:");
|
|
argv[0].help_txt = _("first handled frame");
|
|
argv[0].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[0].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[0].int_ret = (gint)ainfo_ptr->curr_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[1], WGT_INT_PAIR);
|
|
argv[1].constraint = TRUE;
|
|
argv[1].label_txt = _("To:");
|
|
argv[1].help_txt = _("last handled frame");
|
|
argv[1].int_min = (gint)ainfo_ptr->first_frame_nr;
|
|
argv[1].int_max = (gint)ainfo_ptr->last_frame_nr;
|
|
argv[1].int_ret = (gint)ainfo_ptr->last_frame_nr;
|
|
|
|
p_init_arr_arg(&argv[2], WGT_TEXT);
|
|
argv[2].label_txt = _("Layer Basename:");
|
|
argv[2].help_txt = _("Basename for all Layers \n[####] is replaced by frame number");
|
|
argv[2].text_buf_len = len_frame_basename;
|
|
argv[2].text_buf_ret = frame_basename;
|
|
|
|
/* Framerate is not used any longer */
|
|
/*
|
|
p_init_arr_arg(&argv[3], WGT_INT_PAIR);
|
|
argv[3].constraint = FALSE;
|
|
argv[3].label_txt = "Framerate :";
|
|
argv[3].help_txt = "Framedelay in ms";
|
|
argv[3].int_min = (gint)0;
|
|
argv[3].int_max = (gint)300;
|
|
argv[3].int_ret = (gint)50;
|
|
*/
|
|
p_init_arr_arg(&argv[3], WGT_LABEL);
|
|
argv[3].label_txt = " ";
|
|
|
|
p_init_arr_arg(&argv[4], WGT_RADIO);
|
|
argv[4].label_txt = _("Layer Mergemode:");
|
|
argv[4].radio_argc = 4;
|
|
argv[4].radio_argv = radio_args;
|
|
argv[4].radio_help_argv = radio_help;
|
|
argv[4].radio_ret = 1;
|
|
|
|
p_init_arr_arg(&argv[5], WGT_TOGGLE);
|
|
argv[5].label_txt = _("Exclude BG-Layer");
|
|
argv[5].help_txt = _("Exclude the BG-Layers \nin all handled frames\nregardless to selection");
|
|
argv[5].int_ret = 0; /* 1: exclude BG Layer from all selections */
|
|
|
|
|
|
/* Layer select mode RADIO buttons */
|
|
p_init_arr_arg(&argv[6], WGT_RADIO);
|
|
argv[6].label_txt = _("Select Layer(s):");
|
|
argv[6].radio_argc = 7;
|
|
argv[6].radio_argv = sel_args;
|
|
argv[6].radio_help_argv = sel_help;
|
|
argv[6].radio_ret = 6;
|
|
|
|
/* Layer select pattern string */
|
|
g_snprintf (sel_pattern, 2, "0");
|
|
p_init_arr_arg(&argv[7], WGT_TEXT);
|
|
argv[7].label_txt = _("Select Pattern:");
|
|
argv[7].entry_width = 140; /* pixel */
|
|
argv[7].help_txt = _("String to identify layer names \nor layerstack position numbers\n0,3-5");
|
|
argv[7].text_buf_len = MAX_LAYERNAME;
|
|
argv[7].text_buf_ret = sel_pattern;
|
|
|
|
/* case sensitive checkbutton */
|
|
p_init_arr_arg(&argv[8], WGT_TOGGLE);
|
|
argv[8].label_txt = _("Case sensitive");
|
|
argv[8].help_txt = _("Lowercase and UPPERCASE letters are considered as different");
|
|
argv[8].int_ret = 1;
|
|
|
|
/* invert selection checkbutton */
|
|
p_init_arr_arg(&argv[9], WGT_TOGGLE);
|
|
argv[9].label_txt = _("Invert Selection");
|
|
argv[9].help_txt = _("Use all unselected Layers");
|
|
argv[9].int_ret = 0;
|
|
|
|
|
|
|
|
|
|
if(0 != p_chk_framerange(ainfo_ptr)) return -1;
|
|
|
|
if(TRUE == p_array_dialog(title, hline, 10, argv))
|
|
{ *range_from = (long)(argv[0].int_ret);
|
|
*range_to = (long)(argv[1].int_ret);
|
|
*framrate = (long)(argv[3].int_ret);
|
|
*flatten_mode = (long)(argv[4].int_ret);
|
|
if (argv[5].int_ret == 0) *bg_visible = 1; /* 1: use BG like any Layer */
|
|
else *bg_visible = 0; /* 0: exclude (ignore) BG Layer */
|
|
|
|
*sel_mode = argv[6].int_ret;
|
|
/* [7] sel_pattern */
|
|
*sel_case = argv[8].int_ret;
|
|
*sel_invert = argv[9].int_ret;
|
|
|
|
if(0 != p_chk_framechange(ainfo_ptr))
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
} /* end p_range_to_multilayer_dialog */
|
|
|
|
|
|
/* ============================================================================
|
|
* p_frames_to_multilayer
|
|
* returns image_id of the new created multilayer image
|
|
* (or -1 on error)
|
|
* ============================================================================
|
|
*/
|
|
static gint32
|
|
p_frames_to_multilayer(t_anim_info *ainfo_ptr,
|
|
long range_from, long range_to,
|
|
long flatten_mode, long bg_visible,
|
|
long framerate, char *frame_basename,
|
|
gint32 sel_mode, gint32 sel_case,
|
|
gint32 sel_invert, char *sel_pattern)
|
|
{
|
|
GimpImageBaseType l_type;
|
|
guint l_width, l_height;
|
|
long l_cur_frame_nr;
|
|
long l_step, l_begin, l_end;
|
|
long l_vidx;
|
|
gint32 l_tmp_image_id;
|
|
gint32 l_new_image_id;
|
|
gint32 l_cp_layer_id;
|
|
gint32 l_tmp_layer_id;
|
|
gint l_src_offset_x, l_src_offset_y; /* layeroffsets as they were in src_image */
|
|
gint l_nlayers;
|
|
gint32 *l_layers_list;
|
|
gint l_visible;
|
|
gint l_nvisible;
|
|
gint l_nlayers_result;
|
|
gdouble l_percentage, l_percentage_step;
|
|
static char l_layername[256];
|
|
t_LayliElem *l_layli_ptr;
|
|
gint32 l_sel_cnt;
|
|
|
|
|
|
l_percentage = 0.0;
|
|
l_nlayers_result = 0;
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
gimp_progress_init( _("Creating Layer-Animated Image..."));
|
|
}
|
|
|
|
l_tmp_layer_id = -1;
|
|
|
|
/* get info about the image (size and type is common to all frames) */
|
|
l_width = gimp_image_width(ainfo_ptr->image_id);
|
|
l_height = gimp_image_height(ainfo_ptr->image_id);
|
|
l_type = gimp_image_base_type(ainfo_ptr->image_id);
|
|
|
|
l_new_image_id = gimp_image_new(l_width, l_height,l_type);
|
|
l_visible = TRUE; /* only the 1.st layer should be visible */
|
|
|
|
l_begin = range_from;
|
|
l_end = range_to;
|
|
|
|
if(range_from > range_to)
|
|
{
|
|
l_step = -1; /* operate in descending (reverse) order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_from) - range_to);
|
|
|
|
if(range_to < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_from > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l_step = 1; /* operate in ascending order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_to) - range_from);
|
|
|
|
if(range_from < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_to > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
|
|
|
|
l_cur_frame_nr = l_begin;
|
|
while(1)
|
|
{
|
|
/* build the frame name */
|
|
if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
|
|
ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename,
|
|
l_cur_frame_nr,
|
|
ainfo_ptr->extension);
|
|
if(ainfo_ptr->new_filename == NULL)
|
|
goto error;
|
|
|
|
/* load current frame into temporary image */
|
|
l_tmp_image_id = p_load_image(ainfo_ptr->new_filename);
|
|
if(l_tmp_image_id < 0)
|
|
goto error;
|
|
|
|
/* get informations (id, visible, selected) about all layers */
|
|
l_layli_ptr = p_alloc_layli(l_tmp_image_id, &l_sel_cnt, &l_nlayers,
|
|
sel_mode, sel_case, sel_invert, sel_pattern);
|
|
if(l_layli_ptr == NULL)
|
|
goto error;
|
|
|
|
l_nvisible = l_sel_cnt; /* count visible Layers == all selected layers */
|
|
for(l_vidx=0; l_vidx < l_nlayers; l_vidx++)
|
|
{
|
|
/* set all selected layers visible, all others invisible */
|
|
l_tmp_layer_id = l_layli_ptr[l_vidx].layer_id;
|
|
gimp_layer_set_visible(l_tmp_layer_id,
|
|
l_layli_ptr[l_vidx].selected);
|
|
|
|
if((bg_visible == 0) && (l_vidx == (l_nlayers -1)))
|
|
{
|
|
/* set BG_Layer invisible */
|
|
gimp_layer_set_visible(l_tmp_layer_id, FALSE);
|
|
if(l_layli_ptr[l_vidx].selected)
|
|
{
|
|
l_nvisible--;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_free(l_layli_ptr);
|
|
|
|
if((flatten_mode >= FLAM_MERG_EXPAND) && (flatten_mode <= FLAM_MERG_CLIP_BG))
|
|
{
|
|
if(gap_debug) fprintf(stderr, "p_frames_to_multilayer: %d MERGE visible layers=%d\n", (int)flatten_mode, (int)l_nvisible);
|
|
|
|
/* merge all visible Layers */
|
|
if(l_nvisible > 1) gimp_image_merge_visible_layers (l_tmp_image_id, flatten_mode);
|
|
}
|
|
else
|
|
{
|
|
if(gap_debug) fprintf(stderr, "p_frames_to_multilayer: %d FLATTEN\n", (int)flatten_mode);
|
|
/* flatten temporary image (reduce to single layer) */
|
|
gimp_image_flatten (l_tmp_image_id);
|
|
}
|
|
|
|
/* copy (the only visible) layer from temporary image */
|
|
l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
|
|
if(l_layers_list != NULL)
|
|
{
|
|
for(l_vidx=0; l_vidx < l_nlayers; l_vidx++)
|
|
{
|
|
l_tmp_layer_id = l_layers_list[l_vidx];
|
|
|
|
/* stop at 1.st visible layer (this should be the only visible layer) */
|
|
if(gimp_layer_get_visible(l_tmp_layer_id)) break;
|
|
|
|
/* stop at 1.st layer if image was flattened */
|
|
if((flatten_mode < FLAM_MERG_EXPAND) || (flatten_mode > FLAM_MERG_CLIP_BG)) break;
|
|
}
|
|
g_free (l_layers_list);
|
|
|
|
if(l_vidx < l_nlayers)
|
|
{
|
|
l_cp_layer_id = p_my_layer_copy(l_new_image_id,
|
|
l_tmp_layer_id,
|
|
100.0, /* Opacity */
|
|
0, /* NORMAL */
|
|
&l_src_offset_x,
|
|
&l_src_offset_y);
|
|
|
|
/* add the copied layer to current destination image */
|
|
gimp_image_add_layer(l_new_image_id, l_cp_layer_id, 0);
|
|
gimp_layer_set_offsets(l_cp_layer_id, l_src_offset_x, l_src_offset_y);
|
|
|
|
l_nlayers_result++;
|
|
|
|
|
|
/* add aplha channel to all layers
|
|
* (without alpha raise and lower would not work on that layers)
|
|
*/
|
|
gimp_layer_add_alpha(l_cp_layer_id);
|
|
|
|
/* set name and visibility */
|
|
if (frame_basename == NULL) frame_basename = "frame_[####]";
|
|
if (*frame_basename == '\0') frame_basename = "frame_[####]";
|
|
|
|
p_substitute_framenr(&l_layername[0], sizeof(l_layername),
|
|
frame_basename, (long)l_cur_frame_nr);
|
|
gimp_layer_set_name(l_cp_layer_id, &l_layername[0]);
|
|
|
|
gimp_layer_set_visible(l_cp_layer_id, l_visible);
|
|
l_visible = FALSE; /* all further layers are set invisible */
|
|
}
|
|
/* else: tmp image has no visible layers, ignore that frame */
|
|
}
|
|
|
|
/* destroy the tmp image */
|
|
gimp_image_delete(l_tmp_image_id);
|
|
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_percentage += l_percentage_step;
|
|
gimp_progress_update (l_percentage);
|
|
}
|
|
|
|
/* advance to next frame */
|
|
if(l_cur_frame_nr == l_end)
|
|
break;
|
|
l_cur_frame_nr += l_step;
|
|
|
|
}
|
|
|
|
p_prevent_empty_image(l_new_image_id);
|
|
|
|
return l_new_image_id;
|
|
|
|
error:
|
|
gimp_image_delete(l_new_image_id);
|
|
return -1;
|
|
|
|
} /* end p_frames_to_multilayer */
|
|
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_range_to_multilayer
|
|
* ============================================================================
|
|
*/
|
|
gint32 gap_range_to_multilayer(GimpRunMode run_mode, gint32 image_id,
|
|
long range_from, long range_to,
|
|
long flatten_mode, long bg_visible,
|
|
long framerate,
|
|
char *frame_basename, int frame_basename_len,
|
|
gint32 sel_mode, gint32 sel_case,
|
|
gint32 sel_invert, char *sel_pattern)
|
|
{
|
|
gint32 new_image_id;
|
|
gint32 l_rc;
|
|
long l_from, l_to;
|
|
t_anim_info *ainfo_ptr;
|
|
t_video_info *vin_ptr;
|
|
gint32 l_sel_mode;
|
|
gint32 l_sel_case;
|
|
gint32 l_sel_invert;
|
|
gdouble l_framerate;
|
|
|
|
char l_sel_pattern[MAX_LAYERNAME];
|
|
|
|
l_rc = -1;
|
|
ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
|
|
if(ainfo_ptr != NULL)
|
|
{
|
|
if (0 == p_dir_ainfo(ainfo_ptr))
|
|
{
|
|
if(run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_framerate = 24.0;
|
|
vin_ptr = p_get_video_info(ainfo_ptr->basename);
|
|
if(vin_ptr)
|
|
{
|
|
if(vin_ptr->framerate > 0) l_framerate = vin_ptr->framerate;
|
|
g_free(vin_ptr);
|
|
}
|
|
g_snprintf(frame_basename, frame_basename_len, "frame_[####] (%dms)", (int)(1000/l_framerate));
|
|
framerate = 0;
|
|
l_rc = p_range_to_multilayer_dialog (ainfo_ptr, &l_from, &l_to,
|
|
&flatten_mode, &bg_visible,
|
|
&framerate, frame_basename, frame_basename_len,
|
|
_("Frames to Image"),
|
|
_("Create Multilayer-Image from Frames"),
|
|
&l_sel_mode, &sel_case,
|
|
&sel_invert, &l_sel_pattern[0]
|
|
);
|
|
}
|
|
else
|
|
{
|
|
l_from = range_from;
|
|
l_to = range_to;
|
|
l_rc = 0;
|
|
l_sel_mode = sel_mode;
|
|
l_sel_case = sel_case;
|
|
l_sel_invert = sel_invert;
|
|
|
|
strncpy(&l_sel_pattern[0], sel_pattern, sizeof(l_sel_pattern) -1);
|
|
l_sel_pattern[sizeof(l_sel_pattern) -1] = '\0';
|
|
}
|
|
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
if(l_rc >= 0)
|
|
{
|
|
new_image_id = p_frames_to_multilayer(ainfo_ptr, l_from, l_to,
|
|
flatten_mode, bg_visible,
|
|
framerate, frame_basename,
|
|
l_sel_mode, sel_case,
|
|
sel_invert, &l_sel_pattern[0]);
|
|
gimp_display_new(new_image_id);
|
|
l_rc = new_image_id;
|
|
}
|
|
}
|
|
}
|
|
p_free_ainfo(&ainfo_ptr);
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_range_to_multilayer */
|
|
|
|
/* ============================================================================
|
|
* p_type_convert
|
|
* convert image to desired type (reduce to dest_colors for INDEXED type)
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_type_convert(gint32 image_id, GimpImageBaseType dest_type, gint32 dest_colors, gint32 dest_dither,
|
|
gint32 palette_type, gint32 alpha_dither, gint32 remove_unused, char *palette)
|
|
{
|
|
GimpParam *l_params;
|
|
gint l_retvals;
|
|
int l_rc;
|
|
|
|
l_rc = 0;
|
|
l_params = NULL;
|
|
|
|
switch(dest_type)
|
|
{
|
|
case GIMP_INDEXED:
|
|
if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to INDEXED ncolors=%d, palette_type=%d palette_name=%s'\n",
|
|
(int)dest_colors, (int)palette_type, palette);
|
|
l_params = gimp_run_procedure ("gimp_convert_indexed",
|
|
&l_retvals,
|
|
GIMP_PDB_IMAGE, image_id,
|
|
GIMP_PDB_INT32, dest_dither, /* value 1== floyd-steinberg */
|
|
GIMP_PDB_INT32, palette_type, /* value 0: MAKE_PALETTE, 2: WEB_PALETTE 4:CUSTOM_PALETTE */
|
|
GIMP_PDB_INT32, dest_colors,
|
|
GIMP_PDB_INT32, alpha_dither,
|
|
GIMP_PDB_INT32, remove_unused,
|
|
GIMP_PDB_STRING, palette, /* name of custom palette */
|
|
GIMP_PDB_END);
|
|
break;
|
|
case GIMP_GRAY:
|
|
if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to GRAY'\n");
|
|
l_params = gimp_run_procedure ("gimp_convert_grayscale",
|
|
&l_retvals,
|
|
GIMP_PDB_IMAGE, image_id,
|
|
GIMP_PDB_END);
|
|
break;
|
|
case GIMP_RGB:
|
|
if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to RGB'\n");
|
|
l_params = gimp_run_procedure ("gimp_convert_rgb",
|
|
&l_retvals,
|
|
GIMP_PDB_IMAGE, image_id,
|
|
GIMP_PDB_END);
|
|
break;
|
|
default:
|
|
if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert AS_IT_IS (dont convert)'\n");
|
|
return 0;
|
|
break;
|
|
}
|
|
|
|
|
|
if (l_params[0].data.d_status != GIMP_PDB_SUCCESS)
|
|
{ l_rc = -1;
|
|
}
|
|
|
|
g_free(l_params);
|
|
return (l_rc);
|
|
} /* end p_type_convert */
|
|
|
|
/* ============================================================================
|
|
* p_frames_convert
|
|
* convert frames (multiple images) into desired fileformat and type
|
|
* (flatten the images if desired)
|
|
*
|
|
* if save_proc_name == NULL
|
|
* then use xcf save (and flatten image)
|
|
* and new_basename and new_extension
|
|
*
|
|
* returns value >= 0 if all is ok
|
|
* (or -1 on error)
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_frames_convert(t_anim_info *ainfo_ptr,
|
|
long range_from, long range_to,
|
|
char *save_proc_name, char *new_basename, char *new_extension,
|
|
int flatten,
|
|
GimpImageBaseType dest_type, gint32 dest_colors, gint32 dest_dither,
|
|
gint32 palette_type, gint32 alpha_dither, gint32 remove_unused, char *palette)
|
|
{
|
|
GimpRunMode l_run_mode;
|
|
gint32 l_tmp_image_id;
|
|
long l_cur_frame_nr;
|
|
long l_step, l_begin, l_end;
|
|
gint l_nlayers;
|
|
gint l_img_already_flat;
|
|
gint32 *l_layers_list;
|
|
gdouble l_percentage, l_percentage_step;
|
|
char *l_sav_name;
|
|
int l_rc;
|
|
gint l_overwrite_mode;
|
|
static t_but_arg l_argv[3];
|
|
|
|
|
|
l_rc = 0;
|
|
l_overwrite_mode = 0;
|
|
l_percentage = 0.0;
|
|
l_run_mode = ainfo_ptr->run_mode;
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
if(save_proc_name == NULL) gimp_progress_init( _("Flattening Frames..."));
|
|
else gimp_progress_init( _("Converting Frames..."));
|
|
}
|
|
|
|
|
|
l_begin = range_from;
|
|
l_end = range_to;
|
|
|
|
if(range_from > range_to)
|
|
{
|
|
l_step = -1; /* operate in descending (reverse) order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_from) - range_to);
|
|
|
|
if(range_to < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_from > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l_step = 1; /* operate in ascending order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_to) - range_from);
|
|
|
|
if(range_from < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_to > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
|
|
|
|
l_cur_frame_nr = l_begin;
|
|
while(l_rc >= 0)
|
|
{
|
|
/* build the frame name */
|
|
if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
|
|
ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename,
|
|
l_cur_frame_nr,
|
|
ainfo_ptr->extension);
|
|
if(ainfo_ptr->new_filename == NULL)
|
|
return -1;
|
|
|
|
/* load current frame */
|
|
l_tmp_image_id = p_load_image(ainfo_ptr->new_filename);
|
|
if(l_tmp_image_id < 0)
|
|
return -1;
|
|
|
|
l_img_already_flat = FALSE; /* an image without any layer is considered as not flattend */
|
|
l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
|
|
if(l_layers_list != NULL)
|
|
{
|
|
if( (l_nlayers == 1)
|
|
&& (! gimp_drawable_has_alpha(l_layers_list[0]))
|
|
&& (! gimp_layer_get_visible(l_layers_list[0])))
|
|
{
|
|
l_img_already_flat = TRUE;
|
|
}
|
|
g_free (l_layers_list);
|
|
}
|
|
|
|
if((l_img_already_flat == FALSE) && (flatten != 0))
|
|
{
|
|
gint32 l_dummy_layer_id;
|
|
if(gap_debug) fprintf(stderr, "DEBUG: p_frames_convert flatten tmp image'\n");
|
|
|
|
/* hof:
|
|
* we add dummy layers to make sure that flatten works on any kind of image.
|
|
* even if the image had no layer at all, or all its layers were invisible.
|
|
* (flatten need at least 2 layers and at least one visible layer to work.
|
|
* if just invisible layers are flattened
|
|
* we do not get a resulting layer (returned l_layer_id == -1)
|
|
*/
|
|
l_dummy_layer_id = gimp_layer_new(l_tmp_image_id, "dummy",
|
|
1,
|
|
1,
|
|
((gint)(gimp_image_base_type(l_tmp_image_id)) * 2),
|
|
100.0, /* Opacity full opaque */
|
|
0); /* NORMAL */
|
|
gimp_image_add_layer(l_tmp_image_id, l_dummy_layer_id, 0);
|
|
gimp_layer_set_offsets(l_dummy_layer_id, -1, -1);
|
|
|
|
if(l_nlayers == 0)
|
|
{
|
|
/* on empty images we need 2 dummies to make flatten happy */
|
|
l_dummy_layer_id = gimp_layer_new(l_tmp_image_id, "dummy2",
|
|
1,
|
|
1,
|
|
((gint)(gimp_image_base_type(l_tmp_image_id)) * 2),
|
|
100.0, /* Opacity full opaque */
|
|
0); /* NORMAL */
|
|
gimp_image_add_layer(l_tmp_image_id, l_dummy_layer_id, 0);
|
|
gimp_layer_set_offsets(l_dummy_layer_id, -1, -1);
|
|
}
|
|
|
|
|
|
/* flatten current frame image (reduce to single layer) */
|
|
gimp_image_flatten (l_tmp_image_id);
|
|
|
|
/* save back the current frame with same name */
|
|
if(save_proc_name == NULL)
|
|
{
|
|
l_rc = p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename);
|
|
}
|
|
}
|
|
|
|
if(save_proc_name != NULL)
|
|
{
|
|
if(dest_type != gimp_image_base_type(l_tmp_image_id))
|
|
{
|
|
/* have to convert to desired type (RGB, INDEXED, GRAYSCALE) */
|
|
p_type_convert(l_tmp_image_id, dest_type, dest_colors, dest_dither,
|
|
palette_type, alpha_dither, remove_unused, palette);
|
|
}
|
|
|
|
|
|
/* build the name for output image */
|
|
l_sav_name = p_alloc_fname(new_basename,
|
|
l_cur_frame_nr,
|
|
new_extension);
|
|
if(l_sav_name != NULL)
|
|
{
|
|
if(1 == p_file_exists(l_sav_name))
|
|
{
|
|
if (l_overwrite_mode < 1)
|
|
{
|
|
l_argv[0].but_txt = _("Overwrite Frame");
|
|
l_argv[0].but_val = 0;
|
|
l_argv[1].but_txt = _("Overwrite All");
|
|
l_argv[1].but_val = 1;
|
|
l_argv[2].but_txt = GTK_STOCK_CANCEL;
|
|
l_argv[2].but_val = -1;
|
|
|
|
l_overwrite_mode = p_buttons_dialog ( _("GAP Question"), l_sav_name, 3, l_argv, -1);
|
|
}
|
|
|
|
}
|
|
|
|
if(l_overwrite_mode < 0) { l_rc = -1; }
|
|
else
|
|
{
|
|
/* save with selected save procedure
|
|
* (regardless if image was flattened or not)
|
|
*/
|
|
l_rc = p_save_named_image(l_tmp_image_id, l_sav_name, l_run_mode);
|
|
if(l_rc < 0)
|
|
{
|
|
p_msg_win(ainfo_ptr->run_mode, _("Convert Frames: SAVE operation FAILED.\n"
|
|
"Desired save plugin can't handle type\n"
|
|
"or desired save plugin not available."));
|
|
}
|
|
}
|
|
if(l_run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_run_mode = GIMP_RUN_WITH_LAST_VALS; /* for all further calls */
|
|
}
|
|
g_free(l_sav_name);
|
|
}
|
|
}
|
|
|
|
/* destroy the tmp image */
|
|
gimp_image_delete(l_tmp_image_id);
|
|
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_percentage += l_percentage_step;
|
|
gimp_progress_update (l_percentage);
|
|
}
|
|
|
|
/* advance to next frame */
|
|
if((l_cur_frame_nr == l_end) || (l_rc < 0))
|
|
break;
|
|
l_cur_frame_nr += l_step;
|
|
|
|
}
|
|
|
|
|
|
return l_rc;
|
|
} /* end p_frames_convert */
|
|
|
|
|
|
|
|
/* ============================================================================
|
|
* p_image_sizechange
|
|
* scale, resize or crop one image
|
|
* ============================================================================
|
|
*/
|
|
static
|
|
int p_image_sizechange(gint32 image_id,
|
|
t_gap_asiz asiz_mode,
|
|
long size_x, long size_y,
|
|
long offs_x, long offs_y
|
|
)
|
|
{
|
|
GimpParam *l_params;
|
|
gint l_retvals;
|
|
|
|
switch(asiz_mode)
|
|
{
|
|
case ASIZ_CROP:
|
|
l_params = gimp_run_procedure ("gimp_crop",
|
|
&l_retvals,
|
|
GIMP_PDB_IMAGE, image_id,
|
|
GIMP_PDB_INT32, size_x,
|
|
GIMP_PDB_INT32, size_y,
|
|
GIMP_PDB_INT32, offs_x,
|
|
GIMP_PDB_INT32, offs_y,
|
|
GIMP_PDB_END);
|
|
break;
|
|
case ASIZ_RESIZE:
|
|
gimp_image_resize(image_id, (guint)size_x, (guint)size_y, (gint)offs_x, (gint)offs_y);
|
|
break;
|
|
default:
|
|
l_params = gimp_run_procedure ("gimp_image_scale",
|
|
&l_retvals,
|
|
GIMP_PDB_IMAGE, image_id,
|
|
GIMP_PDB_INT32, size_x,
|
|
GIMP_PDB_INT32, size_y,
|
|
GIMP_PDB_END);
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
} /* end p_image_sizechange */
|
|
|
|
/* ============================================================================
|
|
* p_anim_sizechange
|
|
* scale, resize or crop all frames in the animation
|
|
* ============================================================================
|
|
*/
|
|
static
|
|
gint32 p_anim_sizechange(t_anim_info *ainfo_ptr,
|
|
t_gap_asiz asiz_mode,
|
|
long size_x, long size_y,
|
|
long offs_x, long offs_y
|
|
)
|
|
{
|
|
guint l_width, l_height;
|
|
long l_cur_frame_nr;
|
|
long l_step, l_begin, l_end;
|
|
gint32 l_tmp_image_id;
|
|
gdouble l_percentage, l_percentage_step;
|
|
GimpParam *l_params;
|
|
int l_rc;
|
|
|
|
l_rc = 0;
|
|
l_params = NULL;
|
|
|
|
|
|
l_percentage = 0.0;
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
switch(asiz_mode)
|
|
{
|
|
case ASIZ_CROP:
|
|
gimp_progress_init( _("Cropping all Animation Frames..."));
|
|
break;
|
|
case ASIZ_RESIZE:
|
|
gimp_progress_init( _("Resizing all Animation Frames..."));
|
|
break;
|
|
default:
|
|
gimp_progress_init( _("Scaling all Animation Frames..."));
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/* get info about the image (size and type is common to all frames) */
|
|
l_width = gimp_image_width(ainfo_ptr->image_id);
|
|
l_height = gimp_image_height(ainfo_ptr->image_id);
|
|
|
|
|
|
l_begin = ainfo_ptr->first_frame_nr;
|
|
l_end = ainfo_ptr->last_frame_nr;
|
|
|
|
l_step = 1; /* operate in ascending order */
|
|
l_percentage_step = 1.0 / ((1.0 + l_end) - l_begin);
|
|
|
|
|
|
l_cur_frame_nr = l_begin;
|
|
|
|
while(1)
|
|
{
|
|
/* build the frame name */
|
|
if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
|
|
ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename,
|
|
l_cur_frame_nr,
|
|
ainfo_ptr->extension);
|
|
if(ainfo_ptr->new_filename == NULL)
|
|
return -1;
|
|
|
|
/* load current frame into temporary image */
|
|
l_tmp_image_id = p_load_image(ainfo_ptr->new_filename);
|
|
if(l_tmp_image_id < 0)
|
|
return -1;
|
|
|
|
l_rc = p_image_sizechange(l_tmp_image_id, asiz_mode,
|
|
size_x, size_y, offs_x, offs_y);
|
|
if(l_rc < 0) break;
|
|
|
|
/* save back the current frame with same name */
|
|
l_rc = p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename);
|
|
if(l_rc < 0) break;
|
|
|
|
/* destroy the tmp image */
|
|
gimp_image_delete(l_tmp_image_id);
|
|
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_percentage += l_percentage_step;
|
|
gimp_progress_update (l_percentage);
|
|
}
|
|
|
|
/* advance to next frame */
|
|
if(l_cur_frame_nr == l_end)
|
|
break;
|
|
l_cur_frame_nr += l_step;
|
|
|
|
} /* end while loop over all frames*/
|
|
|
|
return l_rc;
|
|
} /* end p_anim_sizechange */
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_range_flatten
|
|
* ============================================================================
|
|
*/
|
|
int gap_range_flatten(GimpRunMode run_mode, gint32 image_id,
|
|
long range_from, long range_to)
|
|
{
|
|
int l_rc;
|
|
long l_from, l_to;
|
|
t_anim_info *ainfo_ptr;
|
|
|
|
l_rc = -1;
|
|
ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
|
|
if(ainfo_ptr != NULL)
|
|
{
|
|
if (0 == p_dir_ainfo(ainfo_ptr))
|
|
{
|
|
if(run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_rc = p_range_dialog (ainfo_ptr, &l_from, &l_to,
|
|
_("Flatten Frames"),
|
|
_("Select Frame Range"), 2);
|
|
|
|
}
|
|
else
|
|
{
|
|
l_rc = 0;
|
|
l_from = range_from;
|
|
l_to = range_to;
|
|
}
|
|
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_frames_convert(ainfo_ptr, l_from, l_to, NULL, NULL, NULL, 1, 0,0,0, 0,0,0, "");
|
|
p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
}
|
|
}
|
|
}
|
|
p_free_ainfo(&ainfo_ptr);
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_range_flatten */
|
|
|
|
|
|
|
|
|
|
/* ============================================================================
|
|
* p_frames_layer_del
|
|
* returns image_id of the new created multilayer image
|
|
* (or -1 on error)
|
|
* ============================================================================
|
|
*/
|
|
static int
|
|
p_frames_layer_del(t_anim_info *ainfo_ptr,
|
|
long range_from, long range_to, long position)
|
|
{
|
|
gint32 l_tmp_image_id;
|
|
|
|
long l_cur_frame_nr;
|
|
long l_step, l_begin, l_end;
|
|
gint32 l_tmp_layer_id;
|
|
gint l_nlayers;
|
|
gint32 *l_layers_list;
|
|
gdouble l_percentage, l_percentage_step;
|
|
gchar *l_buff;
|
|
int l_rc;
|
|
|
|
|
|
l_rc = 0;
|
|
l_percentage = 0.0;
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_buff = g_strdup_printf (_("Removing Layer (pos:%ld) from Frames..."), position);
|
|
gimp_progress_init(l_buff);
|
|
g_free (l_buff);
|
|
}
|
|
|
|
|
|
/* get info about the image (size and type is common to all frames) */
|
|
|
|
l_begin = range_from;
|
|
l_end = range_to;
|
|
|
|
if(range_from > range_to)
|
|
{
|
|
l_step = -1; /* operate in descending (reverse) order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_from) - range_to);
|
|
|
|
if(range_to < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_from > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
l_step = 1; /* operate in ascending order */
|
|
l_percentage_step = 1.0 / ((1.0 + range_to) - range_from);
|
|
|
|
if(range_from < ainfo_ptr->first_frame_nr)
|
|
{ l_begin = ainfo_ptr->first_frame_nr;
|
|
}
|
|
if(range_to > ainfo_ptr->last_frame_nr)
|
|
{ l_end = ainfo_ptr->last_frame_nr;
|
|
}
|
|
}
|
|
|
|
|
|
l_cur_frame_nr = l_begin;
|
|
while(1)
|
|
{
|
|
/* build the frame name */
|
|
if(ainfo_ptr->new_filename != NULL) g_free(ainfo_ptr->new_filename);
|
|
ainfo_ptr->new_filename = p_alloc_fname(ainfo_ptr->basename,
|
|
l_cur_frame_nr,
|
|
ainfo_ptr->extension);
|
|
if(ainfo_ptr->new_filename == NULL)
|
|
return -1;
|
|
|
|
/* load current frame */
|
|
l_tmp_image_id = p_load_image(ainfo_ptr->new_filename);
|
|
if(l_tmp_image_id < 0)
|
|
return -1;
|
|
|
|
/* remove layer[position] */
|
|
l_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
|
|
if(l_layers_list != NULL)
|
|
{
|
|
/* findout layer id of the requestetd position within layerstack */
|
|
if(position < l_nlayers) l_tmp_layer_id = l_layers_list[position];
|
|
else l_tmp_layer_id = l_layers_list[l_nlayers -1];
|
|
|
|
g_free (l_layers_list);
|
|
|
|
/* check for last layer (MUST NOT be deleted !) */
|
|
if(l_nlayers > 1)
|
|
{
|
|
/* remove and delete requested layer */
|
|
gimp_image_remove_layer(l_tmp_image_id, l_tmp_layer_id);
|
|
|
|
/* save current frame */
|
|
l_rc = p_save_named_frame(l_tmp_image_id, ainfo_ptr->new_filename);
|
|
}
|
|
}
|
|
|
|
/* destroy the tmp image */
|
|
gimp_image_delete(l_tmp_image_id);
|
|
|
|
if(ainfo_ptr->run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_percentage += l_percentage_step;
|
|
gimp_progress_update (l_percentage);
|
|
}
|
|
|
|
/* advance to next frame */
|
|
if((l_cur_frame_nr == l_end) || (l_rc < 0))
|
|
break;
|
|
l_cur_frame_nr += l_step;
|
|
|
|
}
|
|
|
|
|
|
return l_rc;
|
|
} /* end p_frames_layer_del */
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_range_layer_del
|
|
* ============================================================================
|
|
*/
|
|
int gap_range_layer_del(GimpRunMode run_mode, gint32 image_id,
|
|
long range_from, long range_to, long position)
|
|
{
|
|
int l_rc;
|
|
long l_position;
|
|
long l_from, l_to;
|
|
t_anim_info *ainfo_ptr;
|
|
|
|
l_rc = -1;
|
|
l_position = 0;
|
|
ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
|
|
if(ainfo_ptr != NULL)
|
|
{
|
|
if (0 == p_dir_ainfo(ainfo_ptr))
|
|
{
|
|
if(run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_rc = p_range_dialog (ainfo_ptr, &l_from, &l_to,
|
|
_("Delete Layers in Frames"),
|
|
_("Select Frame Range & Position"), 3);
|
|
l_position = l_rc;
|
|
|
|
}
|
|
else
|
|
{
|
|
l_rc = 0;
|
|
l_from = range_from;
|
|
l_to = range_to;
|
|
l_position = position;
|
|
}
|
|
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_frames_layer_del(ainfo_ptr, l_from, l_to, l_position);
|
|
p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
}
|
|
}
|
|
}
|
|
p_free_ainfo(&ainfo_ptr);
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_range_layer_del */
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_range_conv
|
|
* convert frame range to any gimp supported fileformat
|
|
* ============================================================================
|
|
*/
|
|
gint32 gap_range_conv(GimpRunMode run_mode, gint32 image_id,
|
|
long range_from, long range_to,
|
|
long flatten,
|
|
GimpImageBaseType dest_type,
|
|
gint32 dest_colors,
|
|
gint32 dest_dither,
|
|
char *basename,
|
|
char *extension,
|
|
gint32 palette_type,
|
|
gint32 alpha_dither,
|
|
gint32 remove_unused,
|
|
char *palette)
|
|
{
|
|
gint32 l_rc;
|
|
long l_from, l_to;
|
|
long l_flatten;
|
|
gint32 l_dest_colors;
|
|
gint32 l_dest_dither;
|
|
gint32 l_palette_type;
|
|
gint32 l_alpha_dither;
|
|
gint32 l_remove_unused;
|
|
GimpImageBaseType l_dest_type;
|
|
|
|
t_anim_info *ainfo_ptr;
|
|
char l_save_proc_name[128];
|
|
char l_basename[256];
|
|
char *l_basename_ptr;
|
|
long l_number;
|
|
char l_extension[32];
|
|
char l_palette[256];
|
|
|
|
strcpy(l_save_proc_name, "gimp_file_save");
|
|
strcpy(l_extension, ".tif");
|
|
|
|
l_rc = -1;
|
|
ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
|
|
if(ainfo_ptr != NULL)
|
|
{
|
|
if (0 == p_dir_ainfo(ainfo_ptr))
|
|
{
|
|
strncpy(l_basename, ainfo_ptr->basename, sizeof(l_basename) -1);
|
|
l_basename[sizeof(l_basename) -1] = '\0';
|
|
|
|
if(run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
|
|
l_flatten = 1;
|
|
/* p_convert_dialog : select destination type
|
|
* to find out extension
|
|
*/
|
|
strcpy(l_palette, "Default");
|
|
l_rc = p_convert_dialog (ainfo_ptr, &l_from, &l_to, &l_flatten,
|
|
&l_dest_type, &l_dest_colors, &l_dest_dither,
|
|
&l_basename[0], sizeof(l_basename),
|
|
&l_extension[0], sizeof(l_extension),
|
|
&l_palette_type, &l_alpha_dither, &l_remove_unused,
|
|
&l_palette[0], sizeof(l_palette));
|
|
|
|
}
|
|
else
|
|
{
|
|
l_rc = 0;
|
|
l_from = range_from;
|
|
l_to = range_to;
|
|
l_flatten = flatten;
|
|
l_dest_type = dest_type;
|
|
l_dest_colors = dest_colors;
|
|
l_dest_dither = dest_dither;
|
|
l_palette_type = palette_type;
|
|
l_alpha_dither = alpha_dither;
|
|
l_remove_unused = remove_unused;
|
|
if(basename != NULL)
|
|
{
|
|
strncpy(l_basename, basename, sizeof(l_basename) -1);
|
|
l_basename[sizeof(l_basename) -1] = '\0';
|
|
}
|
|
if(palette != NULL)
|
|
{
|
|
strncpy(l_palette, palette, sizeof(l_palette) -1);
|
|
l_palette[sizeof(l_palette) -1] = '\0';
|
|
}
|
|
strncpy(l_extension, extension, sizeof(l_extension) -1);
|
|
l_extension[sizeof(l_extension) -1] = '\0';
|
|
|
|
}
|
|
|
|
if(l_rc >= 0)
|
|
{
|
|
/* cut off extension and trailing frame number */
|
|
l_basename_ptr = p_alloc_basename(&l_basename[0], &l_number);
|
|
if(l_basename_ptr == NULL) { l_rc = -1; }
|
|
else
|
|
{
|
|
l_rc = p_frames_convert(ainfo_ptr, l_from, l_to,
|
|
l_save_proc_name,
|
|
l_basename_ptr,
|
|
l_extension,
|
|
l_flatten,
|
|
l_dest_type,
|
|
l_dest_colors,
|
|
l_dest_dither,
|
|
l_palette_type,
|
|
l_alpha_dither,
|
|
l_remove_unused,
|
|
l_palette);
|
|
g_free(l_basename_ptr);
|
|
}
|
|
}
|
|
}
|
|
p_free_ainfo(&ainfo_ptr);
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_range_conv */
|
|
|
|
|
|
|
|
/* ============================================================================
|
|
* gap_anim_sizechange
|
|
* scale, resize or crop all anim_frame images of the animation
|
|
* (depending on asiz_mode)
|
|
* ============================================================================
|
|
*/
|
|
int gap_anim_sizechange(GimpRunMode run_mode, t_gap_asiz asiz_mode, gint32 image_id,
|
|
long size_x, long size_y, long offs_x, long offs_y)
|
|
{
|
|
int l_rc;
|
|
long l_size_x, l_size_y;
|
|
long l_offs_x, l_offs_y;
|
|
t_anim_info *ainfo_ptr;
|
|
|
|
l_rc = 0;
|
|
ainfo_ptr = p_alloc_ainfo(image_id, run_mode);
|
|
if(ainfo_ptr != NULL)
|
|
{
|
|
if (0 == p_dir_ainfo(ainfo_ptr))
|
|
{
|
|
if(run_mode == GIMP_RUN_INTERACTIVE)
|
|
{
|
|
l_rc = p_anim_sizechange_dialog (ainfo_ptr, asiz_mode,
|
|
&l_size_x, &l_size_y,
|
|
&l_offs_x, &l_offs_y);
|
|
}
|
|
else
|
|
{
|
|
l_size_x = size_x;
|
|
l_size_y = size_y;
|
|
l_offs_x = offs_x;
|
|
l_offs_y = offs_y;
|
|
}
|
|
|
|
if(l_rc >= 0)
|
|
{
|
|
l_rc = p_save_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename);
|
|
if(l_rc >= 0)
|
|
{
|
|
/* we have to resize the current anim frame image in gimp's ram
|
|
*(from where we were invoked)
|
|
* Note: All anim frames on disc and the current one in ram
|
|
* must fit in size and type, to allow further animation operations.
|
|
* (Restriction of duplicate_into)
|
|
*/
|
|
l_rc = p_image_sizechange(ainfo_ptr->image_id, asiz_mode,
|
|
l_size_x, l_size_y, l_offs_x, l_offs_y);
|
|
|
|
if(l_rc == 0)
|
|
{
|
|
/* sizechange for all anim frames on disk */
|
|
l_rc = p_anim_sizechange(ainfo_ptr, asiz_mode,
|
|
l_size_x, l_size_y,
|
|
l_offs_x, l_offs_y );
|
|
}
|
|
/* p_load_named_frame(ainfo_ptr->image_id, ainfo_ptr->old_filename); */
|
|
/* dont need to reload, because the same sizechange operation was
|
|
* applied both to ram-image and dikfile
|
|
*
|
|
* But we must clear all undo steps.
|
|
* (If the user could undo the sizechange on the current image,
|
|
* it would not fit to the other frames on disk.)
|
|
*/
|
|
gimp_image_undo_enable(ainfo_ptr->image_id); /* clear undo stack */
|
|
}
|
|
}
|
|
}
|
|
p_free_ainfo(&ainfo_ptr);
|
|
}
|
|
|
|
return(l_rc);
|
|
} /* end gap_anim_sizechange */
|