/* 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.8    1999/08/31   hof: frames convert: save subsequent frames
 *                            with rumode 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
 */
 
/* SYTEM (UNIX) includes */ 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>

/* GIMP includes */
#include "gtk/gtk.h"
#include "libgimp/gimp.h"

/* GAP includes */
#include "gap_layer_copy.h"
#include "gap_lib.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;
  char *title;
  char  hline[100];
  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].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].int_ret   = l_height;
  
  p_init_arr_arg(&argv[2], WGT_INT_PAIR);
  argv[2].label_txt = "Offest X :";
  argv[2].constraint = FALSE;
  argv[2].int_min    = 0;
  argv[2].int_max    = l_width;
  argv[2].int_ret   = 0;
  
  p_init_arr_arg(&argv[3], WGT_INT_PAIR);
  argv[3].label_txt = "Offest Y :";
  argv[3].constraint = FALSE;
  argv[3].int_min    = 0;
  argv[3].int_max    = l_height;
  argv[3].int_ret   = 0;

  switch(asiz_mode)
  {
    case ASIZ_CROP:
      title = "Crop AnimFrames (all)";
      sprintf(hline, "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)";
      sprintf(hline, "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)";
      sprintf(hline, "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);

      *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].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_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,
                      GImageType *dest_type, gint32 *dest_colors, gint32 *dest_dither,
                      char *basename, gint len_base,
                      char *extension, gint len_ext)
{
  static t_arr_arg  argv[9];
  static char *radio_args[4]  = {"KEEP_TYPE", "Conv to RGB", "Conv to GRAY", "Conv to INDEXED" };
  
  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 cant 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;

  p_init_arr_arg(&argv[7], WGT_INT_PAIR);
  argv[7].constraint = TRUE;
  argv[7].label_txt = "Colors  :";
  argv[7].help_txt  = "Number of resulting Colors               \n(ignored if not converted to indexed)";
  argv[7].int_min   = 2;
  argv[7].int_max   = 256;
  argv[7].int_ret   = 255;

  p_init_arr_arg(&argv[8], WGT_TOGGLE);
  argv[8].label_txt = "Dither  :";
  argv[8].help_txt  = "Enable Floyd-Steinberg dithering      \n(ignored if not converted to indexed)";
  argv[8].int_ret   = 1;

  if(0 != p_chk_framerange(ainfo_ptr))   return -1;

  if(TRUE == p_array_dialog("Convert Frames to other Formats",
                                 "Convert Settings :", 
                                  9, argv))
  {
      *range_from  = (long)(argv[0].int_ret);
      *range_to    = (long)(argv[1].int_ret);
      switch(argv[5].radio_ret)
      {
        case 1: 
           *dest_type = RGB;
           break;
        case 2: 
           *dest_type = GRAY;
           break;
        case 3: 
           *dest_type = INDEXED;
           break;
        default:
           *dest_type = 9444;
           break;
      }
      *flatten     = (long)(argv[6].int_ret);
      *dest_colors = (gint32)(argv[7].int_ret);
      *dest_dither = (long)(argv[8].int_ret);

       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]  = {"Expand as necessary", 
                                 "Clipped to image",
                                 "Clipped to bottom layer", 
                                 "Flattened image" };
  static char *radio_help[4]  = {"Resulting Layer Size is made of the outline-rectangle \nof all visible layers (may differ from frame to frame)", 
                                 "Resulting Layer Size is the frame size",
                                 "Resulting Layer Size is the size of the bottom layer\n(may differ from frame to frame)", 
                                 "Resulting Layer Size is the frame size     \ntransparent parts are filled with BG color" };
  /* Layer select modes */
  static char *sel_args[7]    = {"Pattern is equal to LayerName",
                                  "Pattern is Start of LayerName",
                                  "Pattern is End of Layername",
                                  "Pattern is a Part of LayerName",
                                  "Pattern is LayerstackNumber List",
                                  "Pattern is REVERSE-stack List",
                                  "All Visible (ignore Pattern)"
                                  };
  static char *sel_help[7]    = {"select all Layers where Layername is equal to Pattern",
                                  "select all Layers where Layername starts with Pattern",
                                  "select all Layers where Layername ends up with Pattern",
                                  "select all Layers where Layername contains Pattern",
                                  "select Layerstack positions.\n0, 4-5, 8\nwhere 0 == Top-layer",
                                  "select Layerstack positions.\n0, 4-5, 8\nwhere 0 == BG-layer",
                                  "select all visible Layers"
                                  };


  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 */
  sprintf (sel_pattern, "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)
{
  GImageType 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 == 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 == 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(GRunModeType 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;
  gint32    l_sel_mode;
  gint32    l_sel_case;
  gint32    l_sel_invert;
  
  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 == RUN_INTERACTIVE)
      {

         strcpy(frame_basename, "frame_[####]");
         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)
      {
         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, GImageType dest_type, gint32 dest_colors, gint32 dest_dither)
{
  GParam     *l_params;
  gint        l_retvals;
  int         l_rc;

  l_rc = 0;
  l_params = NULL;

  switch(dest_type)
  {
    case INDEXED:
      if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to INDEXED ncolors=%d'\n", (int)dest_colors);
      l_params = gimp_run_procedure ("gimp_convert_indexed",
			           &l_retvals,
			           PARAM_IMAGE,    image_id,
			           PARAM_INT32,    dest_dither,      /* value 1== floyd-steinberg */
			           PARAM_INT32,    dest_colors,
			           PARAM_END);
      break;
    case GRAY:
      if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to GRAY'\n");
      l_params = gimp_run_procedure ("gimp_convert_grayscale",
			           &l_retvals,
			           PARAM_IMAGE,    image_id,
			           PARAM_END);
      break;
    case RGB:
      if(gap_debug) fprintf(stderr, "DEBUG: p_type_convert to RGB'\n");
      l_params = gimp_run_procedure ("gimp_convert_rgb",
			           &l_retvals,
			           PARAM_IMAGE,    image_id,
			           PARAM_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 == FALSE) 
  {  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,
                 GImageType dest_type, gint32 dest_colors, gint32 dest_dither)
{
  GRunModeType l_run_mode;
  gint32  l_tmp_image_id;
  long    l_cur_frame_nr;
  long    l_step, l_begin, l_end;
  gint    l_nlayers;
  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 == 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_layers_list = gimp_image_get_layers(l_tmp_image_id, &l_nlayers);
    if(l_layers_list != NULL)
    {
      g_free (l_layers_list);
    }

    if((l_nlayers > 1 ) &&  (flatten != 0))
    {
       if(gap_debug) fprintf(stderr, "DEBUG: p_frames_convert flatten tmp image'\n");
       
       /* 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);
       }
    
    
       /* 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  = "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 cant handle type\n- or desired save plugin not available\n");
             }
          }
          if(l_run_mode == RUN_INTERACTIVE)
          {
            l_run_mode  = 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 == 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
)
{
  GParam     *l_params;
  gint        l_retvals;

  switch(asiz_mode)
  {
    case ASIZ_CROP:
      l_params = gimp_run_procedure ("gimp_crop",
			         &l_retvals,
			         PARAM_IMAGE,    image_id,
			         PARAM_INT32,    size_x,
			         PARAM_INT32,    size_y,
			         PARAM_INT32,    offs_x,
			         PARAM_INT32,    offs_y,
			         PARAM_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,
			         PARAM_IMAGE,    image_id,
			         PARAM_INT32,    size_x,
			         PARAM_INT32,    size_y,
			         PARAM_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;  
  GParam     *l_params;
  int         l_rc;

  l_rc = 0;
  l_params = NULL;


  l_percentage = 0.0;  
  if(ainfo_ptr->run_mode == 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 == 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(GRunModeType 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 == RUN_INTERACTIVE)
      {
         l_rc = p_range_dialog (ainfo_ptr, &l_from, &l_to,
                                "Flatten Frames",
                                "Select Frame Range", 2);

      }
      else
      {
         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); 
           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;  
  static  char l_buff[50];
  int          l_rc;
  

  l_rc = 0;
  l_percentage = 0.0;  
  if(ainfo_ptr->run_mode == RUN_INTERACTIVE)
  {
    sprintf(l_buff, "Removing Layer (pos:%ld) from Frames ..", position); 
    gimp_progress_init(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);

        /*  gimp_layer_delete(l_tmp_layer_id); */ /* gives sigsegv ERROR */
        /* A: gimp_image_remove_layer does automatic delete 
         *    if layer is not attched to any image
         * B: gimp_layer_delete has a BUG
         */


        /* 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 == 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(GRunModeType 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 == 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_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(GRunModeType run_mode, gint32 image_id,
                      long range_from, long range_to,
                      long       flatten,
                      GImageType dest_type,
                      gint32     dest_colors,
                      gint32     dest_dither,
                      char      *basename,
                      char      *extension)
{
  gint32  l_rc;
  long   l_from, l_to;
  long   l_flatten;
  gint32     l_dest_colors;
  gint32     l_dest_dither;
  GImageType 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];

  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 == RUN_INTERACTIVE)
      {
         
         l_flatten = 1;
         /* p_convert_dialog : select destination type
          * to find out extension
          */
         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));

      }
      else
      {
         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;
         if(basename != NULL)
         {
            strncpy(l_basename, basename, sizeof(l_basename) -1);
            l_basename[sizeof(l_basename) -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);
            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(GRunModeType 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 == 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_enable_undo(ainfo_ptr->image_id); /* clear undo stack */
         }
      }
    }
    p_free_ainfo(&ainfo_ptr);
  }
  
  return(l_rc);    
}	/* end gap_anim_sizechange */
